// DEMOLITION MAN! from the movie by the same name. Plant Tripmines.
// Based on "Vexd Entity demo (TripMine)" by Vexd
/* CVARS - copy and paste to shconfig.cfg
//Demolition Man
demoman_level 6
demoman_radius 300 // radius of blast
demoman_maxdamage 125 // max damage a mine can cause
demoman_maxmines 2 // max ammount of mines that can be placed at once
demoman_minehealth 80 // health of mines (determines how many shots blow them up)
*/
#include <superheromod>
//Max number of mines that can be set using the CVAR
#define MAX_MINES 20
// *** Color and Alpha for Tripline ***
//Colors To Pick From
#define CUSTOM 0
#define RED 1
#define GREEN 2
#define BLUE 3
#define LTBLUE 4
#define YELLOW 5
#define PURPLE 6
#define ORANGE 7
//Number of colors above (must be correct)
#define COLORS_NUM 8 //Do not set higher than 100 or you will have problems
//Special color modes
#define RNDLIST 100 //Picks a random color from the list
#define RNDCOLOR 101 //Picks random RBG values (may cause lag)
#define TEAMCOLOR 102 //Colors them for what team the planter is on
//Color Settings
#define MINE_COLOR LTBLUE
#define MINE_ALPHA 60
//Color definitions
new const LineColors[COLORS_NUM][3] = {
{150, 150, 150}, //Custom
{255, 0, 0}, //Red
{0, 255, 0}, //Green
{0, 0, 255}, //Blue
{0, 255, 255}, //Light Blue
{255, 255, 0}, //Yellow
{255, 0, 255}, //Purple
{255, 128, 0} //Orange
}
//How often does a mine think
#define MINE_THINK 0.05
// GLOBAL VARIABLES
new gHeroID
new const gHeroName[] = "Demolition Man"
new bool:gHasDemolitionMan[SH_MAXSLOTS+1] //shouldn't really need this
new bool:gBombPlanted
new gPlayerMines[SH_MAXSLOTS+1][MAX_MINES]
new gPlayerMinesCount[SH_MAXSLOTS+1]
new const gSoundActivate[] = "weapons/mine_activate.wav"
new const gSoundCharge[] = "weapons/mine_charge.wav"
new const gSoundDeploy[] = "weapons/mine_deploy.wav"
new const gModelMine[] = "models/v_tripmine.mdl"
new gSpriteBeam, gSpriteFire, gSpriteSmoke
new gPcvarRadius, gPcvarMaxDamage, gPcvarMaxMines, gPcvarMineHealth
//----------------------------------------------------------------------------------------------
public plugin_init()
{
// Plugin Info
register_plugin("SUPERHERO Demolition Man", SH_VERSION_STR, "AssKicR/jtp10181")
register_dictionary("sh_demoman.txt")
// DO NOT EDIT THIS FILE TO CHANGE CVARS, USE THE SHCONFIG.CFG
new pcvarLevel = register_cvar("demoman_level", "6")
gPcvarRadius = register_cvar("demoman_radius", "300")
gPcvarMaxDamage = register_cvar("demoman_maxdamage", "125")
gPcvarMaxMines = register_cvar("demoman_maxmines", "2")
gPcvarMineHealth = register_cvar("demoman_minehealth", "80")
// FIRE THE EVENT TO CREATE THIS SUPERHERO!
gHeroID = sh_create_hero(gHeroName, pcvarLevel)
sh_set_hero_info(gHeroID, "Tripmines", "Unlimited Tripmines - Limited ammount that can be planted at once")
sh_set_hero_bind(gHeroID)
register_forward(FM_Think, "mine_think")
}
//----------------------------------------------------------------------------------------------
public plugin_precache()
{
precache_sound(gSoundActivate)
precache_sound(gSoundCharge)
precache_sound(gSoundDeploy)
precache_model(gModelMine)
gSpriteBeam = precache_model("sprites/laserbeam.spr")
gSpriteFire = precache_model("sprites/explode1.spr")
gSpriteSmoke = precache_model("sprites/steam1.spr")
}
//----------------------------------------------------------------------------------------------
public sh_hero_init(id, heroID, mode)
{
if ( gHeroID != heroID ) return
switch(mode) {
case SH_HERO_ADD: {
gHasDemolitionMan[id] = true
}
case SH_HERO_DROP: {
remove_mines(id)
gHasDemolitionMan[id] = false
}
}
sh_debug_message(id, 1, "%s %s", gHeroName, mode ? "ADDED" : "DROPPED")
}
//----------------------------------------------------------------------------------------------
public sh_hero_key(id, heroID, key)
{
if ( gHeroID != heroID ) return
if ( key == SH_KEYDOWN ) {
if ( sh_is_freezetime() || gBombPlanted ) return
if ( !is_user_alive(id) || !gHasDemolitionMan[id] ) return
create_mine(id)
}
}
//----------------------------------------------------------------------------------------------
create_mine(id)
{
if ( !is_user_alive(id) ) return
new maxmines = get_pcvar_num(gPcvarMaxMines)
if ( maxmines > MAX_MINES ) {
set_pcvar_num(gPcvarMaxMines, MAX_MINES)
maxmines = MAX_MINES
}
if ( gPlayerMinesCount[id] >= maxmines ) {
sh_sound_deny(id)
sh_chat_message(id, gHeroID, "[SH] %L", LANG_PLAYER, "SH_DEMOMAN_MMLR", maxmines)
return
}
new Float:vOrigin[3]
pev(id, pev_origin, vOrigin)
new Float:vTraceDirection[3], Float:vTraceEnd[3], Float:vTraceResult[3], Float:vNormal[3]
velocity_by_aim(id, 64, vTraceDirection)
vTraceEnd[0] = vTraceDirection[0] + vOrigin[0]
vTraceEnd[1] = vTraceDirection[1] + vOrigin[1]
vTraceEnd[2] = vTraceDirection[2] + vOrigin[2]
new Float:fraction, tr = 0
engfunc(EngFunc_TraceLine, vOrigin, vTraceEnd, 0, id, tr)
get_tr2(tr, TR_vecEndPos, vTraceResult)
get_tr2(tr, TR_vecPlaneNormal, vNormal)
get_tr2(tr, TR_flFraction, fraction)
if ( fraction >= 1.0 ) {
sh_sound_deny(id)
sh_chat_message(id, gHeroID, "[SH] %L", LANG_PLAYER, "SH_DEMOMAN_YMP")
return
}
new slot
for ( slot = 0; slot < MAX_MINES; slot++ ) {
if ( gPlayerMines[id][slot] == -1 ) break
}
if ( slot >= maxmines || slot >= MAX_MINES ) return
new NewEnt = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
if ( !NewEnt ) return
set_pev(NewEnt, pev_classname, "demoman_tripmine")
engfunc(EngFunc_SetModel, NewEnt, gModelMine)
//set_pev(NewEnt, pev_owner, id)
set_pev(NewEnt, pev_iuser2, id)
gPlayerMines[id][slot] = NewEnt
gPlayerMinesCount[id]++
set_pev(NewEnt, pev_movetype, MOVETYPE_FLY) //5 = movetype_fly, No grav, but collides.
set_pev(NewEnt, pev_solid, SOLID_NOT)
set_pev(NewEnt, pev_body, 3)
set_pev(NewEnt, pev_sequence, 7) // 7 = TRIPMINE_WORLD
set_pev(NewEnt, pev_takedamage, DAMAGE_NO)
set_pev(NewEnt, pev_iuser1, 0) //0 Will be for inactive.
new Float:vNewOrigin[3], Float:vEntAngles[3]
vNewOrigin[0] = vTraceResult[0] + (vNormal[0] * 8.0)
vNewOrigin[1] = vTraceResult[1] + (vNormal[1] * 8.0)
vNewOrigin[2] = vTraceResult[2] + (vNormal[2] * 8.0)
engfunc(EngFunc_SetSize, NewEnt, Float:{-8.0, -8.0, -8.0}, Float:{8.0, 8.0, 8.0})
engfunc(EngFunc_SetOrigin, NewEnt, vNewOrigin)
vector_to_angle(vNormal, vEntAngles)
set_pev(NewEnt, pev_angles, vEntAngles)
vEntAngles[0] *= -1.0
vEntAngles[1] *= -1.0
vEntAngles[2] *= -1.0
set_pev(NewEnt, pev_v_angle, vEntAngles)
new Float:vBeamEnd[3], Float:vTracedBeamEnd[3]
vBeamEnd[0] = vNewOrigin[0] + (vNormal[0] * 8192.0)
vBeamEnd[1] = vNewOrigin[1] + (vNormal[1] * 8192.0)
vBeamEnd[2] = vNewOrigin[2] + (vNormal[2] * 8192.0)
tr = 0
engfunc(EngFunc_TraceLine, vNewOrigin, vBeamEnd, 1, -1, tr)
get_tr2(tr, TR_vecEndPos, vTracedBeamEnd)
set_pev(NewEnt, pev_vuser1, vTracedBeamEnd)
emit_sound(NewEnt, CHAN_WEAPON, gSoundDeploy, VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
emit_sound(NewEnt, CHAN_VOICE, gSoundCharge, VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
set_pev(NewEnt, pev_nextthink, get_gametime() + 3.0)
}
//----------------------------------------------------------------------------------------------
public mine_think(ent)
{
if ( !pev_valid(ent) ) return FMRES_IGNORED
static classname[32]
classname[0] = '^0'
pev(ent, pev_classname, classname, charsmax(classname))
if ( !equal(classname, "demoman_tripmine") ) return FMRES_IGNORED
static Float:vEnd[3], Float:gametime
pev(ent, pev_vuser1, vEnd)
gametime = get_gametime()
switch( pev(ent, pev_iuser1) )
{
case 0: {
//Activate the mine
set_pev(ent, pev_iuser1, 1) //1 Will be for active.
set_pev(ent, pev_solid, SOLID_BBOX)
set_pev(ent, pev_takedamage, DAMAGE_YES)
set_pev(ent, pev_health, get_pcvar_float(gPcvarMineHealth)+1000.0)
//Double message makes it darker at first
laser_line(ent, vEnd, false)
laser_line(ent, vEnd, false)
set_pev(ent, pev_fuser1, gametime + 0.9)
emit_sound(ent, CHAN_VOICE, gSoundActivate, VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
}
case 1: {
static Float:mineHealth
pev(ent, pev_health, mineHealth)
if ( mineHealth <= 1000.0 ) {
detonate_mine(ent, -1)
return FMRES_IGNORED
}
static Float:beamtime
pev(ent, pev_fuser1, beamtime)
if ( beamtime <= gametime ) {
//Should get called every second
laser_line(ent, vEnd, true)
set_pev(ent, pev_fuser1, gametime + 0.9)
}
static Float:vTrace[3], iHit, tr
static Float:vOrigin[3]
pev(ent, pev_origin, vOrigin)
tr = 0
engfunc(EngFunc_TraceLine, vOrigin, vEnd, 0, ent, tr)
get_tr2(tr, TR_vecEndPos, vTrace)
iHit = get_tr2(tr, TR_pHit)
if ( is_user_alive(iHit) ) detonate_mine(ent, iHit)
}
}
if ( pev_valid(ent) ) set_pev(ent, pev_nextthink, gametime + MINE_THINK)
return FMRES_IGNORED
}
//----------------------------------------------------------------------------------------------
detonate_mine(MineID, iHit)
{
if ( !pev_valid(MineID) ) return
new Float:vOrigin[3], slot
pev(MineID, pev_origin, vOrigin)
new id = pev(MineID, pev_iuser2)
// clear this from the list of live tripmines
for ( slot = 0; slot < MAX_MINES; slot++ ) {
if ( gPlayerMines[id][slot] == MineID ) {
gPlayerMines[id][slot] = -1
gPlayerMinesCount[id]--
break
}
}
if ( iHit == -1 ) {
sh_chat_message(id, gHeroID, "[SH] %L", LANG_PLAYER, "SH_DEMOMAN_YMH")
}
else {
new name[32]
get_user_name(iHit, name, charsmax(name))
sh_chat_message(id, gHeroID, "[SH] %L", LANG_PLAYER, "SH_DEMOMAN_DYM", name)
}
//Kill the Beam
message_begin(MSG_BROADCAST, SVC_TEMPENTITY) //message begin
write_byte(TE_KILLBEAM)
write_short(MineID) // entity
message_end()
engfunc(EngFunc_RemoveEntity, MineID)
blow_up(id, vOrigin)
}
//----------------------------------------------------------------------------------------------
blow_up(id, Float:vExplodeAt[3])
{
new Float:dRatio, Float:distanceBetween, damage
new Float:dmgRadius = get_pcvar_float(gPcvarRadius)
new maxDamage = get_pcvar_num(gPcvarMaxDamage)
new CsTeams:idTeam = cs_get_user_team(id)
new FFOn = sh_friendlyfire_on()
new Float:vOrigin[3]
explode_effect(vExplodeAt)
// Raise the origin a bit
vExplodeAt[2] += 37.0
// Check Players
new players[SH_MAXSLOTS], playerCount, player
get_players(players, playerCount, "ah")
for ( new i = 0; i < playerCount; i++ ) {
player = players[i]
pev(player, pev_origin, vOrigin)
distanceBetween = vector_distance(vExplodeAt, vOrigin)
if ( distanceBetween <= dmgRadius ) {
if ( FFOn || player == id || idTeam != cs_get_user_team(player) ) {
dRatio = distanceBetween / dmgRadius
damage = maxDamage - floatround(maxDamage * dRatio)
if ( !damage ) damage = 1 // Incase damage cvar is really low cause something if within the radius
sh_extra_damage(player, id, damage, "tripmine", _, SH_DMG_NORM, true, _, vExplodeAt)
}
// Add some push and shake effects instead of dmgStun later, and cause it on everyone within radius
}
}
// Check for mines to hurt
new Float:mineHealth, iCurrent = -1
while( (iCurrent = engfunc(EngFunc_FindEntityByString, iCurrent, "classname", "demoman_tripmine")) > 0 ) {
//if ( !pev_valid(iCurrent) ) continue
pev(iCurrent, pev_origin, vOrigin)
distanceBetween = vector_distance(vExplodeAt, vOrigin)
if ( distanceBetween <= dmgRadius ) {
dRatio = distanceBetween / dmgRadius
damage = maxDamage - floatround(maxDamage * dRatio)
// Trim it down a little for the mines
damage = floatround(damage * 0.70)
pev(iCurrent, pev_health, mineHealth)
set_pev(iCurrent, pev_health, (mineHealth - damage))
}
}
}
//----------------------------------------------------------------------------------------------
remove_mines(id)
{
new MineID
gPlayerMinesCount[id] = 0
for ( new x = 0; x < MAX_MINES; x++ ) {
if ( gPlayerMines[id][x] == -1 ) continue
MineID = gPlayerMines[id][x]
gPlayerMines[id][x] = -1
if ( !pev_valid(MineID) ) continue
//Kill the Beam
message_begin(MSG_BROADCAST, SVC_TEMPENTITY) //message begin
write_byte(TE_KILLBEAM)
write_short(MineID) // entity
message_end()
engfunc(EngFunc_RemoveEntity, MineID)
}
}
//----------------------------------------------------------------------------------------------
laser_line(MineID, Float:vEnd[3], bool:killbeam)
{
if ( !pev_valid(MineID) ) return
static colornum, colors[3]
colornum = MINE_COLOR
switch(colornum)
{
case RNDLIST: {
colors = LineColors[random_num(0, COLORS_NUM - 1)]
}
case RNDCOLOR: {
while ( colors[0] + colors[1] + colors[2] < 150 ) {
colors[0] = random_num(30, 255)
colors[1] = random_num(30, 255)
colors[2] = random_num(30, 255)
}
}
case TEAMCOLOR: {
switch ( cs_get_user_team(pev(MineID, pev_iuser2)) )
{
case CS_TEAM_T: colors = LineColors[RED]
case CS_TEAM_CT: colors = LineColors[BLUE]
default: colors = LineColors[CUSTOM]
}
}
default: {
if ( colornum < 0 || colornum >= COLORS_NUM ) {
colornum = 0
}
colors = LineColors[colornum]
}
}
//This is a little cleaner but not much
if ( killbeam ) {
//Kill the Beam
message_begin(MSG_BROADCAST, SVC_TEMPENTITY) //message begin
write_byte(TE_KILLBEAM)
write_short(MineID) // entity
message_end()
}
message_begin(MSG_BROADCAST, SVC_TEMPENTITY) //message begin
write_byte(TE_BEAMENTPOINT)
write_short(MineID) // start entity
engfunc(EngFunc_WriteCoord, vEnd[0]) // end position
engfunc(EngFunc_WriteCoord, vEnd[1])
engfunc(EngFunc_WriteCoord, vEnd[2])
write_short(gSpriteBeam)// sprite index
write_byte(0) // starting frame
write_byte(0) // frame rate in 0.1's
write_byte(10) // life in 0.1's
write_byte(5) // line width in 0.1's
write_byte(0) // noise amplitude in 0.01's
write_byte(colors[0]) // Red
write_byte(colors[1]) // Green
write_byte(colors[2]) // Blue
write_byte(MINE_ALPHA) // brightness
write_byte(0) // scroll speed in 0.1's
message_end()
}
//----------------------------------------------------------------------------------------------
explode_effect(Float:vec[3])
{
//Explosion
message_begin(MSG_BROADCAST, SVC_TEMPENTITY) //message begin
write_byte(TE_EXPLOSION)
engfunc(EngFunc_WriteCoord, vec[0]) // start position
engfunc(EngFunc_WriteCoord, vec[1])
engfunc(EngFunc_WriteCoord, vec[2])
write_short(gSpriteFire) // sprite index
write_byte(60) // scale in 0.1's
write_byte(10) // framerate
write_byte(0) // flags
message_end()
//Explosion2
message_begin(MSG_BROADCAST, SVC_TEMPENTITY) //message begin
write_byte(TE_EXPLOSION2)
engfunc(EngFunc_WriteCoord, vec[0]) // start position
engfunc(EngFunc_WriteCoord, vec[1])
engfunc(EngFunc_WriteCoord, vec[2])
write_byte(188) // starting color
write_byte(10) // num colors
message_end()
//Smoke
message_begin(MSG_BROADCAST, SVC_TEMPENTITY) //message begin
write_byte(TE_SMOKE)
engfunc(EngFunc_WriteCoord, vec[0]) // start position
engfunc(EngFunc_WriteCoord, vec[1])
engfunc(EngFunc_WriteCoord, vec[2])
write_short(gSpriteSmoke) // sprite index
write_byte(20) // scale in 0.1's
write_byte(10) // framerate
message_end()
}
//----------------------------------------------------------------------------------------------
public client_connect(id)
{
remove_mines(id)
}
//----------------------------------------------------------------------------------------------
public client_disconnected(id)
{
remove_mines(id)
}
//----------------------------------------------------------------------------------------------
public sh_client_spawn(id)
{
remove_mines(id)
}
//----------------------------------------------------------------------------------------------
public bomb_explode()
{
gBombPlanted = true
// Remove all the mines when the bomb blows up to prevent crashes
static players[SH_MAXSLOTS], playerCount, i
get_players(players, playerCount, "h")
for ( i = 0; i < playerCount; i++ ) {
remove_mines(players[i])
}
}
//----------------------------------------------------------------------------------------------
public sh_round_start()
{
gBombPlanted = false
}
//----------------------------------------------------------------------------------------------