I posted this in another thread, but decided to post it here as a contribution, in case someone is interested.
It's just a custom 'flawless' code for detecting what is the floor surface.
It's a bit long, but that's because it detects the floor "type" of any block, sprite, model, map entity block, or terrain, combining some techniques including the soundmap technique from Lolek.
Features:
- When on blocks, it takes the Floor Type from the flags 1, 2 and 3 of the block surface (combinations of 000, 001, 101..etc - see defines). Furthermore, if all flags are cleared, but the texture starts with a minus sign and a number, it uses this number instead. This way, you can have textures like "-2Wood" or "-4Snow" in your block surfaces and don't have to set any flags. If everything else fails it sets the floor to 0 (FLOOR_DEFAULT)
- When on sprites it's the same, but it uses the entity flags set in WED for the sprite. It also checks for the sprite name ("-4metalgrate.tga") for an alternative.
- When on terrains, it uses the soundmap technique. It uses the last skin of the terrain, so it won't conflict with any shaders like the multimaterial terrain shader. (so you can add "skin9" as a soundmap). When painting the 'soundmap' Use the RGB values as flags 1, 2 and 3 respectively, in that order (RGB). For instance, R=0, G=255, B=255 is the equivalent to flags 0 1 1 which is the value of 3 in a range from 0 to 7 (see table above). Also, you don't have to use exact colors: anything above 128 is a 1, and below 128 is a zero.
- When in models, the code uses also the last skin as a 'soundmap'. But instead of a relative position X,Y over the model, (since the model geometry can be arbitrary), the code takes the vertex next to the c_trace point below the creature, and reads the soundmap from its position. It's not as accurate as the terrain soundmap, but it allows to have different floor types even in a single complex model.
Make sure the last skin of the terrain or model has a bitmap attached! Otherwise the FIRST skin will be used.
If the terrain or model has no more than 1 skin, then the skin will NOT be used as soundmap. Instead of that, the code will check for the entity FLAGS. Else, it will look for the terrain/model NAME (so you can assign a single sound to a whole terrain or model easily)
Don't bother in using soundmaps too big. a 128x128 soundmap will do fine.
HOW TO USE:
This function is a LOOP itself. You should call it in your ACTION, but BEFORE the action while() loop. This way you can attach this script for only some entities, while not for others.
What the function does is to set the FLOOR TYPE (see defines) in a skill: my._mActor_FloorType in a constant loop. You can rename and define the skill.
This code doesn't include sound management. But it's easy: before playing the sound, check for my._mActor_FloorType to see where the creature is walking on.
Code:
//FLOOR TYPE:
//defined thru terrain or model skin2 RGB values, or 3 surface FLAGS, or a number in the texture name
define FLOOR_NONE ,-1; //no floor found (for no footstep sound)
define FLOOR_DEFAULT ,0; //default (generic sound) flags 0 0 0
define FLOOR_HARD ,1; //stone, brick, tile flags 0 0 1
define FLOOR_SOFT ,2; //soil etc flags 0 1 0
define FLOOR_GRASS_SNOW ,3; //also snow, sand flags 0 1 1
define FLOOR_WOOD ,4; //wooden planks / old wood flags 1 0 0
define FLOOR_GRAVEL ,5; //optional for loose pebbles/etc flags 1 0 1
define FLOOR_METAL ,6; //metal, gratings flags 1 1 0
define FLOOR_REVERB ,7; //hard reverb default (may be combined with others?) flags 1 1 1
//Temporary variables for the loop below
var floor_vec[3]; //for several vector operations
var floor_rgb[3]; //for storing the pixel color values
//NOTE: color vectors in 3dgs are BGR, but I'll be reading them in RGB order and converting them to "flags."
//to make it easier to remember when painting the map
string floor_name;
bmap* floor_bmap; //store the bmap for the floor texture
entity* floor_entity; //null if no entity/blocks (maybe not needed?)
var floor_width;
var floor_height; //store bitmap width and height to avoid unnecessary bmap operations
var floor_pixel; // pixel
var floor_format; // bitmap format
function mActor_scanFloor_loop()
{
while (!me)
{
wait(1);
}
while (me)
{
my._mActor_FloorType = FLOOR_DEFAULT; //default?
vec_set(floor_vec.x,my.x);
floor_vec.z -= mActor_SCANDISTANCE;
result=c_trace(my.x,floor_vec.x,ignore_me|ignore_passents|scan_texture|get_hitvertex);
if (result>0 && result <100)
{
//found any kind of floor
if (you)
{
str_for_entfile(floor_name,you); //get entity filename
if (str_stri(floor_name,".mdl"))
{
//MODEL floor
//If the model has more than one skin, take the 'sound map' from the last skin bitmap
if(ent_skins(you)>1) //test (normal is 1)
{
floor_bmap = bmap_for_entity(you,ent_skins(you));
//get hit vertex UV coords (0.0 to 1.0) into floor_vec
vec_for_uv(floor_vec,you,hitvertex);
//convert to bitmap coordinates
floor_vec.x *= bmap_width(floor_bmap);
floor_vec.y *= bmap_height(floor_bmap);
//get pixel
floor_format = bmap_lock(floor_bmap,0);
floor_pixel = pixel_for_bmap(floor_bmap,floor_vec.x,floor_vec.y);
//convert to rgb vector
pixel_to_vec(floor_rgb.blue,0,floor_format,floor_pixel);
bmap_unlock(floor_bmap);
my._mActor_FloorType = (floor_rgb.red >128) * 4 + (floor_rgb.green>128) *2 + (floor_rgb.blue>128);
}
else
{
//no model map. Get the floor from model flags
me._mActor_floorType = you.flag1*4 + you.flag2*2 + you.flag3;
//if no flags are set, try the model name (for easy level building) i.e. a simple bridge, a grate floor model...
if (me._mActor_floorType ==0)
{
if (str_to_num(floor_name) < 0)
{
//if texture name starts with a negative number (i.e. -7metalgrate.mdl)
me._mActor_floorType = abs(str_to_num(floor_name));
}
}
}
}
else
{
if (str_stri(floor_name,".hmp"))
{
//TERRAIN floor
//terrain is similar to model, but instead of hitvertex, coordinates have to be calculated.
//If the terrain has more than one skin, take the 'sound map' from the last skin bitmap
if(ent_skins(you)>1)
{
floor_bmap = bmap_for_entity(you,ent_skins(you));
floor_width = bmap_width(floor_bmap);
floor_height = bmap_height(floor_bmap);
//get relative coordinates to read from terrain bitmap
floor_vec.x =
clamp (int( (my.x - you.x)/(you.max_x - you.min_x)*floor_width + floor_width*0.5 ), 0, floor_width-1);
floor_vec.y =
clamp (int( ( - my.y + you.y)/(you.max_y - you.min_y)*floor_height + floor_height*0.5 ), 0, floor_height-1);
//get pixel
floor_format = bmap_lock(floor_bmap,0);
floor_pixel = pixel_for_bmap(floor_bmap,floor_vec.x,floor_vec.y);
//convert to rgb vector
pixel_to_vec(floor_rgb,0,floor_format,floor_pixel);
bmap_unlock(floor_bmap);
//convert rgb "flags" to a number 0 to 7
my._mActor_FloorType = (floor_rgb.red >128) * 4 + (floor_rgb.green>128) *2 + (floor_rgb.blue>128);
}
else
{
//no terrain skins to get soundmap from. Get the floor from terrain flags
me._mActor_floorType = you.flag1*4 + you.flag2*2 + you.flag3;
//if no flags are set, try the terrain name (for easy level building)
//i.e. a simple "sand" terrain would be "-3terrain.hmp", being that 3 is the floor type FLOOR_SNOW....
if (me._mActor_floorType ==0)
{
if (str_to_num(floor_name) < 0)
{
//if texture name starts with a negative number (i.e. -7metalgrate.mdl)
me._mActor_floorType = abs(str_to_num(floor_name));
}
}
}
}
else
{
if (str_stri(floor_name,".wmb"))
{
//MAP ENTITY (ignore YOU and proceed as blocks below!)
you = NULL;
}
else
{
//SPRITE floor
me._mActor_floorType = you.flag1*4 + you.flag2*2 + you.flag3;
//if no flags are set, try the sprite name (for easy level building)
if (me._mActor_floorType ==0)
{
if (str_to_num(floor_name) < 0)
{
//if texture name starts with a negative number (i.e. -2woodenplank.tga)
me._mActor_floorType = abs(str_to_num(floor_name));
}
}
}
}
}
} //if (you)
if (!you) //No entity, or entity reset by (map entity) above
{
//Either blocks, or map entity. Act just as blocks.
//store floor type from flags
me._mActor_floorType = tex_flag1*4 + tex_flag2*2 + tex_flag3;
//if no flags are set, try the texture name (for easy level building)
if (me._mActor_floorType ==0)
{
if (str_to_num(tex_name) < 0)
{
//if texture name starts with a negative number
me._mActor_floorType = abs(str_to_num(tex_name));
}
}
}
}
else
{
//no result floor
//the player is on the air and will not produce any sound
my._mActor_floorType = FLOOR_NONE;
}
wait(1);
} //while (me)
}