Add support for Source Engine decal groups (scripts/decals.txt).

This commit is contained in:
Marco Cawthorne 2022-10-27 20:03:03 -07:00
parent 36fdd443d2
commit 2945e1f65d
Signed by: eukara
GPG Key ID: CE2032F0A2882A22
13 changed files with 286 additions and 76 deletions

View File

@ -53,7 +53,9 @@ CSQC_Init(float apilevel, string enginename, float engineversion)
/* Sound shaders */
Sound_Init();
SurfData_Init();
PropData_Init();
PropData_Init();
DecalGroups_Init();
precache_sound("common/wpn_hudon.wav");
precache_sound("common/wpn_hudoff.wav");
precache_sound("common/wpn_moveselect.wav");

View File

@ -121,6 +121,9 @@ Event_Parse(float type)
case EV_SURFIMPACT:
SurfData_Impact_Parse();
break;
case EV_DECALGROUP:
DecalGroups_Receive();
break;
case EV_CLEARDECALS:
CMD_Cleardecals();
break;

View File

@ -319,6 +319,7 @@ init(float prevprogs)
Sound_Init();
PropData_Init();
SurfData_Init();
DecalGroups_Init();
}
/** Called inside initents() to make sure the entities have their Respawn()

View File

@ -1,38 +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.
*/
hashtable g_hashMaterials;
hashtable hashMaterials;
void Materials_Init(void);
/* 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;

35
src/shared/decalgroups.h Normal file
View File

@ -0,0 +1,35 @@
/* Copyright (c) 2022, Vera Visions, L.L.C. All rights reserved. */
/*
Decal Property List Specs
A decal group consists of one material per line:
groupname
{
"textures/decals/foo" "1"
"textures/decals/bar" "1"
}
And the weight is used to determine how often the decal gets used.
If you're unsure, put in "1".
Also, any group named 'TranslationData' will be ignored.
That group is used in Source Engine games to map the 'gamematerial'
keys from surfaceproperties.txt over to a decal.
The reason why we can't support it is simple:
In Source, decals are tied to surfaceproperties and in GoldSrc
the weapons are responsible for deciding which decals to use
on a surface. We obviously cannot do both, but the GoldSrc way
is the most appropriate one for our purposes.
*/
/* public API */
void DecalGroups_Init(void);
void DecalGroups_Place(string group, vector org);
#ifdef CLIENT
void DecalGroups_Receive(void);
#endif

196
src/shared/decalgroups.qc Normal file
View File

@ -0,0 +1,196 @@
/* Copyright (c) 2022, Vera Visions, L.L.C. All rights reserved. */
#ifdef CLIENT
typedef struct
{
string materials;
int members;
} decalGroup_t;
decalGroup_t *g_decalgroup;
int g_decalgroup_count;
#endif
var hashtable g_hashdecalgroup;
#ifdef CLIENT
static void
DecalGroups_CountLine(string line)
{
int c;
string key;
static string t_name;
static int braced = 0;
c = tokenize_console(line);
key = argv(0);
switch(key) {
case "{":
braced++;
break;
case "}":
braced--;
t_name = "";
break;
default:
/* new definition starts */
if (c == 1 && braced == 0) {
t_name = strtolower(line);
if (t_name)
g_decalgroup_count++;
}
}
return;
}
#endif
static void
DecalGroups_Parse(string line)
{
int c;
string key;
static string t_name;
static int braced = 0;
static int i;
c = tokenize_console(line);
key = argv(0);
switch(key) {
case "{":
braced++;
break;
case "}":
/* increase counter when done */
if (t_name)
i++;
braced--;
t_name = "";
break;
default:
if (braced == 1 && t_name != "") {
/* the server doesn't need to know any of this */
#ifdef CLIENT
/* valid material + weight combo */
if (c == 2) {
if (g_decalgroup[i].members > 0)
g_decalgroup[i].materials = strcat(g_decalgroup[i].materials, ";", argv(0));
else
g_decalgroup[i].materials = argv(0);
g_decalgroup[i].members++;
}
#endif
} else if (braced == 0) {
t_name = strtolower(line);
hash_add(g_hashdecalgroup, t_name, (int)i);
}
}
}
void
DecalGroups_Init(void)
{
filestream fh;
string line;
/* create the hash-table if it doesn't exist */
if (!g_hashdecalgroup) {
g_hashdecalgroup = hash_createtab(2, EV_STRING | HASH_REPLACE);
}
fh = fopen("scripts/decals.txt", FILE_READ);
if (fh < 0) {
print("^1[DECALS] Can't find scripts/decals.txt\n");
return;
}
#ifdef CLIENT
/* count content */
while ((line = fgets(fh))) {
DecalGroups_CountLine(line);
}
/* alocate our stuff */
g_decalgroup = (decalGroup_t *)memalloc(sizeof(decalGroup_t) * g_decalgroup_count);
/* Defaults */
for (int i = 0; i < g_decalgroup_count; i++) {
g_decalgroup[i].materials = "";
g_decalgroup[i].members = 0;
}
#endif
fseek(fh, 0);
while ((line = fgets(fh))) {
/* when we found it, quit */
DecalGroups_Parse(line);
}
fclose(fh);
#ifdef CLIENT
for (int i = 0; i < g_decalgroup_count; i++) {
print(sprintf("%i (members: %i) %s\n", i, g_decalgroup[i].members, g_decalgroup[i].materials));
}
#endif
}
#ifdef CLIENT
void
DecalGroups_PlaceGroupID(int index, vector org)
{
/* on the client we only need to go ahead and place the final decal */
string material;
int r;
/* get all materials of the group */
tokenizebyseparator(g_decalgroup[index].materials, ";");
/* pick a random one. TODO: respects weights */
r = random(0, (float)g_decalgroup[index].members);
/* place a single one. */
Decals_Place(org, argv(r));
}
#endif
void
DecalGroups_Place(string group, vector org)
{
int index;
index = (int)hash_get(g_hashdecalgroup, group, -1);
#ifdef SERVER
/* on the server we only need to tell the clients in the PVS
to go ahead and place a decal of id X at a certain position */
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EV_DECALGROUP);
WriteByte(MSG_MULTICAST, index);
WriteCoord(MSG_MULTICAST, org[0]);
WriteCoord(MSG_MULTICAST, org[1]);
WriteCoord(MSG_MULTICAST, org[2]);
multicast(org, MULTICAST_PVS);
#else
DecalGroups_PlaceGroupID(index, org);
#endif
}
#ifdef CLIENT
void
DecalGroups_Receive(void)
{
int index;
vector org;
index = readbyte();
org[0] = readcoord();
org[1] = readcoord();
org[2] = readcoord();
DecalGroups_PlaceGroupID(index, org);
}
#endif

View File

@ -69,7 +69,7 @@ string __fullspawndata;
#include "NSVehicle.h"
#include "NSMaterial.h"
#include "materials.h"
#include "damage.h"
#include "flags.h"
#include "effects.h"
@ -77,13 +77,13 @@ string __fullspawndata;
#include "events.h"
#include "flags.h"
#include "hitmesh.h"
#include "materials.h"
#include "math.h"
#include "pmove.h"
#include "memory.h"
#include "platform.h"
#include "propdata.h"
#include "surfaceproperties.h"
#include "decalgroups.h"
#include "colors.h"
#include "weapons.h"

View File

@ -50,5 +50,6 @@ enum
EV_VIEWMODEL,
EV_CLEARDECALS,
EV_SURFIMPACT,
EV_DECALGROUP,
EV_SEPARATOR
};

View File

@ -26,7 +26,8 @@ NSClientPlayer.qc
player_pmove.qc
propdata.qc
surfaceproperties.qc
NSMaterial.qc
decalgroups.qc
materials.qc
NSSpraylogo.qc
util.qc
weapons.qc

View File

@ -1,19 +1,42 @@
/*
* 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.
*/
/*
* 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.
*/
hashtable g_hashMaterials;
hashtable hashMaterials;
void Materials_Init(void);
/* 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;
// Impact types
typedef enum
{
@ -189,4 +212,4 @@ Materials_FixName(string old_name)
NSLog("%s > %s", old_name, tex_name);
return tex_name;
}
}

View File

@ -169,7 +169,7 @@ Materials_LoadFromLegacyText(string filename)
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));
///print(sprintf("hlmaterial: %S %S\n", tex_name, mat_type));
g_hlmaterial_entries++;
}
}

View File

@ -87,7 +87,6 @@ typedef struct
string m_fxBulletImpact;
float m_fxBulletImpactID;
string _name;
} surfaceData_t;
/* entity will have to have a .surfdata field pointing to an id */

View File

@ -121,6 +121,7 @@ SurfData_ParseField(int i, int a)
g_surfdata[i].m_sndBreak = 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);
@ -159,7 +160,6 @@ SurfData_Parse(string line)
SurfData_ParseField(i, c);
} else if (braced == 0) {
t_name = strtolower(line);
g_surfdata[i]._name = t_name;
hash_add(g_hashsurfdata, t_name, (int)i);
}
}
@ -208,7 +208,7 @@ SurfData_CountLine(string line)
break;
default:
/* new definition starts */
if (braced == 0) {
if (c == 1 && braced == 0) {
t_name = strtolower(line);
if (t_name)
@ -236,19 +236,6 @@ SurfData_TexToSurfData(string tex_name)
return SurfData_Load(mat);
}
string
SurfData_DataForMatID(float matid)
{
int id = -1;
for (int i = 0; i < g_surfdata_count; i++) {
if (matid == g_surfdata[i].m_flMaterial) {
return g_surfdata[i]._name;
}
}
return 0;
}
/* Public API functions */
__variant
SurfData_GetInfo(int i, int type)