// TMW2 scripts.
// Authors:
// Jesusalva
// Description:
// Controls world seasons. RESPECT MASK_* VARS ON CONSTANTS DB
// is_night(set=False)
function script is_night {
// If we're not configuring, retrieve the variable (cache)
// It is more efficient this way
.@set=getarg(0, false);
if (!.@set)
return $@WEATHER_NIGHT;
/*
// Night time depends on season.
// Summer: Day > Night
// Winter: Night > Day
// Autumn/Spring: Day = Night
//
// But we have TWO SUNS, meaning night is always smaller.
//
// 2 = GETTIME_MINUTE
// 3 = GETTIME_HOUR
// Summer: 5h day. 1h night. (4 cycles, 8 updates)
// Others: 3h day. 1h night. (6 cycles, 12 updates)
// Winter: 2h day. 1h night. (8 cycles, 16 updates)
*/
// NEW Unified Rule: Day last 3 hours. Night lasts 1 hour. Always.
.@t=(season() == SUMMER ? 6 : (season() == WINTER ? 3 : 4));
return (gettime(3) % .@t == 1);
}
000-0,0,0,0 script #WeatherCore NPC_HIDDEN,{
end;
/*
* removemapflag("<map name>", <flag>)
* setmapflag("<map name>", <flag>{, <val>})
* getmapflag("<map name>", <flag>)
mf_snow: 16
mf_jexp: 39
mf_bexp: 40
*/
OnInit:
// This is weather startup
.@init=true;
$@WEATHER_NIGHT=is_night(true);
array_pad($@PLAYER_ACTIVITY, 96, $HIST_ONLINE);
.tpc=0;
.tcl=0;
// Bind commands
bindatcmd "wsnow", "#WeatherCore::OnSnow", 80, 80, 1;
bindatcmd "wrain", "#WeatherCore::OnRain", 80, 80, 1;
bindatcmd "wsand", "#WeatherCore::OnSand", 80, 80, 1;
bindatcmd "wevil", "#WeatherCore::OnEvil", 80, 80, 1;
bindatcmd "wnight", "#WeatherCore::OnNight", 80, 80, 1;
bindatcmd "wclear", "#WeatherCore::OnClear", 80, 80, 1;
bindatcmd "wreset", "#WeatherCore::OnReset", 99, 99, 1;
bindatcmd "wset", "#WeatherCore::OnManual", 99, 99, 1;
// Determine which maps are subject to weather, and how weather works:
// eg. it will never snow on a desert, or a sandstorm on icelands.
.wcore = htnew;
// Deserts
htput(.wcore, "003-1", "desert");
htput(.wcore, "004-1", "desert");
htput(.wcore, "004-2", "desert");
htput(.wcore, "009-1", "desert");
htput(.wcore, "010-1", "desert");
htput(.wcore, "010-2", "desert");
htput(.wcore, "032-1", "desert");
// Woodlands
htput(.wcore, "001-4", "woodland");
htput(.wcore, "001-11", "woodland");
htput(.wcore, "001-12", "woodland");
htput(.wcore, "005-1", "woodland");
htput(.wcore, "012-1", "woodland");
htput(.wcore, "014-1", "woodland");
htput(.wcore, "014-2", "woodland");
htput(.wcore, "014-3", "woodland");
htput(.wcore, "014-4", "woodland");
htput(.wcore, "014-5", "woodland");
htput(.wcore, "017-1", "woodland");
htput(.wcore, "018-1", "woodland");
htput(.wcore, "018-2", "woodland");
htput(.wcore, "018-4", "woodland");
htput(.wcore, "018-5", "woodland");
htput(.wcore, "027-1", "woodland");
// Icelands
htput(.wcore, "001-7", "iceland");
htput(.wcore, "019-1", "iceland");
htput(.wcore, "019-2", "iceland");
htput(.wcore, "019-3", "iceland");
htput(.wcore, "019-4", "iceland");
htput(.wcore, "019-5", "iceland");
htput(.wcore, "019-6", "iceland");
htput(.wcore, "020-1", "iceland");
htput(.wcore, "022-1", "iceland");
htput(.wcore, "024-1", "iceland");
htput(.wcore, "031-1", "iceland");
// Special
htput(.wcore, "011-3", "special");
htput(.wcore, "011-4", "special");
consoleinfo "[Weather.sys] Total Maps = " + htsize(.wcore);
// No "end" here, so server starts with weather
OnMinute00:
OnMinute15:
OnMinute30:
OnMinute45:
// Update the online average activity
.@idx = (gettime(GETTIME_HOUR) * 4) + (gettime(GETTIME_MINUTE) / 15);
$@PLAYER_ACTIVITY[.@idx] = getusers(1);
// There is no weather in test servers
if (debug && !$@GM_OVERRIDE)
end;
//debugmes "[Weather.sys] Starting to regen";
.tpc+=getusers(1);
.tcl+=1;
.@hti = htiterator(.wcore);
for(.@key$ = htinextkey(.@hti); hticheck(.@hti); .@key$ = htinextkey(.@hti)) {
// Local variables: .@key$ .@type .@r
.@type$=htget(.wcore, .@key$);
.@r=rand(0,10000);
// Clear weather map masks
.@mk=getmapmask(.@key$);
if (.@mk & MASK_RAIN)
.@mk=.@mk^MASK_RAIN;
if (.@mk & MASK_SANDSTORM)
.@mk=.@mk^MASK_SANDSTORM;
if (.@mk & MASK_SNOW)
.@mk=.@mk^MASK_SNOW;
if (.@mk & MASK_NIGHT)
.@mk=.@mk^MASK_NIGHT;
if (.@mk & MASK_EVILSANCTUM)
.@mk=.@mk^MASK_EVILSANCTUM;
// Monster King Sieges (can be weird, siege last 10mn and mask last 15)
if ($@MK_SCENE)
.@mk=.@mk|MASK_EVILSANCTUM;
if (.@mk & (MASK_MATTACK|MASK_EVILSANCTUM))
.@mk=.@mk^MASK_EVILSANCTUM;
// Remove all current masks, and add rain/snow/sand
if (.@type$ == "desert") {
if (.@r < 10)
.@mk=.@mk|MASK_RAIN;
else if (.@r < 300)
.@mk=.@mk|MASK_SANDSTORM;
} else if (.@type$ == "woodland") {
if (.@r < 300)
.@mk=.@mk|MASK_RAIN;
if ($EVENT$ == "Christmas")
.@mk=.@mk|MASK_SNOW;
else if (season() == WINTER && .@r >= 9250)
.@mk=.@mk|MASK_SNOW;
} else if (.@type$ == "iceland") {
if (.@r < 30)
.@mk=.@mk|MASK_RAIN;
else if (.@r < 300)
.@mk=.@mk|MASK_SNOW;
} else if (.@type$ == "special") {
// This biom is too hot for snow. But it may rain.
if (.@r < 300)
.@mk=.@mk|MASK_RAIN;
// Fog? (Evil Sanctum and Monster Attack) Or Sandstorm?
if (.@r % 4 == 2)
.@mk=.@mk|MASK_EVILSANCTUM;
else if (.@r % 4 == 1)
.@mk=.@mk|MASK_SANDSTORM;
else if (.@r % 4 == 3)
.@mk=.@mk|MASK_MATTACK;
} else {
consolebug "Warning warning, blame Saulc! Weather system error on map "+.@key$;
announce("ERROR BLAME SAULC! WEATHER SYSTEM CORRUPTED.", bc_all);
}
// Bugfix
if (!(.@mk & MASK_NONE))
.@mk=.@mk|MASK_NONE;
setmapmask .@key$, .@mk;
// Is it night time?
if (is_night()) {
setmapflag(.@key$, mf_nightenabled);
addmapmask .@key$, MASK_NIGHT;
} else if (getmapmask(.@key$) & MASK_NIGHT) {
removemapflag(.@key$, mf_nightenabled);
removemapmask .@key$, MASK_NIGHT;
}
}
htidelete(.@hti);
//debugmes "[Weather.sys] Regenerated";
// Hardcore: Skip day/night cycles
if ($HARDCORE || $@GM_OVERRIDE) {
$@WEATHER_NIGHT=is_night(true);
end;
}
// During night, normal monsters respawn 30% faster. (Bifs and Bosses are immune)
//
// Also, announce to players about day/night cycle changes
// The fastest you'll get is 30 minutes for night cycle.
// It's 2 messages every 3 hours. (r7.5)
// Player might be on cave, and this will help them tracking time.
.@current=is_night(true);
.@update = (.@current != $@WEATHER_NIGHT);
if (.@update) {
$@WEATHER_NIGHT=.@current;
if (.tcl > 0) {
.tpc = .tpc / .tcl;
// Monsters spawn faster based on average player count of previous
// cycle, 2% faster per player, capped at 2/3 of the lowest rate
// Default lowest is 70%, so it can go down up to 45% reduction
// For a total of (70-45 = 25%) or monsters spawning 4× faster
// when server is at "full" load (22 players or so)
.@bon = min(.tpc * 2, min($BCONFN_SPAWN, $BCONFD_SPAWN) * 2 / 3);
}
.tpc=0;
.tcl=0;
}
if (is_night() && .@update) {
.@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
for (.@i = 0; .@i < .@c; .@i++) {
attachrid(.@players[.@i]);
callfunc("SC_Bonus", 180, SC_INCMHPRATE, 10, 10);
percentheal 100, 100;
callfunc("SC_Bonus", 60, SC_INCFLEERATE, 40, 40); // Night Wishes
message(.@players[.@i], "Night wishes: Evasion up!");
message(.@players[.@i], "The night falls.");
detachrid();
}
setbattleflag("mob_spawn_delay", $BCONFN_SPAWN - .@bon);
setbattleflag("monster_hp_rate", $BCONFN_MOBHP);
//charcommand("@reloadbattleconf"); // Careful!
donpcevent("@exprate::OnInheirtedReload");
//donpcevent("@droprate::OnReload");
} else if (!is_night() && .@update) {
.@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
for (.@i = 0; .@i < .@c; .@i++) {
attachrid(.@players[.@i]);
callfunc("SC_Bonus", 180, SC_INCMHPRATE, 10, 10);
percentheal 100, 100;
callfunc("SC_Bonus", 60, SC_ATTHASTE_POTION3, 15, 15); // Morning Rush
message(.@players[.@i], "Morning rush: Attack speed up!");
message(.@players[.@i], "The day rises.");
detachrid();
}
setbattleflag("mob_spawn_delay", $BCONFD_SPAWN - .@bon);
setbattleflag("monster_hp_rate", $BCONFD_MOBHP);
//charcommand("@reloadbattleconf"); // Careful!
donpcevent("@exprate::OnInheirtedReload");
//donpcevent("@droprate::OnReload");
}
callfunc "FYE_Normalize";
if (debug || $@GM_OVERRIDE)
debugmes "[Weather.sys] Weather reloaded";
end;
// Function to check stuff
// WeatherSwitch ( MASK, MAP )
function WeatherSwitch {
// Get map
getmapxy(.@key$,.@a,.@b,0);
// Sanitize
.@m$ = htget(.wcore, .@key$, "Not found");
// Change Weather or abort
if (.@m$ == "Not found")
dispbottom l("Command not permitted on this map! Check npc/functions/weather.conf");
else
addmapmask(.@key$, getarg(0));
return;
}
// Some commands, for GMs manually override weather
OnRain:
WeatherSwitch(MASK_RAIN);
end;
OnSand:
WeatherSwitch(MASK_SANDSTORM);
end;
OnSnow:
WeatherSwitch(MASK_SNOW);
end;
OnNight:
WeatherSwitch(MASK_NIGHT);
end;
OnEvil:
WeatherSwitch(MASK_EVILSANCTUM);
end;
OnManual:
if (!.@atcmd_numparameters) {
dispbottom l("Syntax: @wset <map_mask>");
end;
}
// Never allow negative numbers, or to disable map mask 1 (never, EVER, do such insane thing)
.@rq = atoi(.@atcmd_parameters$[0]);
if (.@rq <= 1 || .@rq % 2 == 1) {
dispbottom l("Invalid map mask");
end;
}
// <Insert a helpful comment here>
getmapxy(.@key$,.@a,.@b,0);
.@mk=getmapmask(.@key$);
.@mk=.@mk^.@rq;
setmapmask(.@key$, .@mk);
end;
// Clear works on any map
OnClear:
getmapxy(.@key$,.@a,.@b,0);
.@mk=getmapmask(.@key$);
if (.@mk & MASK_RAIN)
.@mk=.@mk^MASK_RAIN;
if (.@mk & MASK_SANDSTORM)
.@mk=.@mk^MASK_SANDSTORM;
if (.@mk & MASK_SNOW)
.@mk=.@mk^MASK_SNOW;
if (.@mk & MASK_NIGHT)
.@mk=.@mk^MASK_NIGHT;
setmapmask(.@key$, .@mk);
end;
// Reset the whole map, including season, event and weather masks
OnReset:
getmapxy(.@key$,.@a,.@b,0);
setmapmask(.@key$, MASK_NONE);
end;
}