explosiongenerator.cpp

来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 747 行 · 第 1/2 页

CPP
747
字号
#include "StdAfx.h"
#include <fstream>
#include <stdexcept>
#include <SDL_types.h>
#include "creg/VarTypes.h"
#include "ExplosionGenerator.h"
#include "FileSystem/FileHandler.h"
#include "Game/Camera.h"
#include "LogOutput.h"
#include "Map/Ground.h"
#include "Platform/ConfigHandler.h"
#include "Rendering/GL/myGL.h"
#include "Rendering/GroundFlash.h"
#include "Rendering/Textures/ColorMap.h"
#include "Sim/Projectiles/ProjectileHandler.h"
#include "Sim/Projectiles/Unsynced/BubbleProjectile.h"
#include "Sim/Projectiles/Unsynced/DirtProjectile.h"
#include "Sim/Projectiles/Unsynced/ExploSpikeProjectile.h"
#include "Sim/Projectiles/Unsynced/HeatCloudProjectile.h"
#include "Sim/Projectiles/Unsynced/SmokeProjectile2.h"
#include "Sim/Projectiles/Unsynced/SpherePartProjectile.h"
#include "Sim/Projectiles/Unsynced/WakeProjectile.h"
#include "Sim/Projectiles/Unsynced/WreckProjectile.h"
#include "mmgr.h"

using namespace std;

CR_BIND_DERIVED_INTERFACE(CExpGenSpawnable, CWorldObject);

CR_REG_METADATA(CExpGenSpawnable,
);

CExplosionGeneratorHandler* explGenHandler = NULL;


// -------------------------------------------------------------------------------
// ClassAliasList: Finds C++ classes with class aliases
// -------------------------------------------------------------------------------


ClassAliasList::ClassAliasList() {}


void ClassAliasList::Load(const LuaTable& aliasTable)
{
	map<string, string> aliasList;
	aliasTable.GetMap(aliasList);
	aliases.insert(aliasList.begin(), aliasList.end());
}


creg::Class* ClassAliasList::GetClass(const string& name)
{
	string n = name;
	for (;;) {
		map<string,string>::iterator i = aliases.find(n);
		if (i == aliases.end())
			break;

		n = i->second;
	}
	creg::Class *cls = creg::System::GetClass(n);
	if (!cls)
		throw content_error("Unknown class: " + name);
	return cls;
}

string ClassAliasList::FindAlias(const string& className)
{
	for (map<string,string>::iterator i = aliases.begin(); i != aliases.end(); ++i)
		if (i->second == className) return i->first;
	return className;
}


// -------------------------------------------------------------------------------
// Explosion generator handler: loads and stores a list of explosion generators
// -------------------------------------------------------------------------------

CExplosionGeneratorHandler::CExplosionGeneratorHandler()
: luaParser("gamedata/explosions.lua",
	          SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP)

{
	LuaParser aliasParser("gamedata/explosion_alias.lua",
	                      SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
	if (!aliasParser.Execute()) {
		logOutput.Print(aliasParser.GetErrorLog());
	} else {
		const LuaTable root = aliasParser.GetRoot();
		projectileClasses.Load(root.SubTable("projectiles"));
		generatorClasses.Load(root.SubTable("generators"));
	}

	if (!luaParser.Execute()) {
		logOutput.Print(luaParser.GetErrorLog());
	} else {
		luaTable = luaParser.GetRoot();
	}
}


CExplosionGenerator* CExplosionGeneratorHandler::LoadGenerator(const string& tag)
{
	string klass;
	string::size_type seppos = tag.find(':');
	if (seppos == string::npos) {
		klass = tag;
	} else {
		klass = tag.substr(0, seppos);
	}

	creg::Class *cls = generatorClasses.GetClass(klass);
	if (!cls->IsSubclassOf(CExplosionGenerator::StaticClass())) {
		throw content_error(klass + " is not a subclass of CExplosionGenerator");
	}

	CExplosionGenerator* eg = (CExplosionGenerator *)cls->CreateInstance();

	if (seppos != string::npos) {
		eg->Load(this, tag.substr(seppos + 1));
	}

	return eg;
}


// -------------------------------------------------------------------------------
// Base explosion generator class
// -------------------------------------------------------------------------------

CR_BIND_INTERFACE(CExplosionGenerator);


CExplosionGenerator::CExplosionGenerator()
{}


CExplosionGenerator::~CExplosionGenerator()
{}


// -------------------------------------------------------------------------------
// Default explosion generator: everything is calculated from damage and radius
// -------------------------------------------------------------------------------

CR_BIND_DERIVED(CStdExplosionGenerator, CExplosionGenerator, );


CStdExplosionGenerator::CStdExplosionGenerator()
{}


CStdExplosionGenerator::~CStdExplosionGenerator()
{}


void CStdExplosionGenerator::Load(CExplosionGeneratorHandler *h, const string& tag)
{
}


void CStdExplosionGenerator::Explosion(const float3 &pos, float damage, float radius, CUnit *owner,float gfxMod, CUnit *hit, const float3 &dir)
{
	PUSH_CODE_MODE;
	ENTER_MIXED;
	float h2=ground->GetHeight2(pos.x,pos.z);

	float height=pos.y-h2;
	if (height < 0.0f) {
		height = 0.0f;
	}

	bool waterExplosion = h2 < -3;
	bool uwExplosion = pos.y < -15;
	bool airExplosion = pos.y - max((float)0, h2) > 20;

	damage=damage/20;
	if (damage>radius*1.5f) //limit the visual effects based on the radius
		damage=radius*1.5f;
	damage*=gfxMod;
	for (int a=0;a<1;++a) {
//		float3 speed((gs->randFloat()-0.5f)*(radius*0.04f),0.05f+(gs->randFloat())*(radius*0.007f),(gs->randFloat()-0.5f)*(radius*0.04f));
		float3 speed(0,0.3f,0);
		float3 camVect=camera->pos-pos;
		float camLength=camVect.Length();
		camVect/=camLength;
		float moveLength=radius*0.03f;
		if (camLength<moveLength+2)
			moveLength=camLength-2;
		float3 npos=pos+camVect*moveLength;

		SAFE_NEW CHeatCloudProjectile(npos,speed,8+sqrt(damage)*0.5f,7+damage*2.8f,owner);
	}
	if (ph->particleSaturation<1) {		//turn off lots of graphic only particles when we have more particles than we want
		float smokeDamage=damage;
		if (uwExplosion)
			smokeDamage*=0.3f;
		if (airExplosion || waterExplosion)
			smokeDamage*=0.6f;
		float invSqrtsmokeDamage=1/(sqrt(smokeDamage)*0.35f);
		for (int a=0;a<smokeDamage*0.6f;++a) {
			float3 speed(-0.1f+gu->usRandFloat()*0.2f,(0.1f+gu->usRandFloat()*0.3f)*invSqrtsmokeDamage,-0.1f+gu->usRandFloat()*0.2f);
			float3 npos(pos+gu->usRandVector()*(smokeDamage*1.0f));
			float h=ground->GetApproximateHeight(npos.x,npos.z);
			if (npos.y<h)
				npos.y=h;
			float time=(40+sqrt(smokeDamage)*15)*(0.8f+gu->usRandFloat()*0.7f);
			SAFE_NEW CSmokeProjectile2(pos,npos,speed,time,sqrt(smokeDamage)*4,0.4f,owner,0.6f);
		}
		if (!airExplosion && !uwExplosion && !waterExplosion) {
			int numDirt=(int)min(20.f,damage*0.8f);
			float3 color(0.15f,0.1f,0.05f);
			for (int a=0;a<numDirt;++a) {
				float3 speed((0.5f-gu->usRandFloat())*1.5f,1.7f+gu->usRandFloat()*1.6f,(0.5f-gu->usRandFloat())*1.5f);
				speed*=0.7f+min((float)30,damage)/30;
				float3 npos(pos.x-(0.5f-gu->usRandFloat())*(radius*0.6f),pos.y-2.0f-damage*0.2f,pos.z-(0.5f-gu->usRandFloat())*(radius*0.6f));
				SAFE_NEW CDirtProjectile(npos,speed,90+damage*2,2.0f+sqrt(damage)*1.5f,0.4f,0.999f,owner,color);
			}
		}
		if (!airExplosion && !uwExplosion && waterExplosion) {
			int numDirt=(int)min(40.f,damage*0.8f);
			float3 color(1,1,1);
			for (int a=0;a<numDirt;++a) {
				float3 speed((0.5f-gu->usRandFloat())*0.2f,a*0.1f+gu->usRandFloat()*0.8f,(0.5f-gu->usRandFloat())*0.2f);
				speed*=0.7f+min((float)30,damage)/30;
				float3 npos(pos.x-(0.5f-gu->usRandFloat())*(radius*0.2f),pos.y-2.0f-sqrt(damage)*2.0f,pos.z-(0.5f-gu->usRandFloat())*(radius*0.2f));
				SAFE_NEW CDirtProjectile(npos,speed,90+damage*2,2.0f+sqrt(damage)*2.0f,0.3f,0.99f,owner,color);
			}
		}
		if (damage>=20 && !uwExplosion && !airExplosion) {
			int numDebris=gu->usRandInt()%6;
			if (numDebris>0)
				numDebris+=3+(int)(damage*0.04f);
			for (int a=0;a<numDebris;++a) {
				float3 speed;
				if (height<4)
					speed=float3((0.5f-gu->usRandFloat())*2.0f,1.8f+gu->usRandFloat()*1.8f,(0.5f-gu->usRandFloat())*2.0f);
				else
					speed=float3(gu->usRandVector()*2);
				speed*=0.7f+min((float)30,damage)/23;
				float3 npos(pos.x-(0.5f-gu->usRandFloat())*(radius*1),pos.y,pos.z-(0.5f-gu->usRandFloat())*(radius*1));
				SAFE_NEW CWreckProjectile(npos,speed,90+damage*2,owner);
			}
		}
		if (uwExplosion) {
			int numBubbles=(int)(damage*0.7f);
			for (int a=0;a<numBubbles;++a) {
				SAFE_NEW CBubbleProjectile(pos+gu->usRandVector()*radius*0.5f,gu->usRandVector()*0.2f+float3(0,0.2f,0),damage*2+gu->usRandFloat()*damage,1+gu->usRandFloat()*2,0.02f,owner,0.5f+gu->usRandFloat()*0.3f);
			}
		}
		if (waterExplosion && !uwExplosion && !airExplosion) {
			int numWake=(int)(damage*0.5f);
			for (int a=0;a<numWake;++a) {
				SAFE_NEW CWakeProjectile(pos+gu->usRandVector()*radius*0.2f,gu->usRandVector()*radius*0.003f,sqrt(damage)*4,damage*0.03f,owner,0.3f+gu->usRandFloat()*0.2f,0.8f/(sqrt(damage)*3+50+gu->usRandFloat()*90),1);
			}
		}
		if (radius>10 && damage>4) {
			int numSpike=(int)sqrt(damage)+8;
			for (int a=0;a<numSpike;++a) {
				float3 speed=gu->usRandVector();
				speed.Normalize();
				speed*=(8+damage*3.0f)/(9+sqrt(damage)*0.7f)*0.35f;
				if (!airExplosion && !waterExplosion && speed.y<0)
					speed.y=-speed.y;
				SAFE_NEW CExploSpikeProjectile(pos+speed,speed*(0.9f+gu->usRandFloat()*0.4f),radius*0.1f,radius*0.1f,0.6f,0.8f/(8+sqrt(damage)),owner);
			}
		}
	}

	if (radius > 20 && damage > 6 && height < radius * 0.7f) {
		float modSize=max(radius,damage*2);
		float circleAlpha=0;
		float circleGrowth=0;
		float ttl=8+sqrt(damage)*0.8f;
		if (radius>40 && damage>12) {
			circleAlpha=min(0.5f,damage*0.01f);
			circleGrowth=(8+damage*2.5f)/(9+sqrt(damage)*0.7f)*0.55f;
		}
		float flashSize=modSize;
		float flashAlpha=min(0.8f,damage*0.01f);
		SAFE_NEW CStandardGroundFlash(pos,circleAlpha,flashAlpha,flashSize,circleGrowth,ttl);
	}

	if (radius > 40 && damage > 12) {
		CSpherePartProjectile::CreateSphere(pos,min(0.7f,damage*0.02f),5+(int)(sqrt(damage)*0.7f),(8+damage*2.5f)/(9+sqrt(damage)*0.7f)*0.5f,owner);
	}
	POP_CODE_MODE;
}

// -------------------------------------------------------------------------------
// CCustomExplosionGenerator: Uses explosion info from a TDF file
// -------------------------------------------------------------------------------

CR_BIND_DERIVED(CCustomExplosionGenerator, CStdExplosionGenerator, );

#define SPW_WATER 1
#define SPW_GROUND 2
#define SPW_AIR 4
#define SPW_UNDERWATER 8
#define SPW_UNIT 16 // only execute when the explosion hits a unit
#define SPW_NO_UNIT 32 // only execute when the explosion doesn't hit a unit (environment)

CCustomExplosionGenerator::CCustomExplosionGenerator()
{
	groundFlash = 0;
}


CCustomExplosionGenerator::~CCustomExplosionGenerator()
{
	if (groundFlash)
		delete groundFlash;
}


#define OP_END		 0
#define OP_STOREI	 1 // int
#define OP_STOREF	 2 // float
#define OP_STOREC	 3 // char
#define OP_ADD		 4
#define OP_RAND		 5
#define OP_DAMAGE	 6
#define OP_INDEX	 7
#define OP_LOADP	 8 // load a void* into the pointer register
#define OP_STOREP	 9 // store the pointer register into a void*
#define OP_DIR		10 // store the float3 direction
#define OP_SAWTOOTH	11 // Performs a modulo to create a sawtooth wave
#define OP_DISCRETE	12 // Floors the value to a multiple of its parameter
#define OP_SINE		13 // Uses val as the phase of a sine wave
#define OP_YANK     14 // Moves the input value into a buffer, returns zero
#define OP_MULTIPLY 15 // Multiplies with buffer value
#define OP_ADDBUFF  16 // Adds buffer value
#define OP_POW      17 // Power with code as exponent
#define OP_POWBUFF  18 // Power with buffer as exponent

void CCustomExplosionGenerator::ExecuteExplosionCode(const char *code, float damage, char *instance, int spawnIndex, const float3 &dir)
{
	float val = 0.0f;
	void* ptr = NULL;
	float buffer[16];

	for (;;) {
		switch (*(code++)) {
			case OP_END: {
				return;
			}
			case OP_STOREI: {
				Uint16 offset = *(Uint16*) code;
				code += 2;
				*(int*) (instance + offset) = (int) val;
				val = 0.0f;
				break;
			}
			case OP_STOREF: {
				Uint16 offset = *(Uint16*) code;
				code += 2;
				*(float*) (instance + offset) = val;
				val = 0.0f;
				break;
			}
			case OP_STOREC: {
				Uint16 offset = *(Uint16*) code;
				code += 2;
				*(unsigned char*) (instance + offset) = (int) val;
				val = 0.0f;
				break;
			}
			case OP_ADD: {
				val += *(float*) code;
				code += 4;
				break;
			}
			case OP_RAND: {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?