Este tema posee contenido traducido por Rama-PH de:
http://forums.alliedmods.net/showthread.php?t=66076
Autor:
Nomexous
Traductor: Rama-PH
Teoría
¿Qué es "Traceline" realmente? Traceline es una función en el engine de Half-Life que es usada para dibujar líneas de un punto a otro en un espacio 3D. Puede devolver información, como quien o que fue golpeado o atravezado, y muchas otras variantes útiles.
El Traceline se utiliza en todos los modos con el objetivo de determinar donde un jugador está mirando y disparando.
Es por ello que resulta muy útil aprender como manipular el Traceline como uno lo desee.
El modo en el que se programa (en este caso va a ser el CS) se encuentra separado del engine del HL (Half-Life). El Metamod (y por lo tanto su extención, AMX Mod X), se encuentra entre el modo DLL y el engine del HL. Esto nos da la habilidad de interceptar cualquier cosa que el modo DLL le envía al engine y vice versa. Entonces, cuando el CS le pide al engine del HL que calcule un Traceline, podemos cambiar completamente el resultado de este.
El Código
Vamos a usar forwards en Fakemeta para capturar la comunicación entre el CS y el engine del HL. Esto significa que que vamos a ver la información que el CS le iba a pasar al engine para poder calcular el Traceline.
PHP Code:
#include <amxmodx>
#include <fakemeta>
public plugin_init()
{
register_plugin("Tutorial de Traceline", "1.0", "Nomexous (Autor original)")
register_forward(FM_TraceLine, "traceline_forward")
}
public traceline_forward(Float:start[3], Float:end[3], conditions, id, trace)
{
// Nuestra Traceline forward.
}
El significado de los argumentos pasados a nuestra función pueden no estar muy claros, por ello paso a explicarlos:
Float:start[3] El origen del lugar donde la Traceline es creada.
Float:end[3] El origen del lugar donde la Traceline termina.
conditions Especifica si ignorar o no ciertas entidades cuando se dibuja el Traceline. Los valores pueden ser:
DONT_IGNORE_MONSTERS, IGNORE_GLASS, IGNORE_MISSLE, and IGNORE_MONSTERS.
id Esta es la entidad a ignorar cuando se dibuja el Traceline. Se supone que la Traceline se dibuja desde el origen de una entidad (el origen está dentro de la entidad) hasta otro punto. Con Tracelines dibujados desde un jugador, id es el index del jugador que está disparando el Traceline.
trace El resultado del trazo. Esta es la parte más importante. Contiene la información sobre que fue golpeado por el Traceline, entre otras cosas. Hay que usar get_tr2 y set_tr2 con esto.
El Resultado del Trace
Esta es la parte más excitante. La variable trace retiene información detallada, que puede ser recibida así:
PHP Code:
public traceline_forward(Float:start[3], Float:end[3], conditions, id, trace)
{
// Hay más constantes TR_* que las que están acá; pero estas son las que
// más uso.
new hit = get_tr2(trace, TR_pHit)
// Lo que fue golpeado por el Traceline. Puede ser el index de un jugador,
// el index de una entidad, 0 (parte del mapa), o -1 (no golpeó nada;
// no pasa con Tracelines de jugadores).
new hitgroup = get_tr2(trace, TR_iHitgroup)
// Si el Traceline golpea a otro jugador, lo que devuelva va a ser HIT_HEAD,
// HIT_CHEST, HIT_LEFTLEG... etc. Si el Traceline golpea parte del mapa,
// esto devuelve HIT_GENERIC.
new Float:fraction
get_tr2(trace, TR_flFraction, fraction)
// Devuelve un número entre 0.0 y 1.0, indicando cuan lejos el Traceline
// viajo del principio a fin antes de golpear algo. Dependiendo
// de que condiciones fueron pasadas a la función forward del Traceline
// puede ser una pared u otra entidad.
new Float:end_origin[3]
get_tr2(trace, TR_vecEndPos, end_origin)
// El final oficial del Traceline. No necesariamente lo mismo que el
// segundo argumento pasado a la función forward de este Traceline.
new Float:normal[3]
get_tr2(trace, TR_vecPlaneNormal, normal)
// Devuelve un vector de 1 unidad de largo normal al lugar donde golpeo
// el Traceline.
// Noten que "normal" tiene una connotación especial. No significa "regular".
Vean
esto si no saben lo que se refiere con el "normal" en este contexto.
Ahora, cambiar este valor es tan facil como usar la función
set_tr2
Un plugin Potencialmente Malvado
Estoy seguro de que todos jugaron en un servidor que permitía headshots unicamente. Es una buena práctica, los disparos al cuerpo no causan daño, los headshots matan instantaneamente, blah blah. Pero, ¿qué pasaría si pudieramos redireccionar un disparo en cualquier parte del cuerpo a la cabeza? ¿Los disparos a los pies podrían ser headshots? Sería buenisimo!
PHP Code:
public traceline_forward(Float:start[3], Float:end[3], conditions, id, trace)
{
set_tr2(trace, TR_iHitgroup, HIT_HEAD)
return FMRES_IGNORED
}
Es bastante sorprendente, realmente. Pruebenlo. Si están acostumbrados a ver mucha sangre a causa de un headshot, van a estar asombrados al ver esa cantidad de sangre salir de un "footshoot". Las Shoutgund son extremadamente mortales, ya que todos los perdigones (balas) son redireccionados a la cabeza al mismo tiempo. Hacé un
if (is_user_admin(id)) y vas a tener un plugin malvado muajaj.
Pero, resulta medio difícil tratar de esconder el hecho de que la sangre de un headshot salió del pie o del estómago, o cualquier parte del cuerpo donde se dispare. Lo que se quiere es que la sangre también se redireccione a la cabeza. Como esto depende del Traceline, tenemos todo el control del asunto:
PHP Code:
public traceline_forward(Float:start[3], Float:end[3], conditions, id, trace)
{
set_tr2(trace, TR_iHitgroup, HIT_HEAD) // Redirecciona el disparo a la cabeza
// Las variables de angulo no tienen uso aca.
static hit, Float:head_origin[3], Float:angles[3]
hit = get_tr2(trace, TR_pHit) // El que recibe el disparo
engfunc(EngFunc_GetBonePosition, hit, 8, head_origin, angles) // Busca el origen del hueso de la cabeza (8)
set_tr2(trace, TR_vecEndPos, head_origin) // La sangre sale de la cabeza!
return FMRES_IGNORED
}
Esto es aún más malvado. Ahora podemos ir por nuestro servidor, haciendo headshots sin sangre inexplicable. Aunque ahora tendrías que explicar porque no podés hacer nada más que headshots jaja.
Podés claramente cambiar el jugador al cual le disparás. Considerando este anti-team attack plugin para cs:
PHP Code:
#include <amxmodx>
#include <fakemeta>
#include <cstrike>
public plugin_init()
{
register_plugin("No team Attack", "1.0", "Nomexous (Autor del tema original)")
register_forward(FM_TraceLine, "traceline_forward")
}
public traceline_forward(Float:start[3], Float:end[3], conditions, id, trace)
{
static hit
hit = get_tr2(trace, TR_pHit)
// El "id" es el index del que dispara.
if (cs_get_user_team(id) == cs_get_user_team(hit))
{
// Se redirecciona a si mismo!
set_tr2(trace, TR_pHit, id)
}
return FMRES_IGNORED
}
La sangre va a seguir saliendo de la persona que está siendo disparada, pero el daño lo va a recibir el que dispara.
Las personas van a dejar de querer disparar a los de su team!
Subí un plugin (
Shot Administration) que demuestra la malvada capacidad de manipular el Traceline. Miren; tracehull también es hookeado (para el daño del cuchillo)
Otros Usos Menos Malvados Del TraceLine
Ahora vamos a usar Tracelines para hacer cosas fuera del contexto de un jugador apuntando. Usando engfunc() podemos dibujar nuestros propios Tracelines para ver lo que golpeamos. Claramente no estamos disparando y provocando un daño, pero con la información que el resultado del trazo nos da, puede ser igual de útil.
Veamos si dos orígenes (comienzo y fin) están a la vista:
PHP Code:
stock bool:is_in_line_of_sight(Float:origin1[3], Float:origin[2], bool:ignore_players = true)
{
new trace = 0
engfunc(EngFunc_TraceLine, origin1, origin2, (ignore_players ? IGNORE_MONSTERS : DONT_IGNORE_MONSTERS), 0, trace)
new Float:fraction
get_tr2(trace, TR_flFraction, fraction)
return (fraction == 1.0) ? true : false
}
Este stock ve cuan lejos fue el Traceline antes de golpear con algo. Si
TR_flFraction es
1.0 significa que fue 100% del trayecto sin golpear ningún obstáculo. Entonces, los dos orígenes están a la vista.
Otra alternativa es ver lo que
TR_pHit devuelve. Si devuelve
-1 no golpeaste nada (entonces los dos orígenes están a la vista). Si devuelve
0 significa que golpeaste una parte del mapa (se asume que es una pared), y todo lo demás es el index de una entidad que se cruzó.
Tené cuidado; puede que no sea un jugador. Puede ser una puerta o un func_wall.
Ahora, hagamos uso de la parte TR_vecPlaneNormal del resultado del trazo. Hay múltiples cosas para las que puede ser útil esto. Acá mostraré como dibujar un laser normal a una superficie.
Cuando el trazo golpea algo,TR_vecPlaneNormal va a tener un vector de unidad (1 unidad en largo) almacenada en ella, mirando perpendicularmente afuera de la superficie en el lugar donde golpeó. Para obtener el punto final del laser, tenemos que extender el largo del vector normal y agregarlo al origen de donde queremos que el laser empieze. Acá hay un ejemplo:
PHP Code:
#include <amxmodx>
#include <fakemeta>
#define PLUGIN "Dibujar un laser normal"
#define VERSION "1.0"
#define AUTHOR "Nomexous (Autor original del tema)"
new beampoint
public plugin_precache()
{
// Necesario para mostrar el laser.
beampoint = precache_model("sprites/laserbeam.spr")
}
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
// Incluí el plugin entero porque para poder dibujar el laser, necesitas precachear un sprite.
// Incorporá estos elementos en tu propio plugin.
// El shoot_laser() va a (si la entidad está en el piso) disparar un laser desde la entidad, normal a la superficie en la que está .
}
public shoot_laser(ent)
{
// Conseguimos el origen de la entidad
new Float:origin[3]
pev(ent, pev_origin, origin)
// Queremos trazar hasta elk piso, si está allí.
new Float:traceto[3]
traceto[0] = origin[0]
traceto[1] = origin[1]
traceto[2] = origin[2] - 10.0
new trace = 0
// Dibujamos el Traceline. Estamos asumiendoel que el objteto está en el piso.
engfunc(EngFunc_TraceLine, origin, traceto, IGNORE_MONSTERS, ent, trace)
new Float:fraction
get_tr2(trace, TR_flFraction, fraction)
// Si no golpeamos nada, entonces no obtendremos un TR_vecPlaneNormal válido.
if (fraction == 1.0) return
new Float:normal[3]
get_tr2(trace, TR_vecPlaneNormal, normal)
// Vamos a multiplicar el vector normal por un escalar para hacerlo más largo.
normal[0] *= 400.0 // Matematicamente, multiplicamos el largo del vector por 400*(3)^(1/2),
normal[1] *= 400.0 // o, en palabras, cuatrocientas veces por tres four.
// Para conseguir el punto final, agregamos el vector normal y el origen.
new Float:endpoint[3]
endpoint[0] = origin[0] + normal[0]
endpoint[1] = origin[1] + normal[1]
endpoint[2] = origin[2] + normal[2]
// Finalmente, dibujamos desde el laser!
draw_laser(origin, endpoint, 100) // Lo dejamos estar por 10 segundos. El tiempo que se mantiene está en 10ths de un segundo.
}
public draw_laser(Float:start[3], Float:end[3], staytime)
{
message_begin(MSG_ALL, SVC_TEMPENTITY)
write_byte(TE_BEAMPOINTS)
engfunc(EngFunc_WriteCoord, start[0])
engfunc(EngFunc_WriteCoord, start[1])
engfunc(EngFunc_WriteCoord, start[2])
engfunc(EngFunc_WriteCoord, end[0])
engfunc(EngFunc_WriteCoord, end[1])
engfunc(EngFunc_WriteCoord, end[2])
write_short(beampoint)
write_byte(0)
write_byte(0)
write_byte(staytime) // En decimas de segundo.
write_byte(10)
write_byte(1)
write_byte(255) // Rojo
write_byte(0) // Verde
write_byte(0) // Azul
write_byte(127)
write_byte(1)
message_end()
}
Bueno, eso es todo por ahora. Con suerte pudieron aprender más acerca del Traceline y pueden tomar ventaja de ello!
Espero que mi traducción les haya gustado, fue muy placentero traducirlo ya que adquerí el conocimiento mucho más facilmente.