/* AMXX Mod script.
This plugin can change map environment to day/night/rain/snow/clean.
Thanks to: KORD_12.7 and GordonFreeman for code, testing and suggestions.
(c) Copyright 2013, AGHL.RU DevTeam
This file is provided as is (no warranties).
*/
#pragma semicolon 1
#pragma ctrlchar '\'
#include <amxmodx>
#include <amxmisc>
#include <engine>
#include <fakemeta>
#include <fakemeta_util>
#include <hamsandwich>
#define AUTHOR "Lev"
#define PLUGIN "Custom Environment"
#define VERSION "1.2"
#define MAX_PLAYERS 32
#define MAX_RAINDRIPS 10
#define MAX_SNOWFLAKS 10
#define SND_SPAWNING (1<<8) // duplicated in protocol.h we're spawing, used in some cases for ambients
#define SND_STOP (1<<5) // duplicated in protocol.h stop sound
#define SND_CHANGE_VOL (1<<6) // duplicated in protocol.h change sound vol
#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch
new const DEFAULT_DAY_SKY_NAME[] = "desert";
new const CLEAN_NIGHT_SKY_NAMES[][] = { "space", "night1" };
new const RAIN_DAY_SKY_NAMES[][] = { "doom1" };
new const RAIN_NIGHT_SKY_NAMES[][] = { "grimmnight" };
new const SHOW_DAY_SKY_NAMES[][] = { "snow", "snow1", "snowlake_", "blizzard256" };
new const SHOW_NIGHT_SKY_NAMES[][] = { "space", "night1" };
new const RAIN_SOUND[] = "ambience/rain2.wav";
new mapSkyName[32];
new pcvar_env_wind, pcvar_env_rain, pcvar_env_snow, pcvar_env_night, pcvar_env_lightning, pcvar_env_lights, pcvar_env_skyname, pcvar_sv_skyname;
new snowSprite, rainSprite, hazeSprite, splashSprite;
new taskEnt;
new bool:activePlayers[MAX_PLAYERS + 1];
public plugin_precache()
{
pcvar_sv_skyname = get_cvar_pointer("sv_skyname");
pcvar_env_skyname = register_cvar("env_skyname", "");
pcvar_env_wind = register_cvar("env_wind", "0");
pcvar_env_rain = register_cvar("env_rain", "0");
pcvar_env_snow = register_cvar("env_snow", "0");
pcvar_env_night = register_cvar("env_night", "0");
pcvar_env_lightning = register_cvar("env_lightning", "0");
pcvar_env_lights = register_cvar("env_lights", "");
ExecuteConfigs();
// Store sky name from the map
get_pcvar_string(pcvar_sv_skyname, mapSkyName, charsmax(mapSkyName));
// Update server skybox
UpdateSky();
// Precache the sky for client download
new skyName[32];
get_pcvar_string(pcvar_sv_skyname, skyName, charsmax(skyName));
if (skyName[0])
PrecacheSky(skyName);
// Precache sprites
snowSprite = precache_model("sprites/effects/snow8.spr");
rainSprite = precache_model("sprites/effects/rain2.spr");
hazeSprite = precache_model("sprites/effects/rainhaze.spr");
splashSprite = precache_model("sprites/effects/wsplashf.spr");
// Precache ambient sound
precache_sound(RAIN_SOUND);
}
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR);
register_concmd("env_set", "SetGamma");
register_concmd("day", "CmdLight", ADMIN_BAN, "- changes light to day");
register_concmd("night", "CmdLight", ADMIN_BAN, "- changes light to night");
register_concmd("clean", "CmdWeather", ADMIN_BAN, "- changes to clear weather");
register_concmd("rain", "CmdWeather", ADMIN_BAN, "- changes to rainy weather");
register_concmd("snow", "CmdWeather", ADMIN_BAN, "- changes to snowy weather");
// Disable sky lighting so it doesn't mess with our custom lighting
set_cvar_num("sv_skycolor_r", 0);
set_cvar_num("sv_skycolor_g", 0);
set_cvar_num("sv_skycolor_b", 0);
// Create our think entity
taskEnt = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"));
set_pev(taskEnt, pev_classname, "weather_think");
set_pev(taskEnt, pev_nextthink, get_gametime() + 0.5); // Delayed start
RegisterHamFromEntity(Ham_Think, taskEnt, "WeatherThink");
}
public plugin_end()
{
set_pcvar_string(pcvar_sv_skyname, "");
}
ExecuteConfigs()
{
new configFile[128], curMap[64], configDir[128];
get_configsdir(configDir, charsmax(configDir));
get_mapname(curMap, charsmax(curMap));
formatex(configFile, charsmax(configFile), "%s/environment.cfg", configDir);
if (file_exists(configFile))
server_cmd("exec %s", configFile);
new i = 0;
while (curMap[i] != '_' && curMap[i++] != '\0') {/*do nothing*/}
if (curMap[i]=='_')
{
// this map has a prefix
curMap[i] = '\0';
formatex(configFile, charsmax(configFile), "%s/maps/prefix_%s.cfg", configDir, curMap);
if (file_exists(configFile))
server_cmd("exec %s", configFile);
}
get_mapname(curMap, charsmax(curMap));
formatex(configFile, charsmax(configFile), "%s/maps/%s.cfg", configDir, curMap);
if (file_exists(configFile))
server_cmd("exec %s", configFile);
server_exec();
}
UpdateSky()
{
new night = get_pcvar_num(pcvar_env_night);
new rain = get_pcvar_num(pcvar_env_rain);
new snow = get_pcvar_num(pcvar_env_snow);
new skyName[32];
get_pcvar_string(pcvar_env_skyname, skyName, charsmax(skyName));
if (skyName[0])
set_pcvar_string(pcvar_sv_skyname, skyName);
else if (night && rain)
set_pcvar_string(pcvar_sv_skyname, RAIN_NIGHT_SKY_NAMES[random_num(0, sizeof(RAIN_NIGHT_SKY_NAMES) - 1)]);
else if (night && snow)
set_pcvar_string(pcvar_sv_skyname, SHOW_NIGHT_SKY_NAMES[random_num(0, sizeof(SHOW_NIGHT_SKY_NAMES) - 1)]);
else if (night)
set_pcvar_string(pcvar_sv_skyname, CLEAN_NIGHT_SKY_NAMES[random_num(0, sizeof(CLEAN_NIGHT_SKY_NAMES) - 1)]);
else if (rain)
set_pcvar_string(pcvar_sv_skyname, RAIN_DAY_SKY_NAMES[random_num(0, sizeof(RAIN_DAY_SKY_NAMES) - 1)]);
else if (snow)
set_pcvar_string(pcvar_sv_skyname, SHOW_DAY_SKY_NAMES[random_num(0, sizeof(SHOW_DAY_SKY_NAMES) - 1)]);
else if (mapSkyName[0])
set_pcvar_string(pcvar_sv_skyname, mapSkyName);
else
set_pcvar_string(pcvar_sv_skyname, DEFAULT_DAY_SKY_NAME);
}
public client_putinserver(id)
{
SetGamma();
if (!is_user_bot(id) && !is_user_hltv(id))
activePlayers[id] = true;
if (get_pcvar_num(pcvar_env_rain))
engfunc(EngFunc_EmitAmbientSound, taskEnt, {0, 0, 0}, RAIN_SOUND, 1.0, ATTN_NONE, SND_CHANGE_VOL, 100);
}
public client_disconnect(id)
{
if (!is_user_bot(id) && !is_user_hltv(id))
activePlayers[id] = false;
}
public SetGamma()
{
new lightPattern[32];
get_pcvar_string(pcvar_env_lights, lightPattern, charsmax(lightPattern));
if (lightPattern[0])
set_lights(lightPattern);
else if (get_pcvar_num(pcvar_env_night))
set_lights("b");
else
set_lights("m");
}
public CmdLight(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED;
new cmd[10];
read_argv(0, cmd, charsmax(cmd));
if (equal(cmd, "day"))
{
set_pcvar_num(pcvar_env_night, 0);
console_print(id, "Light set to day, change the map to change the sky.");
}
else if (equal(cmd, "night"))
{
set_pcvar_num(pcvar_env_night, 1);
console_print(id, "Light set to night, change the map to change the sky.");
}
UpdateSky();
SetGamma();
return PLUGIN_HANDLED;
}
public CmdWeather(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED;
new cmd[10];
read_argv(0, cmd, charsmax(cmd));
if (equal(cmd, "clean"))
{
set_pcvar_num(pcvar_env_rain, 0);
set_pcvar_num(pcvar_env_snow, 0);
console_print(id, "Weather set to clear, change the map to change the sky.");
}
else if (equal(cmd, "rain"))
{
set_pcvar_num(pcvar_env_rain, MAX_RAINDRIPS);
set_pcvar_num(pcvar_env_snow, 0);
console_print(id, "Weather set to rainy, change the map to change the sky.");
}
else if (equal(cmd, "snow"))
{
set_pcvar_num(pcvar_env_rain, 0);
set_pcvar_num(pcvar_env_snow, MAX_SNOWFLAKS);
console_print(id, "Weather set to snowy, change the map to change the sky.");
}
UpdateSky();
return PLUGIN_HANDLED;
}
public WeatherThink(ent)
{
static Float:origin[3], Float:offset[3], Float:spawn[3], Float:end[3], Float:vel[3], Float:windVel[3];
static content, i, c, maxi, maxc, id, u, l, sprite, life, bool:far, bool:windy;
static wind, rain, snow, night, lightning, lastRain;
if (ent != taskEnt)
return;
// Fetch cvar values
if (u == 0)
{
wind = get_pcvar_num(pcvar_env_wind);
rain = get_pcvar_num(pcvar_env_rain);
snow = get_pcvar_num(pcvar_env_snow);
night = get_pcvar_num(pcvar_env_night);
lightning = get_pcvar_num(pcvar_env_lightning);
clamp(wind, 0, 10);
clamp(rain, 0, MAX_RAINDRIPS);
clamp(snow, 0, MAX_SNOWFLAKS);
// Rain have precedence like in CS
if (rain && snow)
{
set_pcvar_num(pcvar_env_snow, 0);
snow = 0;
}
// Ambient sounds
if (!lastRain && rain)
{
engfunc(EngFunc_EmitAmbientSound, taskEnt, {0, 0, 0}, RAIN_SOUND, 1.0, ATTN_NONE, SND_CHANGE_VOL, 100);
lastRain = rain;
}
else if (lastRain && !rain)
{
engfunc(EngFunc_EmitAmbientSound, taskEnt, {0, 0, 0}, RAIN_SOUND, 0.0, ATTN_NONE, SND_CHANGE_VOL, 100);
lastRain = rain;
}
}
u++;
if (u > 20) u = 0;
// Skip if weather is clear
if (!rain && !snow)
{
set_pev(ent, pev_nextthink, get_gametime() + 1.0);
return;
}
set_pev(ent, pev_nextthink, get_gametime() + 0.1);
// Night rain lightning
if (night && rain)
{
if (lightning && !l && random_num(0, 1000) <= 5)
{
set_lights(random_num(0, 1) < 1 ? "cbbbbbbbbbbbbbbbbbcccbbbbbbbbb" : "cccbbbbbbbbcbbbbbbccbbbbbbbbbb");
l++;
}
else if (l)
{
l++;
// Delay before setting back to night
if (l == 120)
{
set_lights("b");
}
// Delay before new lightning can be fired
else if (l > 500)
{
l = 0;
}
}
}
// Calculate wind
if (wind)
{
windVel[0] += random_float(-1.0, 1.0) * wind;
windVel[1] += random_float(-1.0, 1.0) * wind;
windVel[0] = floatclamp(windVel[0], -40.0 * wind, 40.0 * wind);
windVel[1] = floatclamp(windVel[1], -40.0 * wind, 40.0 * wind);
windVel[2] = -(floatabs(windVel[0]) + floatabs(windVel[1]));
}
else
{
windVel[0] = 0.0;
windVel[1] = 0.0;
windVel[2] = 0.0;
}
windy = windVel[2] < -250.0;
// Setup effect parameters
if (rain)
{
maxc = rain;
maxi = maxc * 4;
vel[0] = random_float(-5.0, 5.0) + windVel[0] * 2.0;
vel[1] = random_float(-5.0, 5.0) + windVel[1] * 2.0;
vel[2] = random_float(-3500.0, -2500.0) - windVel[2];
sprite = rainSprite;
life = 1;
}
else // snow
{
maxc = snow / 2;
maxi = maxc * 4;
vel[0] = random_float(-20.0, 20.0) + windVel[0];
vel[1] = random_float(-20.0, 20.0) + windVel[1];
vel[2] = random_float(-300.0, -150.0) + windVel[2] / 2.0;
sprite = snowSprite;
life = !windy ? 6 : (windVel[2] > -450.0 ? 5 : 4);
}
for (id = 1; id < MAX_PLAYERS + 1; id++)
{
if (!activePlayers[id])
continue;
pev(id, pev_origin, origin);
// Lets try 'maxi' times to create 'maxc' rain drips or snow flaks
for (i = 0, c = 0; i < maxi && c < maxc; i++)
{
// Random point where rain drip or snow flak will spawn (around a player a bit higher)
offset[0] = random_float(-1000.0, 1000.0);
offset[1] = random_float(-1000.0, 1000.0);
offset[2] = vector_length(offset);
far = offset[2] > 700.0;
offset[2] = rain
? random_float(500.0, 800.0)
: (windy
? far ? -vel[2] : -vel[2] * 1.5 //random_float(400.0, far ? 500.0 : 800.0)
: far ? -vel[2] * 1.5 : -vel[2] * 2.0); //random_float(300.0, far ? 400.0 : 700.0));
spawn[0] = origin[0] + offset[0];
spawn[1] = origin[1] + offset[1];
spawn[2] = origin[2] + offset[2];
// Check where we want to spawn
content = engfunc(EngFunc_PointContents, spawn);
if (content == CONTENTS_EMPTY)
{
// If empty point then search upward for the sky
end[0] = spawn[0];
end[1] = spawn[1];
end[2] = spawn[2] + 99999.0;
fm_trace_line(0, spawn, end, end);
content = engfunc(EngFunc_PointContents, end);
// Skip this point if not under the sky
if (content != CONTENTS_SKY)
continue;
}
else if (content == CONTENTS_SKY || content == CONTENTS_SOLID)
{
// If solid or sky point then search down then upward for the sky
end[0] = spawn[0];
end[1] = spawn[1];
end[2] = spawn[2] - 99999.0;
fm_trace_line(0, spawn, end, end);
fm_trace_line(0, end, spawn, spawn);
content = engfunc(EngFunc_PointContents, spawn);
// Skip this point if not under the sky
if (content != CONTENTS_SKY)
continue;
}
else
continue;
// Lower a bit for the case if we are in the sky
spawn[2] = spawn[2] - 10.0;
// Reduce amount of live tempents at a time for a snow
if (snow && far)
{
life = windy ? 2 : 3;
}
engfunc(EngFunc_MessageBegin, MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, spawn, id);
{
write_byte(TE_PROJECTILE);
engfunc(EngFunc_WriteCoord, spawn[0]); // position
engfunc(EngFunc_WriteCoord, spawn[1]);
engfunc(EngFunc_WriteCoord, spawn[2]);
engfunc(EngFunc_WriteCoord, vel[0]); // velocity
engfunc(EngFunc_WriteCoord, vel[1]);
engfunc(EngFunc_WriteCoord, vel[2]);
write_short(sprite); // sprite or model index
write_byte(life); // life
write_byte(0); // owner
}
message_end();
// Additional effects for a rain
if (rain)
{
// Find where drip will fall
end[0] = spawn[0] + vel[0] * 100.0;
end[1] = spawn[1] + vel[1] * 100.0;
end[2] = spawn[2] + vel[2] * 100.0;
fm_trace_line(0, spawn, end, end);
// Move up a bit
end[2] = end[2] + 1.0;
// Only if near a player
if (get_distance_f(origin, end) < 500.0)
{
// Create a splash
engfunc(EngFunc_MessageBegin, MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, end, id);
{
write_byte(TE_SPRITE);
engfunc(EngFunc_WriteCoord, end[0]); // position
engfunc(EngFunc_WriteCoord, end[1]);
engfunc(EngFunc_WriteCoord, end[2]);
write_short(splashSprite); // sprite index
write_byte(9); // scale in 0.1's
write_byte(100); // brightness
}
message_end();
// Create a haze
engfunc(EngFunc_MessageBegin, MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, end, id);
{
write_byte(TE_SPRITE);
engfunc(EngFunc_WriteCoord, end[0]); // position
engfunc(EngFunc_WriteCoord, end[1]);
engfunc(EngFunc_WriteCoord, end[2] + random_float(10.0, 30.0));
write_short(hazeSprite); // sprite index
write_byte(random_num(5, 8)); // scale in 0.1's
write_byte(random_num(4, 8)); // brightness
}
message_end();
}
}
// Iterate created effects
c++;
}
}
}
/// Precache sky files for given name.
PrecacheSky(const skyName[])
{
new buffer[128];
new const prefixes[][] = { "bk.tga", "dn.tga", "ft.tga", "lf.tga", "rt.tga", "up.tga" };
for (new i = 0; i < sizeof(prefixes); i++)
{
formatex(buffer, charsmax(buffer), "gfx/env/%s%s", skyName, prefixes[i]);
precache_generic(buffer);
}
}