nuclide/src/shared/surfaceproperties.qc

548 lines
14 KiB
Plaintext

/*
* Copyright (c) 2016-2024 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
static bool g_spDefaultSet;
static void
SurfData_ParseField(int i, int a)
{
switch (argv(0)) {
case "base":
g_surfdata[i].m_strBase = argv(1);
break;
case "gamematerial":
string mat = argv(1);
g_surfdata[i].m_flMaterial = str2chr(mat, 0);
break;
case "thickness":
g_surfdata[i].m_flThickness = stof(argv(1));
break;
case "density":
g_surfdata[i].m_flDensity = stof(argv(1));
break;
case "elasticity":
g_surfdata[i].m_flElasticity = stof(argv(1));
break;
case "friction":
g_surfdata[i].m_flFriction = stof(argv(1));
break;
case "dampening":
g_surfdata[i].m_flDampening = stof(argv(1));
break;
case "jumpfactor":
g_surfdata[i].m_flJumpFactor = stof(argv(1));
break;
case "maxspeedfactor":
g_surfdata[i].m_flMaxSpeedFactor = stof(argv(1));
break;
case "stepleft":
g_surfdata[i].m_sndStepLeft = argv(1);
break;
case "stepright":
g_surfdata[i].m_sndStepRight = argv(1);
break;
case "bulletimpact":
g_surfdata[i].m_sndBulletImpact = argv(1);
break;
case "scraperough":
g_surfdata[i].m_sndScrapeRough = argv(1);
break;
case "scrapesmooth":
g_surfdata[i].m_sndScrapeSoft = argv(1);
break;
case "impacthard":
g_surfdata[i].m_sndImpactHard = argv(1);
break;
case "impactsoft":
g_surfdata[i].m_sndImpactSoft = argv(1);
break;
case "shake":
g_surfdata[i].m_sndShake = argv(1);
break;
case "strain":
g_surfdata[i].m_sndStrain = argv(1);
break;
case "break":
g_surfdata[i].m_sndBreak = argv(1);
break;
case "roll":
case "rolling":
g_surfdata[i].m_sndRoll = argv(1);
break;
case "fx_bulletimpact":
case "part_bulletimpact":
g_surfdata[i].m_fxBulletImpact = argv(1);
#ifdef CLIENT
g_surfdata[i].m_fxBulletImpactID = particleeffectnum(g_surfdata[i].m_fxBulletImpact);
#endif
break;
}
}
/* concerned with dealing with keeping track of braces and parsing lines */
static int
SurfData_Parse(string line)
{
int c;
string key;
static string t_name;
static int braced = 0;
static int i;
static bool isDefault = false;
c = tokenize_console(line);
key = argv(0);
switch(key) {
case "{":
braced++;
break;
case "}":
/* increase counter when done */
if (t_name) {
/* we now apply our values to everything else. */
if (isDefault) {
for (int m = 1i; m < g_surfdata_count; m++) {
g_surfdata[m].m_flMaterial = g_surfdata[0].m_flMaterial;
g_surfdata[m].m_flThickness = g_surfdata[0].m_flThickness;
g_surfdata[m].m_flDensity = g_surfdata[0].m_flDensity;
g_surfdata[m].m_flElasticity = g_surfdata[0].m_flElasticity;
g_surfdata[m].m_flFriction = g_surfdata[0].m_flFriction;
g_surfdata[m].m_flDampening = g_surfdata[0].m_flDampening;
g_surfdata[m].m_flJumpFactor = g_surfdata[0].m_flJumpFactor;
g_surfdata[m].m_flMaxSpeedFactor = g_surfdata[0].m_flMaxSpeedFactor;
g_surfdata[m].m_sndStepLeft = g_surfdata[0].m_sndStepLeft;
g_surfdata[m].m_sndStepRight = g_surfdata[0].m_sndStepRight;
g_surfdata[m].m_sndBulletImpact = g_surfdata[0].m_sndBulletImpact;
g_surfdata[m].m_sndScrapeRough = g_surfdata[0].m_sndScrapeRough;
g_surfdata[m].m_sndScrapeSoft = g_surfdata[0].m_sndScrapeSoft;
g_surfdata[m].m_sndImpactHard = g_surfdata[0].m_sndImpactHard;
g_surfdata[m].m_sndImpactSoft = g_surfdata[0].m_sndImpactSoft;
g_surfdata[m].m_sndShake = g_surfdata[0].m_sndShake;
g_surfdata[m].m_sndStrain = g_surfdata[0].m_sndStrain;
g_surfdata[m].m_sndRoll = g_surfdata[0].m_sndRoll;
g_surfdata[m].m_sndBreak = g_surfdata[0].m_sndBreak;
}
}
i++;
}
braced--;
t_name = "";
break;
default:
if (braced == 1 && t_name != "") {
SurfData_ParseField(i, c);
} else if (braced == 0) {
t_name = strtolower(line);
if (t_name == "default") {
isDefault = true;
} else {
isDefault = false;
}
hash_add(g_hashsurfdata, t_name, (int)i);
}
}
return (0);
}
static int
SurfData_Load(string type)
{
int index;
if (!type)
return -1;
type = strtolower(type);
index = (int)hash_get(g_hashsurfdata, type, -1);
if (index < 0) {
NSError("SurfData %S is not defined.", type);
return -1;
} else {
return index;
}
}
/* stripped down ParseLine that just counts how many slots we have to allocate */
static void
SurfData_CountLine(string line)
{
int c;
string key;
static string t_name;
static int braced = 0i;
static int surfdataCount = 0i;
c = tokenize_console(line);
key = argv(0);
switch(key) {
case "{":
braced++;
break;
case "}":
braced--;
surfdataCount++;
t_name = "";
break;
default:
/* new definition starts */
if (c == 1 && braced == 0) {
t_name = strtolower(argv(0));
if (t_name)
g_surfdata_count++;
/* sanity test */
if (t_name == "default" && surfdataCount == 0i)
g_spDefaultSet = true;
}
}
return;
}
int
SurfData_TexToSurfData(string tex_name)
{
string mat = "";
/* older map formats need their names 'fixed' */
if (g_materialsAreLegacy == true)
tex_name = Materials_FixName(tex_name);
mat = (string)hash_get(g_hashMaterials, tex_name);
if not (mat)
return __NULL__;
/* we got the surfdata string, return that */
return SurfData_Load(mat);
}
/* Public API functions */
__variant
SurfData_GetInfo(int i, int type)
{
if (i < 0)
return __NULL__;
switch (type)
{
case SURFDATA_MATERIAL:
return __NULL__;
case SURFDATA_THICKNESS:
return (__variant)g_surfdata[i].m_flThickness;
case SURFDATA_DENSITY:
return (__variant)g_surfdata[i].m_flDensity;
case SURFDATA_ELASTICITY:
return (__variant)g_surfdata[i].m_flElasticity;
case SURFDATA_FRICTION:
return (__variant)g_surfdata[i].m_flFriction;
case SURFDATA_DAMPENING:
return (__variant)g_surfdata[i].m_flDampening;
case SURFDATA_JUMPFACTOR:
return (__variant)g_surfdata[i].m_flJumpFactor;
case SURFDATA_MAXSPEEDFACTOR:
return (__variant)g_surfdata[i].m_flMaxSpeedFactor;
case SURFDATA_SND_STEPLEFT:
return (__variant)g_surfdata[i].m_sndStepLeft;
case SURFDATA_SND_STEPRIGHT:
return (__variant)g_surfdata[i].m_sndStepRight;
case SURFDATA_SND_BULLETIMPACT:
return (__variant)g_surfdata[i].m_sndBulletImpact;
case SURFDATA_SND_SCRAPEROUGH:
return (__variant)g_surfdata[i].m_sndScrapeRough;
case SURFDATA_SND_SCRAPESOFT:
return (__variant)g_surfdata[i].m_sndScrapeSoft;
case SURFDATA_SND_IMPACTHARD:
return (__variant)g_surfdata[i].m_sndImpactHard;
case SURFDATA_SND_IMPACTSOFT:
return (__variant)g_surfdata[i].m_sndImpactSoft;
case SURFDATA_SND_SHAKE:
return (__variant)g_surfdata[i].m_sndShake;
case SURFDATA_SND_STRAIN:
return (__variant)g_surfdata[i].m_sndStrain;
case SURFDATA_SND_ROLL:
return (__variant)g_surfdata[i].m_sndRoll;
case SURFDATA_SND_BREAK:
return (__variant)g_surfdata[i].m_sndBreak;
case SURFDATA_FX_BULLETIMPACT:
return (__variant)g_surfdata[i].m_fxBulletImpact;
case SURFDATA_FX_BULLETIMPACTID:
return (__variant)g_surfdata[i].m_fxBulletImpactID;
default:
return __NULL__;
}
}
void
SurfData_Shutdown(void)
{
if (g_surfdata) {
memfree(g_surfdata);
}
g_surfdata_count = 0i;
g_hashsurfdata = 0;
g_spDefaultSet = false;
}
void
SurfData_Init(void)
{
filestream fh;
string line;
int index;
InitStart();
/* remove old data */
SurfData_Shutdown();
index = g_surfdata_count;
/* if it already exists, detroy it first */
if (g_hashsurfdata) {
hash_destroytab(g_hashsurfdata);
}
fh = fopen("scripts/surfaceproperties.txt", FILE_READ);
/* it's OK for one to not exist... */
if (fh < 0) {
NSError("missing scripts/surfaceproperties.txt!");
return;
}
/* count surfaceproperty definitions */
while ((line = fgets(fh))) {
SurfData_CountLine(line);
}
/* we did not find 'default' as the first entry. */
if (g_spDefaultSet == false) {
NSError("no 'default' defined at the top of scripts/surfaceproperties.txt!");
return;
}
/* alocate our stuff */
g_surfdata = (surfaceData_t *)memalloc(sizeof(surfaceData_t) * g_surfdata_count);
g_hashsurfdata = hash_createtab(2, HASH_ADD);
/* Internal, defaults. Most of them get overriden by the first entry ('default') */
for (int i = 0i; i < g_surfdata_count; i++) {
g_surfdata[i].m_strBase = "";
g_surfdata[i].m_flMaterial = -1;
g_surfdata[i].m_flThickness = 1.0f;
g_surfdata[i].m_flDensity = 1.0f;
g_surfdata[i].m_flElasticity = 1.0f;
g_surfdata[i].m_flFriction = 1.0f;
g_surfdata[i].m_flDampening = 1.0f;
g_surfdata[i].m_flJumpFactor = 1.0f;
g_surfdata[i].m_flMaxSpeedFactor = 1.0f;
g_surfdata[i].m_sndStepLeft = "";
g_surfdata[i].m_sndStepRight = "";
g_surfdata[i].m_sndBulletImpact = "";
g_surfdata[i].m_sndScrapeRough = "";
g_surfdata[i].m_sndScrapeSoft = "";
g_surfdata[i].m_sndImpactHard = "";
g_surfdata[i].m_sndImpactSoft = "";
g_surfdata[i].m_sndShake = "";
g_surfdata[i].m_sndStrain = "";
g_surfdata[i].m_sndRoll = "";
g_surfdata[i].m_sndBreak = "";
}
fseek(fh, 0);
while ((line = fgets(fh))) {
/* when we found it, quit */
SurfData_Parse(line);
}
fclose(fh);
for (int i = 0i; i < g_surfdata_count; i++) {
Sound_Precache(g_surfdata[i].m_sndStepLeft);
Sound_Precache(g_surfdata[i].m_sndStepRight);
Sound_Precache(g_surfdata[i].m_sndBulletImpact);
Sound_Precache(g_surfdata[i].m_sndScrapeRough);
Sound_Precache(g_surfdata[i].m_sndScrapeSoft);
Sound_Precache(g_surfdata[i].m_sndImpactHard);
Sound_Precache(g_surfdata[i].m_sndImpactSoft);
Sound_Precache(g_surfdata[i].m_sndShake);
Sound_Precache(g_surfdata[i].m_sndStrain);
Sound_Precache(g_surfdata[i].m_sndRoll);
Sound_Precache(g_surfdata[i].m_sndBreak);
}
NSLog("...SurfData initialized with %i entries.", g_surfdata_count);
InitEnd();
}
static string g_curSurfData;
void
SurfData_SetStage(string type)
{
if (!type)
return;
g_curSurfData = type;
}
int
SurfData_Finish(void)
{
string toload = g_curSurfData;
g_curSurfData = __NULL__;
if (toload) {
return SurfData_Load(toload);
} else {
return -1;
}
}
#ifdef SERVER
void
SurfData_Impact_Net(entity e, vector org)
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EV_SURFIMPACT);
WriteCoord(MSG_MULTICAST, org[0]);
WriteCoord(MSG_MULTICAST, org[1]);
WriteCoord(MSG_MULTICAST, org[2]);
WriteEntity(MSG_MULTICAST, e);
multicast(org, MULTICAST_PVS);
}
void
SurfData_ImpactID_Net(int id, vector org, vector ang)
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EV_SURFIMPACTID);
WriteCoord(MSG_MULTICAST, org[0]);
WriteCoord(MSG_MULTICAST, org[1]);
WriteCoord(MSG_MULTICAST, org[2]);
WriteCoord(MSG_MULTICAST, ang[0]);
WriteCoord(MSG_MULTICAST, ang[1]);
WriteCoord(MSG_MULTICAST, ang[2]);
WriteInt(MSG_MULTICAST, id);
multicast(org, MULTICAST_PVS);
}
#endif
#ifdef CLIENT
/** Called by EV_SURFIMPACT */
void
SurfData_Impact_Parse(void)
{
entity surfnum = __NULL__;
vector impactorg = g_vec_null;
vector impactang = g_vec_null;
float impactSurface;
impactorg[0] = readcoord();
impactorg[1] = readcoord();
impactorg[2] = readcoord();
surfnum = findfloat(world, ::entnum, readentitynum()-1);
impactSurface = getsurfacenearpoint(surfnum, impactorg);
impactang = getsurfacenormal(surfnum, impactSurface);
string tex_name = getsurfacetexture(surfnum, impactSurface);
string impactsfx = SurfData_GetInfo(SurfData_TexToSurfData(tex_name), SURFDATA_SND_BULLETIMPACT);
string impactfx = SurfData_GetInfo(SurfData_TexToSurfData(tex_name), SURFDATA_FX_BULLETIMPACT);
float impactid = SurfData_GetInfo(SurfData_TexToSurfData(tex_name), SURFDATA_FX_BULLETIMPACTID);
Sound_PlayAt(impactorg, impactsfx);
pointparticles( impactid, impactorg, impactang, 1 );
}
/** Called by EV_SURFIMPACTID */
void
SurfData_ImpactID_Parse(void)
{
int matid = __NULL__;
vector impactorg = g_vec_null;
vector impactang = g_vec_null;
impactorg[0] = readcoord();
impactorg[1] = readcoord();
impactorg[2] = readcoord();
impactang[0] = readcoord();
impactang[1] = readcoord();
impactang[2] = readcoord();
matid = readint();
string impactsfx = SurfData_GetInfo(matid, SURFDATA_SND_BULLETIMPACT);
float impactid = SurfData_GetInfo(matid, SURFDATA_FX_BULLETIMPACTID);
Sound_PlayAt(impactorg, impactsfx);
pointparticles( impactid, impactorg, normalize(impactang), 1 );
}
#endif
void
SurfData_Impact(entity e, vector org, vector ang)
{
#ifdef SERVER
static void SurfData_Impact_SurfaceParm(entity e, vector org, vector ang) {
SurfData_Impact_Net(e, org);
}
/* the static world */
if (e == world || e.takedamage == DAMAGE_NO) {
if (trace_surfacename != "") {
string n = strcat(dirname(e.model), "/", trace_surfacename);
int texdata = SurfData_TexToSurfData(n);
SurfData_ImpactID_Net(texdata, org, ang);
} else
SurfData_Impact_SurfaceParm(e, org, ang);
} else { /* anything with else is a NSurfacePropEntity. */
NSSurfacePropEntity foo = (NSSurfacePropEntity)e;
if (foo.HasSurfaceData() && foo.GetSurfaceData(SURFDATA_MATERIAL) != -1)
SurfData_ImpactOfType(foo.GetSurfaceData(SURFDATA_MATERIAL), org, ang);
else
SurfData_Impact_SurfaceParm(e, org, ang);
}
#endif
}
void
SurfData_ImpactOfType(int materialID, vector worldPosition, vector impactNormal)
{
#ifdef SERVER
SurfData_ImpactID_Net(materialID, worldPosition, impactNormal);
#endif
/* TODO: client side only version */
}
void
SurfData_ImpactOfNamedType(string materialName, vector worldPosition, vector impactNormal)
{
int index = (int)hash_get(g_hashsurfdata, materialName, -1);
SurfData_ImpactOfType(index, worldPosition, impactNormal);
}