forked from Mirrors/openclonk
Added new ambience object to Experimental.ocd.
Controls the music playlist according to the situation the player's clonk is currently in.issue1247
parent
612a05fe40
commit
bb3203ff63
|
@ -0,0 +1,5 @@
|
|||
[DefCore]
|
||||
id=Ambience
|
||||
Version=5,4,0,0
|
||||
Category=C4D_StaticBack | C4D_Rule
|
||||
Picture=0,0,128,128
|
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,299 @@
|
|||
/**
|
||||
Ambience
|
||||
Controls sound and music depending on the environment the player is in
|
||||
|
||||
@author Sven2
|
||||
*/
|
||||
|
||||
local exec_counter; // counter to distribute execution of players across frames
|
||||
local last_environment; // array indexed by player number: pointer to environment the player was in last
|
||||
local environments; // array of available environments for which it is checked if the player is in. sorted by priority.
|
||||
|
||||
// Initialization
|
||||
protected func Initialize()
|
||||
{
|
||||
// Base environment
|
||||
Environment = {
|
||||
actions = [],
|
||||
min_change_delay = 1,
|
||||
min_initial_change_delay = 5,
|
||||
AddSound = this.Env_AddSound,
|
||||
AddAction = this.Env_AddAction,
|
||||
SetMusic = this.Env_SetMusic
|
||||
};
|
||||
// Register default environments (overloadable)
|
||||
this->InitializeEnvironments();
|
||||
// Periodic execution of ambience events
|
||||
last_environment = [];
|
||||
AddTimer(this.Execute, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
func InitializeEnvironments()
|
||||
{
|
||||
// Register all standard environments
|
||||
environments = [];
|
||||
// Underwater: Clonk is swimming in water
|
||||
var underwater = this.env_underwater = new Environment {};
|
||||
underwater->SetMusic("underwater");
|
||||
underwater.CheckPlayer = this.EnvCheck_Underwater;
|
||||
AddEnvironment(underwater, 1400);
|
||||
// City: Clonk is surrounded by buildings
|
||||
var city = this.env_city = new Environment {};
|
||||
city->SetMusic("city");
|
||||
city.CheckPlayer = this.EnvCheck_City;
|
||||
AddEnvironment(city, 1200);
|
||||
// Lava: Lava material is nearby
|
||||
var lava = this.env_lava = new Environment {};
|
||||
lava->SetMusic("lava");
|
||||
lava.CheckPlayer = this.EnvCheck_Lava;
|
||||
lava.mat_mask = CreateArray(); // material mask for lava materials. +1 cuz sky.
|
||||
lava.mat_mask[Material("Lava")+1] = true; // loop over materials and check incindiary instead? Whoever introduces the next lava type can do that...
|
||||
lava.mat_mask[Material("DuroLava")+1] = true;
|
||||
lava.min_change_delay = 3; // Easy to miss lava on search.
|
||||
AddEnvironment(lava, 1000);
|
||||
// Underground: Clonk in front of tunnel
|
||||
var underground = this.env_underground = new Environment {};
|
||||
underground->SetMusic("underground");
|
||||
underground.CheckPlayer = this.EnvCheck_Underground;
|
||||
AddEnvironment(underground, 800);
|
||||
// Mountains: Overground and lots of rock around
|
||||
var mountains = this.env_mountains = new Environment {};
|
||||
mountains->SetMusic("mountains");
|
||||
mountains.CheckPlayer = this.EnvCheck_Mountains;
|
||||
mountains.mat_mask = CreateArray(); // material mask for mountain materials. +1 cuz sky.
|
||||
mountains.mat_mask[Material("Rock")+1] = true;
|
||||
mountains.mat_mask[Material("Granite")+1] = true;
|
||||
mountains.mat_mask[Material("Ore")+1] = true;
|
||||
mountains.mat_mask[Material("Gold")+1] = true;
|
||||
mountains.min_change_delay = 3; // Pretty unstable condition
|
||||
AddEnvironment(mountains, 600);
|
||||
// Snow: It's snowing around the clonk
|
||||
var snow = this.env_snow = new Environment {};
|
||||
snow->SetMusic("snow");
|
||||
snow.CheckPlayer = this.EnvCheck_Snow;
|
||||
snow.min_change_delay = 6; // Persist a while after snowing stopped
|
||||
snow.mat = Material("Snow");
|
||||
AddEnvironment(snow, 400);
|
||||
// Night: Sunlight blocked by planet
|
||||
var night = this.env_night = new Environment {};
|
||||
night->SetMusic("night");
|
||||
night.CheckPlayer = this.EnvCheck_Night;
|
||||
AddEnvironment(night, 200);
|
||||
// Overground: Default environment
|
||||
var overground = this.env_overground = new Environment {};
|
||||
overground->SetMusic("overground");
|
||||
overground.CheckPlayer = this.EnvCheck_Overground;
|
||||
overground->AddSound("Ding", 100);
|
||||
AddEnvironment(overground, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private func Execute()
|
||||
{
|
||||
// Per-player execution every third timer (~.8 seconds)
|
||||
var i=GetPlayerCount(C4PT_User);
|
||||
while (i--) if (!(++exec_counter % 3))
|
||||
{
|
||||
ExecutePlayer(GetPlayerByIndex(i, C4PT_User));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private func ExecutePlayer(int plr)
|
||||
{
|
||||
var cursor = GetCursor(plr);
|
||||
// Determine environment the player is currently in
|
||||
var environment = nil;
|
||||
if (cursor)
|
||||
{
|
||||
var last_env = last_environment[plr];
|
||||
var x = cursor->GetX(), y = cursor->GetY();
|
||||
for (test_environment in environments)
|
||||
{
|
||||
if (environment = test_environment->CheckPlayer(cursor, x, y, test_environment == last_env))
|
||||
{
|
||||
// We've found a matchign environment.
|
||||
// Was it a change? Then check delays first
|
||||
if (test_environment != last_env)
|
||||
{
|
||||
if (last_env && last_env.no_change_delay)
|
||||
{
|
||||
// Environment should change but a delay is specified. Keep last environment for now.
|
||||
--last_env.no_change_delay;
|
||||
environment = last_env;
|
||||
break;
|
||||
}
|
||||
// New environment and change delay has passed.
|
||||
environment.no_change_delay = environment.min_initial_change_delay;
|
||||
Log("%s environment: %s", GetPlayerName(plr), environment.music);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Was no change: Reset change delays
|
||||
environment.no_change_delay = Max(environment.no_change_delay, environment.min_change_delay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
last_environment[plr] = environment;
|
||||
if (!environment) return true;
|
||||
// Music by environment
|
||||
this->SetPlayList(environment.music, plr, true, 3000);
|
||||
// Sounds and actions by environment
|
||||
for (var action in environment.actions)
|
||||
if (Random(1000) < action.chance)
|
||||
cursor->Call(action.fn, action.par[0], action.par[1], action.par[2], action.par[3], action.par[4]);
|
||||
return true;
|
||||
}
|
||||
|
||||
func InitializePlayer(int plr)
|
||||
{
|
||||
// Newly joining players should have set playlist immediately (so they don't start playing a random song just to switch it immediately)
|
||||
ExecutePlayer(plr);
|
||||
return true;
|
||||
}
|
||||
|
||||
func RemovePlayer(int plr)
|
||||
{
|
||||
// Ensure newly joining players don't check on another player's environment
|
||||
last_environment[plr] = nil;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected func Activate(int byplr)
|
||||
{
|
||||
MessageWindow(this.Description, byplr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Environment functions */
|
||||
|
||||
func AddEnvironment(proplist new_env, priority)
|
||||
{
|
||||
if (GetType(priority)) new_env.Priority = priority;
|
||||
this.environments[GetLength(environments)] = new_env;
|
||||
SortArrayByProperty(this.environments, "Priority", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private func Env_AddSound(string snd_name, chance)
|
||||
{
|
||||
return Env_AddAction(Global.Sound, snd_name, chance ?? 50);
|
||||
}
|
||||
|
||||
private func Env_AddAction(afn, par0, par1, par2, par3, par4)
|
||||
{
|
||||
return this.actions[GetLength(this.actions)] = { fn=afn, par=[par0, par1, par2, par3, par4] };
|
||||
}
|
||||
|
||||
private func Env_SetMusic(string playlist)
|
||||
{
|
||||
this.music = playlist;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Default environment checks */
|
||||
|
||||
private func EnvCheck_Underwater(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Clonk should be swimming
|
||||
if (cursor->GetProcedure() != "SWIM") return nil;
|
||||
// For initial change, clonk should also be diving: Check for breath below 80%
|
||||
// Use > instead of >= to ensure 0-breath-clonks can also get the environment
|
||||
if (!is_current && cursor->GetBreath() > cursor.MaxBreath*4/5) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_City(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// There must be buildings around the clonk
|
||||
var building_count = cursor->ObjectCount(cursor->Find_AtRect(-180,-100,360,200), Find_Func("IsStructure"));
|
||||
// 3 buildings to start the environment. Just 1 building to sustain it.
|
||||
if (building_count < 3-2*is_current) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Lava(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for lava pixels. First check if the last lava pixel we found is still in place.
|
||||
var search_range;
|
||||
if (is_current)
|
||||
{
|
||||
if (this.mat_mask[GetMaterial(this.last_x, this.last_y)+1])
|
||||
if (Distance(this.last_x, this.last_y, x, y) < 140)
|
||||
return this;
|
||||
search_range = 140;
|
||||
}
|
||||
else
|
||||
{
|
||||
search_range = 70;
|
||||
}
|
||||
// Now search for lava in search range
|
||||
var ang = Random(360);
|
||||
for (; search_range >= 0; search_range -= 10)
|
||||
{
|
||||
ang += 200;
|
||||
var x2 = x + Sin(ang, search_range);
|
||||
var y2 = y + Cos(ang, search_range);
|
||||
if (this.mat_mask[GetMaterial(x2, y2)+1])
|
||||
{
|
||||
// Lava found!
|
||||
this.last_x = x2;
|
||||
this.last_y = y2;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
// No lava found
|
||||
return nil;
|
||||
}
|
||||
|
||||
private func EnvCheck_Underground(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for underground: No sky at cursor or above
|
||||
if (GetMaterial(x,y)<0) return nil;
|
||||
if (GetMaterial(x,y-30)<0) return nil;
|
||||
if (GetMaterial(x-10,y-20)<0) return nil;
|
||||
if (GetMaterial(x+10,y-20)<0) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Mountains(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for mountains: Rock materials below
|
||||
var num_rock;
|
||||
for (var y2=0; y2<=45; y2+=15)
|
||||
for (var x2=-75; x2<=75; x2+=15)
|
||||
num_rock += this.mat_mask[GetMaterial(x+x2,y+y2)+1];
|
||||
// need 15pts on first check; 5 to sustain
|
||||
if (num_rock < 15-is_current*10) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Snow(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Must be snowing from above
|
||||
if (GetPXSCount(this.mat, x-300, y-200, 600, 300) < 20 - is_current*15) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Night(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Night time.
|
||||
var time = FindObject(Find_ID(Environment_Time));
|
||||
if (!time || !time->IsNight()) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Overground(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// This is the fallback environment
|
||||
return this;
|
||||
}
|
||||
|
||||
/*-- Proplist --*/
|
||||
|
||||
local Name = "$Name$";
|
||||
local Description = "$Description$";
|
||||
local Environment;
|
|
@ -0,0 +1,2 @@
|
|||
Name=Ambiente
|
||||
Description=Regelt die Geraeuschkulisse.
|
|
@ -0,0 +1,3 @@
|
|||
Name=Ambience
|
||||
Description=Controls environmental sounds and music.
|
||||
|
Loading…
Reference in New Issue