Get rid of hlmaterials.qc by making the look-up happen fully within NSMaterial.qc as part of the newly unified material routines.

There's also a new file (hlmaterials.txt) that is subject to change which will allow you
to add many custom material definitions without hard-coding a thing. Which fixes conflicts between different GoldSrc games.
This commit is contained in:
Marco Cawthorne 2022-10-15 20:32:40 -07:00
parent 78b81b833c
commit db2d3b1730
Signed by: eukara
GPG Key ID: CE2032F0A2882A22
13 changed files with 306 additions and 184 deletions

View File

@ -0,0 +1,18 @@
// define which material classes (right) are used for
// which material ids (letters on the left side)
B,BloodyFlesh
C,Concrete
D,Dirt
F,Flesh
G,Grate
H,Alien
K,Snow
M,Metal
N,Sand
O,Foliage
P,Computer
S,Slosh
T,Tile
V,Vent
W,Wood
Y,Glass

View File

@ -131,7 +131,6 @@ entity eActivator;
int trace_surfaceflagsi;
string startspot;
string __fullspawndata;
hashtable hashMaterials;
/* damage related tempglobals, like trace_* */
entity g_dmg_eAttacker;

View File

@ -352,14 +352,7 @@ initents(void)
/* sound shader init */
Sound_Init();
/* only bother doing so on Half-Life BSP */
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
HLMaterials_Init();
} else {
Materials_Init();
}
Materials_Init();
PMove_Init();
/* TODO: turn these effects into sound shaders */

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2016-2022 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.
*/
/* FIXME: world.... sigh, we should box this into a worldspawn class */
.string materials_file;
var int g_hlmaterial_entries;
float
HLMaterials_Fetch(string tex_name)
{
return (float)hash_get(hashMaterials, tex_name);
}
void
HLMaterials_Load(string filename)
{
filestream fileMaterial;
string sTemp;
string mat_type;
string tex_name;
fileMaterial = fopen(filename, FILE_READ);
if (fileMaterial >= 0) {
print(strcat("parsing material definitions from ", filename,"\n"));
while ((sTemp = fgets(fileMaterial))) {
/* tokenize and just parse this stuff in */
if (tokenize_console(sTemp) == 2) {
mat_type = strtoupper(argv(0));
tex_name = Materials_FixName(strtolower(argv(1)));
hash_add(hashMaterials, tex_name, str2chr(mat_type, 0));
g_hlmaterial_entries++;
}
}
fclose(fileMaterial);
} else {
dprint(strcat("^1Failed to load ", filename,"!\n"));
}
}
void
HLMaterials_Init(void)
{
print("--------- Initializing HLMaterials ----------\n");
g_hlmaterial_entries = 0;
hashMaterials = __NULL__;
hashMaterials = hash_createtab(2, HASH_ADD);
/* the base definition, every GoldSrc game has this */
HLMaterials_Load("sound/materials.txt");
/* Sven Coop 5.0 does this, fun. */
if (world.materials_file)
HLMaterials_Load(world.materials_file);
/* search through our sound dir for material definitions */
searchhandle pm;
pm = search_begin("sound/materials_*.txt", TRUE, TRUE);
for (int i = 0; i < search_getsize(pm); i++) {
HLMaterials_Load(search_getfilename(pm, i));
}
search_end(pm);
/* the way TW did it back in '03 */
HLMaterials_Load(sprintf("maps/%s.mat", mapname));
/* Trinity-Renderer does it this way */
HLMaterials_Load(sprintf("maps/%s_materials.txt", mapname));
print(sprintf("HLMaterials initiailized with %i entries.\n", g_hlmaterial_entries));
}

View File

@ -15,6 +15,5 @@ vote.qc
weapons.qc
modelevent.qc
mapcycle.qc
hlmaterials.qc
entry.qc
#endlist

View File

@ -14,10 +14,6 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef DOXYGEN
#define enumflags enum
#endif
/**
Bitfield enumeration for NSEntity its SendFlags field.

View File

@ -19,11 +19,15 @@ class
NSMaterial
{
private:
string m_strMaterialName;
bool m_bPenetrable;
bool m_bFootsteps;
float m_flFriction;
string m_strStepSound;
nonvirtual void _PrecacheAndExit(string);
public:
void NSMaterial(void);
@ -38,9 +42,31 @@ public:
/** Called whenever a bullet impact happens onto this material. */
virtual void Impact(vector,vector);
/** Called upon init for each registered game material. */
virtual void Precache(void);
};
hashtable g_hashMaterials;
hashtable hashMaterials;
void Materials_Init(void);
NSMaterial Material_FromTexture(string);
/* legacy material compatibility */
/** hlmaterial to classname mapper table */
typedef struct
{
string id;
string matclass;
} hlmaterials_lut;
hlmaterials_lut *g_hlmlut;
var int g_hlmlut_count;
var int g_hlmaterial_entries;
var bool g_materialsAreLegacy;
/* FIXME: world.... sigh, we should box this into a worldspawn class */
.string materials_file;

View File

@ -21,12 +21,19 @@ NSMaterial::NSMaterial(void)
m_bFootsteps = false;
m_flFriction = 1.0f;
m_strStepSound = "step.default";
/* no need to be around */
think = Util_Destroy;
nextthink = time + 0.25f;
}
void
NSMaterial::_PrecacheAndExit(string cname)
{
/* make it searchable */
classname = "NSMaterial";
m_strMaterialName = cname;
Precache();
}
float
NSMaterial::GetFriction(void)
{
@ -45,6 +52,12 @@ NSMaterial::Penetrable(void)
return (m_bPenetrable);
}
void
NSMaterial::Precache(void)
{
/* foobar */
}
void
NSMaterial::FootstepSound(NSClient client)
{
@ -54,13 +67,36 @@ NSMaterial::FootstepSound(NSClient client)
void
NSMaterial::Impact(vector vecOrigin, vector vecAngle)
{
print("IMPACT, WOOO\n");
print(sprintf("Called impact on material %S at %v, %v\n", m_strMaterialName, vecOrigin, vecAngle));
}
/** Takes a material classname and precaches it. */
static void
Materials_PrecacheClass(string cname)
{
string func = strcat("spawnfunc_", cname);
/* does the material class exist? */
if (isfunction(func)) {
entity oldself = self;
void(void) vFunc;
vFunc = externvalue(-2, func);
NSMaterial new_mat = spawn(NSMaterial);
self = new_mat;
vFunc();
new_mat._PrecacheAndExit(cname);
self = oldself;
print(sprintf("material class %s cached successfully.\n", cname));
} else {
print(sprintf("material class %s failed to load.\n", cname));
}
}
/* general purpose functions to interact with the material system */
void
static void
Materials_LoadFromText(string filename)
{
#if 0
filestream fileMaterial;
string sTemp;
string mat_type;
@ -76,7 +112,7 @@ Materials_LoadFromText(string filename)
if (tokenize_console(sTemp) == 2) {
mat_type = strtoupper(argv(0));
tex_name = Materials_FixName(strtolower(argv(1)));
hash_add(g_hashMaterials, tex_name, str2chr(mat_type, 0));
hash_add(g_hashMaterials, tex_name, str2chr(mat_type, 0), EV_STRING);
}
}
@ -84,9 +120,10 @@ Materials_LoadFromText(string filename)
} else {
dprint(strcat("^1Failed to load ", filename,"!\n"));
}
#endif
}
void
static void
Materials_LoadFromMat(string filename)
{
filestream fileMaterial;
@ -106,7 +143,8 @@ Materials_LoadFromMat(string filename)
parameters = argv(1);
if (command == "material") {
hash_add(g_hashMaterials, materialname, parameters);
hash_add(g_hashMaterials, materialname, parameters, EV_STRING);
Materials_PrecacheClass(parameters);
print(sprintf("added Material %S type %S\n", materialname, parameters));
break;
}
@ -117,43 +155,178 @@ Materials_LoadFromMat(string filename)
}
}
/** loads a temporary mapper so we can map letters to class names. */
static void
Materials_Mapper_Init(void)
{
string sTemp;
int c = 0;
filestream fileLUT;
fileLUT = fopen("scripts/hlmaterials.txt", FILE_READ);
g_hlmlut_count = 0;
/* count valid entries. */
if (fileLUT >= 0) {
while ((sTemp = fgets(fileLUT))) {
if (tokenizebyseparator(sTemp, ",") == 2) {
g_hlmlut_count++;
}
}
}
/* back to the beginning... */
fseek(fileLUT, 0);
g_hlmlut = memalloc(sizeof(hlmaterials_lut) * g_hlmlut_count);
/* read in the elements */
if (fileLUT >= 0) {
while ((sTemp = fgets(fileLUT))) {
/* tokenize and just parse this stuff in */
if (tokenizebyseparator(sTemp, ",") == 2) {
g_hlmlut[c].id = argv(0);
g_hlmlut[c].matclass = argv(1);
Materials_PrecacheClass(argv(1));
c++;
}
}
}
}
/** takes a mat id and returns a classname */
static string
Materials_Mapper_Lookup(string character)
{
int i;
for (i = 0; i < g_hlmlut_count; i++)
if (g_hlmlut[i].id == character)
return g_hlmlut[i].matclass;
return __NULL__;
}
/** unallocates the mapper */
static void
Materials_Mapper_Shutdown(void)
{
g_hlmlut_count = 0;
memfree(g_hlmlut);
}
/** loads a materials.txt type file into our hashtable. */
static void
Materials_LoadFromLegacyText(string filename)
{
filestream fileMaterial;
string sTemp;
string mat_type;
string tex_name;
fileMaterial = fopen(filename, FILE_READ);
if (fileMaterial >= 0) {
print(strcat("parsing material definitions from ", filename,"\n"));
while ((sTemp = fgets(fileMaterial))) {
/* tokenize and just parse this stuff in */
if (tokenize_console(sTemp) == 2) {
mat_type = Materials_Mapper_Lookup(strtoupper(argv(0)));
tex_name = Materials_FixName(strtolower(argv(1)));
hash_add(g_hashMaterials, tex_name, mat_type, EV_STRING);
print(sprintf("hlmaterial: %S %S\n", tex_name, mat_type));
g_hlmaterial_entries++;
}
}
fclose(fileMaterial);
} else {
dprint(strcat("^1Failed to load ", filename,"!\n"));
}
}
/** Initialize our material backbone */
void
Materials_Init(void)
{
g_hashMaterials = __NULL__;
g_hashMaterials = hash_createtab(2, EV_STRING);
g_hashMaterials = hash_createtab(2, EV_STRING | HASH_REPLACE);
/* global table */
Materials_LoadFromText("textures/materials.def");
/* save away the type of material formats we're dealing with */
switch (serverkeyfloat("*bspversion")) {
case BSPVER_Q3: /* Q3 */
case BSPVER_RTCW: /* RtCW */
case BSPVER_RBSP: /* RFVBSP */
g_materialsAreLegacy = false;
break;
case BSPVER_HL:
default:
g_materialsAreLegacy = true;
}
/* iterate through our mat files */
searchhandle searchy = search_begin("textures/*/*.mat", SEARCH_NAMESORT, TRUE);
for (int i = 0; i < search_getsize(searchy); i++) {
Materials_LoadFromMat(search_getfilename(searchy, i));
/* we're dealing with legacy materials */
if (g_materialsAreLegacy) {
/* prepare the mapper */
Materials_Mapper_Init();
/* the base definition, every GoldSrc game has this */
Materials_LoadFromLegacyText("sound/materials.txt");
/* Sven Coop 5.0 loads it from a worldspawn key */
if (world.materials_file)
Materials_LoadFromLegacyText(world.materials_file);
/* search through our sound dir for material definitions */
searchhandle pm;
pm = search_begin("sound/materials_*.txt", TRUE, TRUE);
for (int i = 0; i < search_getsize(pm); i++) {
Materials_LoadFromLegacyText(search_getfilename(pm, i));
}
search_end(pm);
/* the way TW did it back in '03 */
Materials_LoadFromLegacyText(sprintf("maps/%s.mat", mapname));
/* Trinity-Renderer does it this way */
Materials_LoadFromLegacyText(sprintf("maps/%s_materials.txt", mapname));
/* no longer needed! */
Materials_Mapper_Shutdown();
} else {
/* iterate through our mat files */
searchhandle searchy = search_begin("textures/*/*.mat", SEARCH_NAMESORT, TRUE);
for (int i = 0; i < search_getsize(searchy); i++) {
Materials_LoadFromMat(search_getfilename(searchy, i));
}
}
}
/** Returns an NSMaterial for a given material. */
NSMaterial
Material_FromTexture(string tex_name)
{
string mat = (string)hash_get(g_hashMaterials, tex_name);
string func = strcat("spawnfunc_", mat);
NSMaterial ret = __NULL__;
string mat = "";
if (isfunction(func)) {
entity oldself = self;
void(void) vFunc;
vFunc = externvalue(-2, func);
NSMaterial new_mat = spawn(NSMaterial);
self = new_mat;
vFunc();
self = oldself;
return (new_mat);
} else if (mat != "") {
/* let the console know */
print(sprintf("^1material %S does not exist!\n", mat));
/* older map formats need their names 'fixed' */
if (g_materialsAreLegacy == true) {
tex_name = Materials_FixName(tex_name);
}
/* return the builtin generic material */
NSMaterial gen_mat = spawn(NSMaterial);
return (gen_mat);
mat = (string)hash_get(g_hashMaterials, tex_name);
if not (mat)
return __NULL__;
/** look for all existing materials */
if (mat != "")
for (entity f = world; (f = find(f, ::classname, "NSMaterial"));) {
ret = (NSMaterial)f;
/* we found it */
if (ret.m_strMaterialName == mat)
return ret;
}
/* just return a dummy NSMaterial... */
ret = spawn(NSMaterial);
ret._PrecacheAndExit(mat);
return ret;
}

View File

@ -14,34 +14,35 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/** All available damage types. */
typedef enumflags
{
DMG_GENERIC,
DMG_CRUSH,
DMG_BULLET,
DMG_SLASH,
DMG_FREEZE,
DMG_BURN,
DMG_VEHICLE,
DMG_FALL,
DMG_EXPLODE,
DMG_BLUNT,
DMG_ELECTRO,
DMG_SOUND,
DMG_ENERGYBEAM,
DMG_GIB_NEVER,
DMG_GIB_ALWAYS,
DMG_DROWN,
DMG_PARALYZE,
DMG_NERVEGAS,
DMG_POISON,
DMG_RADIATION,
DMG_DROWNRECOVER,
DMG_CHEMICAL,
DMG_SLOWBURN,
DMG_SLOWFREEZE,
DMG_SKIP_ARMOR,
DMG_SKIP_RAGDOLL
DMG_GENERIC, /**< Non specific. */
DMG_CRUSH, /**< Being crushed by something heavy. */
DMG_BULLET, /**< Shot by a gun. */
DMG_SLASH, /**< Cutting, from swords or knives. */
DMG_FREEZE, /**< Ice/freezing temperature damage. */
DMG_BURN, /**< Short flame, or on-fire type damage. */
DMG_VEHICLE, /**< Vehicle ramming into you at speed. */
DMG_FALL, /**< Fall damage */
DMG_EXPLODE, /**< Firery explosion damage. */
DMG_BLUNT, /**< Blunt damage, like from a pipe or a bat. */
DMG_ELECTRO, /**< Electric shock damage. */
DMG_SOUND, /**< Noise so irritating it creates damage. */
DMG_ENERGYBEAM, /**< Energy beam damage. */
DMG_GIB_NEVER, /**< This damage type doesn't cause gibbing. */
DMG_GIB_ALWAYS, /**< This damage type will always gib. */
DMG_DROWN, /**< Drown damage, gets restored over time. */
DMG_PARALYZE, /**< Paralyzation damage. */
DMG_NERVEGAS, /**< Toxins to the nerve, special effect? */
DMG_POISON, /**< Poisonous damage. Similar to nervegas? */
DMG_RADIATION, /**< Radiation damage. Geiger counter go brrr */
DMG_DROWNRECOVER, /**< Health increase from drown recovery. */
DMG_CHEMICAL, /**< Chemical damage. */
DMG_SLOWBURN, /**< Slow burning, just like burning but different rate. */
DMG_SLOWFREEZE, /**< Slow freeze, just freezing but different rate. */
DMG_SKIP_ARMOR, /**< This damage will skip armor checks entirely. */
DMG_SKIP_RAGDOLL /**< This damage will not affect ragdolls. */
} damageType_t;
#define DMG_ACID DMG_CHEMICAL

View File

@ -1,4 +1,3 @@
/*
* Copyright (c) 2022 Vera Visions LLC.
*
@ -16,25 +15,25 @@
*/
/* engine reserved */
#define EF_BRIGHTFIELD (1<<0)
#define EF_MUZZLEFLASH (1<<1)
#define EF_BRIGHTLIGHT (1<<2)
#define EF_DIMLIGHT (1<<3)
#define EF_NODRAW (1<<4) /* also known as EF_FLAG1 in QW */
#define EF_ADDITIVE (1<<5) /* also known as EF_FLAG2 in QW */
#define EF_BLUE (1<<6)
#define EF_RED (1<<7)
#define EF_BRIGHTFIELD (1<<0) /**< Cast a bright, starry field volume. */
#define EF_MUZZLEFLASH (1<<1) /**< Cast a medium sized dynamic light. */
#define EF_BRIGHTLIGHT (1<<2) /**< Cast a large sized dynamic light. */
#define EF_DIMLIGHT (1<<3) /**< Cast a small sized dynamic light. */
#define EF_NODRAW (1<<4) /**< Skip rendering of the entity. Also known as EF_FLAG1 in QW */
#define EF_ADDITIVE (1<<5) /**< Render the entity additively. Also known as EF_FLAG2 in QW */
#define EF_BLUE (1<<6) /**< Cast a blue dynamic light. */
#define EF_RED (1<<7) /**< Cast a red dynamic light. */
#define EF_UNUSED1 (1<<8)
#define EF_FULLBRIGHT (1<<9)
#define EF_FULLBRIGHT (1<<9) /**< Render entity without lighting. */
#define EF_UNUSED2 (1<<10)
#define EF_UNUSED3 (1<<11)
#define EF_NOSHADOW (1<<12)
#define EF_NODEPTHTEST (1<<13)
#define EF_NOSHADOW (1<<12) /**< Entity won't cast a shadow. */
#define EF_NODEPTHTEST (1<<13) /**< Entity renders through walls. */
#define EF_UNUSED4 (1<<14)
#define EF_UNUSED5 (1<<15)
#define EF_UNUSED6 (1<<16)
#define EF_UNUSED7 (1<<17)
#define EF_GREEN (1<<18)
#define EF_GREEN (1<<18) /**< Cast a green dynamic light. */
#define EF_UNUSED8 (1<<19)
#define EF_UNUSED9 (1<<20)
#define EF_UNUSED10 (1<<21)

View File

@ -14,6 +14,10 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef DOXYGEN
#define enumflags enum
#endif
void
_NSLog(string msg)
{
@ -32,5 +36,4 @@ enumflags
SEARCH_NAMESORT
};
const vector g_vec_null = [0.0f, 0.0f, 0.0f];

View File

@ -14,15 +14,17 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* global hitmesh definitions */
/** Hitmesh types that models can support. You can figure those
out on any model as long as you cast a traceline with the MOVE_HITMODEL
flag. The result will be stored inside the global `trace_surface_id`. */
typedef enum
{
BODY_DEFAULT,
BODY_HEAD,
BODY_CHEST,
BODY_STOMACH,
BODY_ARMLEFT,
BODY_ARMRIGHT,
BODY_LEGLEFT,
BODY_LEGRIGHT
BODY_DEFAULT, /**< No specific body part. */
BODY_HEAD, /**< trace hit the head */
BODY_CHEST, /**< trace hit the chest */
BODY_STOMACH, /**< trace hit the stomach */
BODY_ARMLEFT, /**< trace hit the left arm */
BODY_ARMRIGHT, /**< trace hit the right arm */
BODY_LEGLEFT, /**< trace hit the left leg */
BODY_LEGRIGHT /**< trace hit the right leg */
} bodyType_t;

View File

@ -15,8 +15,6 @@
*/
#ifdef SERVER
float HLMaterials_Fetch(string);
/* takes a material id (e.g. 'W' for wood) and returns an breakmodel id */
static materialType_t
SurfData_IDtoMaterial(float mat)
@ -464,7 +462,7 @@ void
SurfData_Impact(entity e, int fl, vector org, vector ang)
{
static void SurfData_Impact_SurfaceParm(entity e, int fl, vector org, vector ang) {
#ifndef NEW_MATERIALS
#ifdef OLD_MATERIALS
switch (serverkeyfloat("*bspversion")) {
case BSPVER_HL:
float surf;