Add sound and music ambience object.

Also tagged the existing music as "day". Only enabled in the first three missions for now.
lights3
Sven Eberhardt 2015-08-29 14:11:00 -04:00
parent 3886f76bf3
commit 05a78138a3
51 changed files with 789 additions and 2432 deletions

View File

@ -1,5 +0,0 @@
[DefCore]
id=Ambience
Version=6,0
Category=C4D_StaticBack | C4D_Rule
Picture=0,0,128,128

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,297 +0,0 @@
/**
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);
exec_counter += !(i%3);
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;

View File

@ -1,15 +0,0 @@
[DefCore]
id=CrystalCommunicator
Version=6,0
Category=C4D_Structure
Width=90
Height=70
Offset=-45,-35
Vertices=2
VertexX=-40,40
VertexY=34,34
VertexFriction=100,100
Mass=300
Components=Ruby=6;Amethyst=6;Metal=6;
Rotate=0
Construction=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

View File

@ -1,357 +0,0 @@
/*--
Crystal communicator
Author: Sven2
Shining structure built from gems and metal
--*/
#include Library_Structure
local top_face, base_face;
public func IsCrystalCommunicator() { return !base_face; }
/* Construction */
public func SetConstructionSiteOverlay(object site, int dir, object stick, object component_obj)
{
// Play component-specific sound for adding stuff to the site
if (component_obj && !component_obj->GetDefFragile()) component_obj->~Hit();
// Construction site graphics by provided metal
var metal_completion = site->ContentsCount(Metal) * 3 / Max(GetComponent(Metal, nil, nil, this), 1);
site->SetGraphics(["Site0", "Site1", "Site2", nil][metal_completion], CrystalCommunicator, 1, GFXOV_MODE_Base);
site->SetGraphics(nil, nil, 2);
// Add graphics of contained gems
UpdateGemOverlays(site, [1, 3, 7, 12][metal_completion]);
return true;
}
public func DoConstructionEffects(object site)
{
// Grab all gems from site
GrabContents(site);
var metal;
while (metal = FindContents(Metal)) metal->RemoveObject();
// Site is done immediately
SetCon(100);
// Create TopFace overlay
top_face = CreateObjectAbove(GetID(),0,35,GetOwner());
top_face.Plane = this.Plane + 10;
top_face->SetGraphics("TopFace");
top_face->SetAction("Attach", this);
top_face.base_face = this;
// Transfer gem overlays
this.gem_overlays = site.gem_overlays;
UpdateGemOverlays(this, 13, true);
// Construction done. Remove site.
site->RemoveObject(site);
// Start finals effect
return true;
}
/* Gem overlays */
static const CrystalCommunicator_GemsX = [ 15,440,336,221,121,298,220, 50, 14,333,129, 77],
CrystalCommunicator_GemsY = [255, 75,100, 84, 44,107, 15,130,107,153,149,106],
CrystalCommunicator_GemsZ = [ 5, 3, 4, 0, 4, 0, 5, 2, 3, 1, 1, 4],
CrystalCommunicator_GemCount = 12;
private func UpdateGemOverlays(object obj, int max_overlays, bool refresh_existing)
{
// Add overlays for any gems that have not yet been added
var gem_overlay_index = 3;
if (!obj.gem_overlays) obj.gem_overlays = [];
var n_overlays = GetLength(obj.gem_overlays);
var i;
// Remove overlays of gems that have left
for (i=0; i<n_overlays; ++i)
if (!obj.gem_overlays[i] || obj.gem_overlays[i]->Contained() != obj)
{
obj->SetGraphics(nil, nil, gem_overlay_index+i);
if (obj.top_face) obj.top_face->SetGraphics(nil, nil, gem_overlay_index+i);
obj.gem_overlays[i] = nil;
}
// Add new overlays
for (var gem in FindObjects(Find_Container(obj), Find_Func("GetGemColor")))
{
// Already displayed?
i = GetIndexOf(obj.gem_overlays, gem);
if (i>=0)
{
if (!refresh_existing) continue;
}
else
{
// Find a spot for this gem
i = GetIndexOf(obj.gem_overlays, nil);
if (i<0) i=n_overlays;
// No free space?
if (i == max_overlays) if (refresh_existing) continue; else break;
}
// Add overlay
var gem_gfx = gem.graphics_index;
if (gem_gfx) gem_gfx = Format("%d", gem_gfx+1); else gem_gfx = nil;
var x = CrystalCommunicator_GemsX[i];
var y = CrystalCommunicator_GemsY[i];
var z = CrystalCommunicator_GemsZ[i];
var size = z*100+500;
var off_y;
if (obj == this) off_y = 35000; else off_y = 70000;
var gem_target;
if (obj.top_face && z>=3) gem_target = obj.top_face; else gem_target = obj;
gem_target->SetGraphics(gem_gfx, gem->GetID(), gem_overlay_index+i, GFXOV_MODE_Base);
gem_target->SetObjDrawTransform(size,0,x*200-45000, 0,size,y*200-off_y, gem_overlay_index+i);
if (z<3) gem_target->SetClrModulation(0xffb0b0b0, gem_overlay_index+i);
// Remember in list
obj.gem_overlays[i] = gem;
n_overlays = GetLength(obj.gem_overlays);
}
// Make sure a glitter effect is there
if (!GetEffect("IntGemGlitter", obj)) AddEffect("IntGemGlitter", obj, 1, 12, nil, CrystalCommunicator);
return true;
}
private func FxIntGemGlitterTimer(target)
{
// Glitter at random gem position
if (Random(2))
{
var i = Random(12), gem;
if (gem = target.gem_overlays[i])
{
var x = CrystalCommunicator_GemsX[i]/5 - 45;
var y = CrystalCommunicator_GemsY[i]/5 - 35;
if (target->GetID() == ConstructionSite) y -= 35;
var size = CrystalCommunicator_GemsZ[i]*4 + 10;
var sparkle_fx = GetEffect("Sparkle", gem);
sparkle_fx.Size = PV_KeyFrames(1, 0, 0, 500, size, 1000, 0); // modifying value directly in gem, assuming gem won't leave this structure any more
if (sparkle_fx && sparkle_fx.particles)
target->CreateParticle("MagicRing", x, y, 0, 0, size, sparkle_fx.particles, 1);
}
}
}
/* End sequence */
local ruby_particle, amethyst_particle, beam_particles, gem_particles;
local flash_particle, small_flash_particle, large_flash_particle;
local time;
local send_code, next_send_time, send_code_pos;
public func StartCommunication()
{
// forward to main object
if (base_face) return base_face->StartCommunication();
// Init particles
// Gem particles
beam_particles = CreateArray(CrystalCommunicator_GemCount);
gem_particles = CreateArray(CrystalCommunicator_GemCount);
ruby_particle = new Particles_MagicRing() { R = 0xff, G = 0x00, B = 0x30 };
amethyst_particle = new Particles_MagicRing() { R = 0xa0, G = 0x00, B = 0xff };
for (var i=0; i<CrystalCommunicator_GemCount; ++i)
{
var base;
if (this.gem_overlays && this.gem_overlays[i])
{
if (this.gem_overlays[i]->GetID() == Ruby) base = ruby_particle; else base = amethyst_particle;
}
else
{
if (i%2) base = ruby_particle; else base = amethyst_particle;
}
gem_particles[i] = base;
var x = CrystalCommunicator_GemsX[i]/5 - 45;
beam_particles[i] = CreateCirclingParticle(base, 100, 20, Abs(x), x>0);
}
// Central flash particles
flash_particle = {
Size = PV_KeyFrames(0, 0, 0, 500, 60, 1000, 0),
R = PV_KeyFrames(0, 750, 255, 1000, 0),
G = PV_KeyFrames(0, 300, 255, 1000, 0),
B = PV_KeyFrames(0, 300, 255, 500, 0),
Rotation = PV_Random(0, 360),
Alpha = PV_KeyFrames(0, 0, 255, 750, 100, 1000, 0),
BlitMode = GFX_BLIT_Additive,
Attach = ATTACH_Front | ATTACH_MoveRelative,
};
large_flash_particle = {
Size = PV_KeyFrames(0, 0, 0, 500, 200, 1000, 0),
R = PV_KeyFrames(0, 750, 255, 1000, 0),
G = PV_KeyFrames(0, 300, 255, 1000, 0),
B = PV_KeyFrames(0, 300, 255, 500, 0),
Rotation = PV_Random(0, 360),
Alpha = PV_KeyFrames(0, 0, 255, 750, 100, 1000, 0),
BlitMode = GFX_BLIT_Additive,
Attach = ATTACH_Front | ATTACH_MoveRelative,
};
// Gem flash particle
small_flash_particle = {
Size = PV_KeyFrames(0, 0, 0, 500, 20, 1000, 0),
R = PV_KeyFrames(0, 750, 255, 1000, 0),
G = PV_KeyFrames(0, 300, 255, 1000, 0),
B = PV_KeyFrames(0, 300, 255, 500, 0),
Rotation = PV_Random(0, 360),
Alpha = PV_KeyFrames(0, 0, 255, 750, 100, 1000, 0),
BlitMode = GFX_BLIT_Additive,
Attach = ATTACH_Front | ATTACH_MoveRelative,
};
// Run effects
Sound("CrystalCommCharge");
time = 0;
AddTimer(this.PreActivity, 5);
}
private func PreActivity()
{
// Warmup effects
var i,x,y,z;
for (i=0; i<CrystalCommunicator_GemCount; ++i)
{
x = CrystalCommunicator_GemsX[i]/5 - 45;
y = CrystalCommunicator_GemsY[i]/5 - 35;
z = CrystalCommunicator_GemsZ[i];
var gem_target;
if (top_face && z>=3) gem_target = top_face; else gem_target = this;
if (time < 20 || !Random(3))
{
if (!(time % 5)) gem_target->CreateParticle("StarFlash", x,y, 0,0, 60+Random(10), small_flash_particle, 1);
}
else
gem_target->CreateParticle("StarFlash", x, y, -x, -y, 10, small_flash_particle, 10);
}
if (time == 20) Sound("CrystalCommBoost");
if (time > 50)
{
RemoveTimer(this.PreActivity);
time = 0;
CreateParticle("StarFlash", PV_Random(-12, +12), PV_Random(-12, +12), PV_Random(-10, +10),PV_Random(-10, +10), PV_Random(20, 100), large_flash_particle, 10);
Sound("CrystalCommWumm");
SetAction("Active");
AddTimer(this.Activity, 1);
}
++time;
}
public func StopCommunication()
{
// forward to main object
if (base_face) return base_face->StopCommunication();
// Stop activities
RemoveTimer(this.PreActivity);
RemoveTimer(this.Activity);
SetAction("Idle");
time = 0;
return true;
}
private func Activity()
{
// Send codes
if (send_code)
{
if (next_send_time == time)
{
var send_char = GetChar(send_code, send_code_pos++);
if (!send_char)
{
// All sent.
send_code = nil;
}
else
{
// Next char to send
if (send_char == GetChar("."))
{
Sound("CrystalCommToneA");
next_send_time = time + 13;
}
else
{
Sound("CrystalCommToneB");
next_send_time = time + 27;
}
}
}
if (next_send_time - time > 10)
{
// During tone: Effects
CreateParticle("StarFlash", PV_Random(-3, +3), 0, 0,-30, 500, flash_particle, 1);
}
}
// Effects
// Circulate through gems
var i = time % CrystalCommunicator_GemCount;
var x = CrystalCommunicator_GemsX[i]/5 - 45, y = CrystalCommunicator_GemsY[i]/5 - 35, z = CrystalCommunicator_GemsZ[i];
var gem_target;
if (top_face && z>=3) gem_target = top_face; else gem_target = this;
// Create ring moving upwards
if (Abs(x) > 5) CreateParticle("MagicRing", x, y, 0, -Min(time/20,10), 2000, beam_particles[i], 1);
// Create flash at gem
gem_target->CreateParticle("StarFlash", x, y, 0, 0, 20+Random(10), gem_particles[i], 1);
// Create central flash
if (!(time % 5)) CreateParticle("StarFlash", PV_Random(-6, +6), PV_Random(-6, +6), 0,0, 20+Random(10), flash_particle, 1);
++time;
}
private func CreateCirclingParticle(proplist prototype, int frames_per_cycle, int num_cycles, int rad, bool start_backmove)
{
var a = (rad * 10000000) / (2429 * frames_per_cycle * frames_per_cycle);
var ang0 = (!!start_backmove) * 180;
var particle = {
Prototype = prototype,
Size = PV_Sin(PV_Linear( ang0, 360*num_cycles),5,8),
ForceX = PV_Sin(PV_Linear( ang0+90, ang0+90+360*num_cycles), a, 0),
ForceY = 0,
Attach = ATTACH_Front | ATTACH_MoveRelative,
};
return particle;
}
public func SendCode(string code)
{
send_code = code;
next_send_time = time;
send_code_pos = 0;
return true;
}
/* Definition data */
public func Definition(proplist def)
{
}
local ActMap = {
Attach = {
Prototype = Action,
Name = "Attach",
Procedure = DFA_ATTACH,
Directions = 1,
FacetBase = 1,
Length = 1,
Delay = 0,
NextAction = "Hold"
},
Active = {
Prototype = Action,
Name = "Active",
Procedure = DFA_NONE,
Directions = 1,
FacetBase = 1,
Delay = 0,
Length = 1,
NextAction = "Active",
Sound = "CrystalCommActive",
},
};
local Collectible = false;
local Name = "$Name$";
local Description = "$Description$";
local Touchable = 0;
local Plane = 280;

View File

@ -1,2 +0,0 @@
Name=Kristallkommunikator
Description=Gerät zur Langstreckenkommunikation

View File

@ -1,2 +0,0 @@
Name=Crystal communicator
Description=Device for long range communication.

View File

@ -1,5 +0,0 @@
[DefCore]
id=Goal_BuildCrystalCommunicator
Version=6,0
Category=C4D_StaticBack|C4D_Goal
Picture=0,0,128,128

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,61 +0,0 @@
/*--
Build crystal communicator
Author: Sven2
Player must build the crystal communicator
--*/
#include Library_Goal
/*-- Goal interface --*/
// The goal is fulfilled if the communicator has been built
public func IsFulfilled()
{
return ObjectCount(Find_ID(CrystalCommunicator));
}
public func GetDescription(int plr)
{
var message;
if (IsFulfilled())
message = "$MsgGoalFulfilled$";
else
message = "$MsgGoalUnfulfilled$";
return message;
}
// Shows or hides a message window with information.
public func Activate(int plr)
{
// If goal message open -> hide it.
if (GetEffect("GoalMessage", this))
{
CustomMessage("", nil, plr, nil, nil, nil, nil, nil, MSG_HCenter);
RemoveEffect("GoalMessage", this);
return;
}
// Otherwise open a new message.
AddEffect("GoalMessage", this, 100, 0, this);
var message;
if (IsFulfilled())
{
message = "@$MsgGoalFulfilled$";
}
else
{
message = "@$MsgGoalUnfulfilled$";
}
CustomMessage(message, nil, plr, 0, 16 + 64, 0xffffff, GUI_MenuDeco, this, MSG_HCenter);
return;
}
protected func FxGoalMessageStart() {}
//public func GetShortDescription(int plr) { return ""; }
/*-- Proplist --*/
local Name = "$Name$";

View File

@ -1,5 +0,0 @@
Name=Kristallkommunikator bauen
#Goal window
MsgGoalFulfilled=Glückwunsch; der Kristallkommunikator steht.
MsgGoalUnfulfilled=Bringe das nötige Baumaterial zur Baustelle des Kristallkommunikator!

View File

@ -1,5 +0,0 @@
Name=Build crystal communicator
#Goal window
MsgGoalFulfilled=Congratulations, the crystal communicator is done.
MsgGoalUnfulfilled=Find the missing components and bring them to the crystal communicator construction site!

View File

@ -1,275 +0,0 @@
/**
Blue lake
Dynamic map with a big lake containing islands of material
Plus lava basins with gems at the bottom
Parts adapted from Gem Grabbers by Maikel
@authors Sven2
*/
#include Library_Map
// zoomed coordinates for scenario script
static main_island_x, main_island_y;
static goal_platform_x, goal_platform_y;
// set after intro to force map creation
static g_intro_done;
// Called be the engine: draw the complete map here.
public func InitializeMap(proplist map)
{
if (!g_intro_done && !SCEN_TEST) return true;
Resize(300,400);
this.sea_y = 50;
this.ground_y = 350;
this.map_zoom = 7;
main_island_x = this.Wdt/2 * this.map_zoom;
main_island_y = this.sea_y * this.map_zoom;
Draw("Water", nil, [0,this.sea_y,this.Wdt,this.Hgt]);
DrawMainIsland(80);
DrawGround();
// Regular resource islands
DrawSecondaryIslands(3, 15, [["Ore", 50], ["Coal", 40]], true);
DrawSecondaryIslands(10, 6, [["Firestone", 70]], false);
DrawSecondaryIslands(3, 8, [["Gold", 40]], true);
// Amethyst islands
var i=0, imod=Random(2);
while (i<3 || GetPixelCount("Amethyst")<15)
{
DrawSecondaryIsland(8, [["Amethyst", 70]], true, [0, this.Wdt-70][(i+imod)%2], 70, this.sea_y+50);
++i;
}
FixLiquidBorders("Earth");
// Ensure that left and right side are always open because they're used for water refill
Draw("Sky", nil, [0,0,1,this.sea_y]);
Draw("Sky", nil, [this.Wdt-1,0,1,this.sea_y]);
Draw("Water", nil, [0,this.sea_y,1,70]);
Draw("Water", nil, [this.Wdt-1,this.sea_y,1,70]);
// Top level of water has sky background
Draw("^Water", Duplicate("Water"), [0,this.sea_y,this.Wdt,11]);
// Return true to tell the engine a map has been successfully created.
return true;
}
// Draws the main island with all basic resources
private func DrawMainIsland(int size)
{
// Shape of the main island. Shape taken from Gem Grabbers.
var island_algo = {Algo = MAPALGO_Polygon};
var x = this.Wdt / 2;
var y = this.sea_y;
island_algo.X = [x-size/3, x-size/2, x-size/3, x-size/6, x+size/6, x+size/3, x+size/2, x+size/3];
island_algo.Y = [y-size/6, y, y+size/3, y+size/6, y+size/6, y+size/3, y, y-size/6];
// Draw the earth patch of the island.
island_algo = {Algo = MAPALGO_Turbulence, Iterations = 4, Op = island_algo};
var island = CreateLayer();
island->Draw("Earth", island_algo);
// Draw goal platform shape
while (island->GetPixel(x,y)) ++x; // Find right side of island at sea level
var platform_algo = {Algo = MAPALGO_Polygon};
platform_algo.X = [x-5,x+14,x+14,x+7,x ,x-5];
platform_algo.Y = [y ,y ,y+ 1,y+2,y+4,y ];
island->Draw("Earth", platform_algo);
// Preserve drawn island shape for border algorithms
var island_shape = island->Duplicate();
// Overlay a set of materials inside the island.
DrawIslandMat("Earth-earth_dry", island, 4, 30, true);
DrawIslandMat("Earth-earth_midSoil", island, 3, 30, true);
DrawIslandMat("Tunnel", island, 3, 10, true);
//DrawIslandMat("Water", island, 4, 8);
//DrawIslandMat("Gold", island, 3, 6);
DrawIslandMat("Ore", island, 6, 18, true);
DrawIslandMat("Coal", island, 6, 18, true);
DrawIslandMat("Firestone", island, 6, 12, true);
// Draw a top border out of sand and top soil.
var sand_border = {Algo = MAPALGO_And, Op = [{Algo = MAPALGO_Border, Op = island_shape, Top = [-1,2]}, {Algo = MAPALGO_RndChecker, Ratio = 50, Wdt = 4, Hgt = 3}]};
var topsoil_border = {Algo = MAPALGO_And, Op = [{Algo = MAPALGO_Border, Op = island_shape, Top = [-1,3]}, {Algo = MAPALGO_RndChecker, Ratio = 40, Wdt = 4, Hgt = 2}]};
island->Draw("Sand", sand_border);
island->Draw("Earth-earth_topSoil", topsoil_border);
// Draw a bottom border out of granite and rock.
var granite_border = {Algo = MAPALGO_Border, Op = island_shape, Bottom = [-4,3]};
island->Draw("Granite", granite_border);
var rock_border = {Algo = MAPALGO_RndChecker, Ratio = 20, Wdt = 2, Hgt = 2};
island->Draw("Rock", {Algo = MAPALGO_And, Op = [granite_border, rock_border]});
island->Draw("Rock-rock_cracked", {Algo = MAPALGO_And, Op = [granite_border, rock_border]});
// Draw goal platform
island->Draw("Sky", nil, [x,y-10,14,10]);
island->Draw("Brick", nil, [x,y,14,2]);
goal_platform_x = (x+7)*this.map_zoom;
goal_platform_y = y *this.map_zoom;
// Draw island onto main map
Blit(island);
return true;
}
// Draws multiple underwater resource islands
private func DrawSecondaryIslands(int n, ...)
{
for (var i=0; i<n; ++i) DrawSecondaryIsland(...);
return true;
}
// Draws underwater resource island
private func DrawSecondaryIsland(int size, array materials, bool has_border, int xmin, int xwdt, int ymin)
{
// Find a free spot underwater
var x,y;
var border = size; // left and right border
if (!xwdt) xwdt = this.Wdt;
if (!ymin) ymin = this.sea_y;
var i_tries = 200;
while (i_tries--)
{
var x = Random(xwdt - border*2) + border + xmin;
var y = Random(this.ground_y - ymin - size) + ymin + size/2;
if (GetPixelCount("Solid", [x-size,y-size,size,size])) continue;
break;
}
// Shape of the resource island
var island_algo = {Algo = MAPALGO_Ellipsis, X=x, Y=y, Wdt=size, Hgt=size};
island_algo = {Algo = MAPALGO_Turbulence, Amplitude = [20,5], Iterations = 3, Op = island_algo};
var island = CreateLayer();
island->Draw("Earth", island_algo);
var island_shape = island->Duplicate();
DrawIslandMat("Earth-earth_dry", island, 4, 30);
DrawIslandMat("Earth-earth_midSoil", island, 3, 30);
// Overlay a set of materials inside the island.
for (var mat in materials)
{
DrawIslandMat(mat[0], island, 3, mat[1], has_border);
}
// Draw a border out of granite and rock.
if (has_border)
{
var island_shape = island->Duplicate();
var granite_border = {Algo = MAPALGO_Border, Op = island_shape, Top = [-2,2]};
island->Draw("Granite", granite_border);
var rock_border = {Algo = MAPALGO_RndChecker, Ratio = 20, Wdt = 2, Hgt = 2};
island->Draw("Rock", {Algo = MAPALGO_And, Op = [granite_border, rock_border]});
island->Draw("Rock-rock_cracked", {Algo = MAPALGO_And, Op = [granite_border, rock_border]});
}
// Draw island onto main map
Blit(island);
return true;
}
private func DrawGround()
{
// Bottom of the sea
var ground_algo = { Algo = MAPALGO_Rect, X=-100, Y=this.ground_y, Wdt=this.Wdt+200, Hgt=this.Hgt-this.ground_y+1000 };
ground_algo = {Algo = MAPALGO_Turbulence, Iterations = 4, Amplitude = [10,100], Scale = 20, Op = ground_algo };
var ground2_algo = { Algo = MAPALGO_Rect, X=-100, Y=this.Hgt-10, Wdt=this.Wdt+200, Hgt=this.Hgt-this.ground_y+1000 };
ground2_algo = {Algo = MAPALGO_Turbulence, Iterations = 2, Amplitude = 10, Scale = 30, Op = ground2_algo };
var ground = CreateLayer();
// Ensure lots of earth
while (true)
{
ground->Draw("Earth", ground_algo);
ground->Draw("Granite", ground2_algo);
if (ground->GetPixelCount("Earth") > 10000) break;
}
ground->Draw("DuroLava", {Algo=MAPALGO_Turbulence, Amplitude=10, Scale=20, Iterations=3, Op={Algo=MAPALGO_And, Op=[ground->Duplicate("Granite"), {Algo = MAPALGO_RndChecker, Ratio=50, Wdt=30, Hgt=2}]}});
// Granite/Rock top border
ground->Draw("Granite", {Algo = MAPALGO_Turbulence, Amplitude = 5, Iterations = 1, Op = {Algo = MAPALGO_Border, Op = ground->Duplicate(), Top= [-2,2]}});
ground->Draw("Rock", {Algo=MAPALGO_And, Op=[ground->Duplicate("Granite"), {Algo = MAPALGO_RndChecker, Ratio = 40, Wdt = 2, Hgt = 2}]});
// Alterations in earth material
DrawIslandMat("Earth-earth_dry", ground, 12, 60, false);
DrawIslandMat("Earth-earth_midSoil", ground, 8, 60, false);
// Gem spots
var gem_spots = CreateLayer();
var earth_mats = CreateMatTexMask("Earth");
var i=0;
while (i<3 || gem_spots->GetPixelCount("Ruby") < 15)
{
var gem_mat = "Ruby";
// Find an earth spot
var pos = {X=Random(this.Wdt), Y=this.Hgt/2+Random(this.Hgt/2)};
ground->FindPosition(pos, "Earth");
// Find center of this earth area
var x=pos.X, y=pos.Y;
var x0=x-1, x1=x+1, y0=y-1, y1=y+1;
while (earth_mats[ground->GetPixel(x,y0)]) --y0;
while (earth_mats[ground->GetPixel(x,y1)]) ++y1;
y=Min((y0+y1)/2, this.Hgt-6);
while (earth_mats[ground->GetPixel(x0,y)]) --x0;
while (earth_mats[ground->GetPixel(x1,y)]) ++x1;
x=(x0+x1)/2;
var size = 9;
// Lava basin here
var lava_algo = {Algo = MAPALGO_Ellipsis, X=x, Y=y, Wdt=size, Hgt=size};
lava_algo = {Algo = MAPALGO_Turbulence, Amplitude = 5, Iterations = 5, Op = lava_algo};
gem_spots->Draw("DuroLava", lava_algo);
// Gems at bottom center
y += 2;
size = 3;
var gem_algo = {Algo = MAPALGO_Ellipsis, X=x, Y=y, Wdt=size, Hgt=size};
gem_algo = {Algo = MAPALGO_Turbulence, Amplitude = 3, Iterations = 1, Op = gem_algo};
gem_spots->Draw(gem_mat, gem_algo);
// Draw to map
ground->Blit(gem_spots);
++i;
}
// Lava basins surrounded by granite
ground->Draw("Rock", {Algo=MAPALGO_And, Op=[{Algo=MAPALGO_Not, Op=gem_spots}, {Algo = MAPALGO_Turbulence, Amplitude = 5, Iterations = 5, Op = {Algo = MAPALGO_Border, Op = gem_spots, Wdt=-4}}]});
ground->Draw("Granite", {Algo = MAPALGO_Border, Op = gem_spots, Wdt=-1});
// Lots of rocks
DrawIslandMat("Rock-rock_cracked", ground, 2, 20, false);
DrawIslandMat("Rock", ground, 2, 20, false);
// And some lava
DrawIslandMat("DuroLava", ground, 2, 20, false);
// Other materials (rare)
DrawIslandMat("Ore", ground, 12, 8, false);
DrawIslandMat("Coal", ground, 12, 8, false);
DrawIslandMat("Gold", ground, 12, 8, false);
DrawIslandMat("Firestone", ground, 12, 8, false);
Blit(ground);
return true;
}
// Draws some material inside an island.
private func DrawIslandMat(string mat, proplist onto_mask, int speck_size, int ratio, has_border)
{
if (!speck_size)
speck_size = 3;
if (!ratio)
ratio = 20;
// Use random checker algorithm to draw patches of the material.
var rnd_checker = {Algo = MAPALGO_RndChecker, Ratio = ratio, Wdt = speck_size, Hgt = speck_size};
rnd_checker = {Algo = MAPALGO_Turbulence, Iterations = 4, Op = rnd_checker};
var algo;
if (has_border)
{
var mask_border = {Algo = MAPALGO_Border, Op = onto_mask, Wdt = 3};
algo = {Algo = MAPALGO_And, Op = [{Algo = MAPALGO_Xor, Op = [onto_mask->Duplicate("Earth"), mask_border]}, rnd_checker]};
}
else
{
algo = {Algo = MAPALGO_And, Op = [onto_mask->Duplicate("Earth"), rnd_checker]};
}
onto_mask->Draw(mat, algo);
return true;
}

View File

@ -1,57 +0,0 @@
[Head]
Title=DeepSeaMining
Icon=31
Version=6,0
Difficulty=30
MissionAccess=S2Crash
[Definitions]
Definition1=Objects.ocd
[Game]
Rules=Rule_TeamAccount=1;Rule_BuyAtFlagpole=1;Rule_BaseRespawn=1;
Goals=Goal_BuildCrystalCommunicator=1;
ValueOverloads=Ruby=10;Amethyst=10
[Player1]
Wealth=25
Crew=Clonk=2
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;JarOfWinds=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
BaseMaterial=Clonk=25;Bread=25;
BaseProduction=Clonk=25;Bread=25;
[Player2]
Wealth=25
Crew=Clonk=2
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;JarOfWinds=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
BaseMaterial=Clonk=25;Bread=25;
BaseProduction=Clonk=25;Bread=25;
[Player3]
Wealth=25
Crew=Clonk=2
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;JarOfWinds=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
BaseMaterial=Clonk=25;Bread=25;
BaseProduction=Clonk=25;Bread=25;
[Player4]
Wealth=25
Crew=Clonk=2
Knowledge=Flagpole=1;Foundry=1;WindGenerator=1;SteamEngine=1;Compensator=1;Sawmill=1;ChemicalLab=1;Elevator=1;Pump=1;ToolsWorkshop=1;Basement=1;WallKit=1;GoldBar=1;Loam=1;Metal=1;Axe=1;Barrel=1;Bucket=1;Dynamite=1;Hammer=1;JarOfWinds=1;Pickaxe=1;Pipe=1;Shovel=1;TeleGlove=1;DynamiteBox=1;Lorry=1;
BaseMaterial=Clonk=25;Bread=25;
BaseProduction=Clonk=25;Bread=25;
[Landscape]
Sky=Clouds2
TopOpen=1
BottomOpen=0
AutoScanSideOpen=1
MapWidth=300
MapHeight=400
MapZoom=7
[Weather]
Climate=0
StartSeason=0
YearSpeed=0
Wind=0,100,-100,100

View File

@ -1,300 +0,0 @@
/**
Deep Sea Mining
Mine gems buried deeply below the ocean.
@authors Sven2
*/
// set in Map.c
static main_island_x, main_island_y;
static goal_platform_x, goal_platform_y;
static const SCEN_TEST = true;
static g_is_initialized, g_is_in_intro, g_intro_done, npc_tuesday, g_tuesday_pos;
protected func PostIntroInitialize()
{
// Construction site on goal platform
var goal_site = CreateObjectAbove(ConstructionSite, goal_platform_x+10, goal_platform_y+3);
goal_site->Set(CrystalCommunicator);
goal_site->MakeUncancellable();
if (SCEN_TEST)
{
for (var i=0; i<6; ++i)
{
goal_site->CreateObjectAbove(Metal,-20);
goal_site->CreateObjectAbove(Ruby,0);
goal_site->CreateObjectAbove(Amethyst,20);
}
goal_site->CreateContents(Metal,6);
goal_site->CreateContents(Ruby,6);
goal_site->CreateContents(Amethyst,5);
}
// Initialize different parts of the scenario.
InitEnvironment();
InitVegetation();
InitAnimals();
InitMainIsland();
// NPC
g_tuesday_pos = FindMainIslandPosition(0, 100, true);
npc_tuesday = CreateObjectAbove(Clonk, g_tuesday_pos[0]+20, g_tuesday_pos[1]-10);
npc_tuesday->SetDir(DIR_Left);
npc_tuesday->SetColor(0x804000);
npc_tuesday->SetName("$Tuesday$");
return true;
}
func DoInit(int first_player)
{
if (!SCEN_TEST)
StartSequence("Intro", 0, GetCrew(first_player));
else
{
PostIntroInitialize();
g_intro_done = true;
}
return true;
}
protected func InitializePlayer(int plr)
{
// intro has its own initialization
if (g_is_in_intro) return true;
// Harsh zoom range
SetPlayerZoomByViewRange(plr, 500, 350, PLRZOOM_LimitMax);
SetPlayerZoomByViewRange(plr, 500, 350, PLRZOOM_Direct);
SetPlayerViewLock(plr, true);
// Intro
if (!g_is_initialized) g_is_initialized = DoInit(plr);
if (!g_intro_done) return true;
// Position and materials
var i, crew;
for (i = 0; crew = GetCrew(plr, i); ++i)
{
var pos = FindMainIslandPosition();
crew->SetPosition(pos[0], pos[1] - 11);
crew->CreateContents(Shovel);
if (SCEN_TEST)
{
var cs = FindObject(Find_ID(ConstructionSite));
crew->SetPosition(cs->GetX(), cs->GetY()-20);
}
}
// Claim ownership of unowned structures
for (var structure in FindObjects(Find_Or(Find_Category(C4D_Structure), Find_Func("IsFlagpole")), Find_Owner(NO_OWNER)))
structure->SetOwner(plr);
return;
}
// Initializes environment and disasters.
private func InitEnvironment()
{
// Water refill from sides
var initial_water_level = 0;
while (GetMaterial(0,initial_water_level) != Material("Water")) ++initial_water_level;
ScheduleCall(nil, this.EnsureWaterLevel, 20, 999999999, initial_water_level);
// Set a certain parallax.
SetSkyParallax(0, 20, 20);
// Ambience sounds
CreateObjectAbove(Ambience);
// No disasters for now
//Meteor->SetChance(5); Cloud->SetLightning(16);
return;
}
// Ensures that the sea doesn't disappear
func EnsureWaterLevel(int level, bool no_recursion)
{
var water_mat = Material("Water");
if (GetMaterial(0,level) != water_mat) CastPXS("Water", 100, 20, 0,level, 90, 10);
if (GetMaterial(LandscapeWidth()-1,level) != water_mat) CastPXS("Water", 100, 20, LandscapeWidth()-1,level, 270, 10);
// Extra insertion at a lower level so it's not easy to block off
if (!no_recursion && !Random(3)) EnsureWaterLevel(level + 50 + Random(450), true);
return true;
}
private func InitVegetation()
{
// Grass on starting island.
PlaceGrass(85);
// Place some cocont trees all around the main island
for (var i = 0; i < 10 + Random(8); i++)
PlaceVegetation(Tree_Coconut, 0, 0, LandscapeWidth(), LandscapeHeight(), 1000 * (61 + Random(40)));
// Create an effect to make sure there will always grow some new trees.
ScheduleCall(nil, this.EnsureTrees, 100, 999999999);
// Some objects in the earth.
PlaceObjects(Rock, 10 + Random(10),"Earth");
PlaceObjects(Firestone, 35 + Random(5), "Earth");
PlaceObjects(Loam, 25 + Random(5), "Earth");
// Underwater vegetation
Seaweed->Place(20);
Coral->Place(30);
return;
}
// Ensures that there will always grow some trees on the main island.
func EnsureTrees()
{
var wdt = LandscapeWidth();
var hgt = LandscapeHeight();
// Place a tree if there are less than eight trees, with increasing likelihood for lower amounts of trees.
var nr_trees = ObjectCount(Find_Func("IsTree"), Find_Func("IsStanding"));
if (Random(9) >= nr_trees)
if (!Random(20))
PlaceVegetation(Tree_Coconut, main_island_x - 300, main_island_y - 200, 600, 400, 3);
return true;
}
private func InitAnimals()
{
// Place fish in upper ocean area (there tend to be small basins below, where lots of fish could get stuck)
var fish_area = GetFishArea();
Fish->Place(50, fish_area);
Piranha->Place(25, fish_area);
ScheduleCall(nil, this.EnsureAnimals, 237, 999999999);
return true;
}
private func GetFishArea() { return Shape->Rectangle(50, main_island_y, LandscapeWidth() - 100, LandscapeHeight()/2 - main_island_y); }
private func EnsureAnimals()
{
if (ObjectCount(Find_ID(Fish), Find_Not(Find_Action("Dead"))) < 50) DoFishSpawn(Fish);
if (ObjectCount(Find_ID(Piranha), Find_Not(Find_Action("Dead"))) < 25) DoFishSpawn(Piranha);
return true;
}
private func DoFishSpawn(fish_type)
{
// Try placement away from Clonks. If a Clonk was nearby, just remove it immediately.
var fish = fish_type->Place(1, GetFishArea());
if (fish)
if (fish->FindObject(fish->Find_Distance(300), Find_ID(Clonk), Find_OCF(OCF_Alive)))
fish->RemoveObject();
return fish;
}
// Initializes the main island according to the material specification.
private func InitMainIsland()
{
var amount = 3;
var pos;
// Always start with a lorry filled with: hammer(x2), axe(x2), wood(x6) and metal(x4).
var lorry_pos = FindMainIslandPosition(0, 80);
var lorry = CreateObjectAbove(Lorry, lorry_pos[0], lorry_pos[1] - 8);
lorry->CreateContents(Hammer, 2);
lorry->CreateContents(Axe, 2);
lorry->CreateContents(Wood, 6);
lorry->CreateContents(Metal, 4);
// If more material is specified, create a small settlement: flag(x2) and windmill.
// Also fill lorry a bit more with: pickaxe(x1), dynamite(x4), wood(x4), metal(x2).
if (amount >= 2)
{
pos = FindMainIslandPosition(-120, 20);
CreateObjectAbove(Flagpole, pos[0]-7, pos[1]);
var rfp = CreateObjectAbove(Flagpole, pos[0]+7, pos[1]);
rfp->SetNeutral(true);
pos = FindMainIslandPosition(120, 20);
CreateObjectAbove(Flagpole, pos[0], pos[1]);
pos = FindMainIslandPosition(nil, nil, true);
CreateObjectAbove(WindGenerator, pos[0], pos[1]);
lorry->CreateContents(Wood, 4);
lorry->CreateContents(Metal, 2);
lorry->CreateContents(Pickaxe, 1);
lorry->CreateContents(Dynamite, 4);
}
// If still more material is specified, create a larger settlement: sawmill, chemical lab, tools workshop.
// Also fill lorry a bit more with: Barrel (x1), Bucket(x1), Loam(x4), DynamiteBox(x2).
if (amount >= 3)
{
pos = FindMainIslandPosition(nil, nil, true);
CreateObjectAbove(Sawmill, pos[0], pos[1]);
pos = FindMainIslandPosition(nil, nil, true);
CreateObjectAbove(ChemicalLab, pos[0], pos[1]);
pos = FindMainIslandPosition(nil, nil, true);
CreateObjectAbove(ToolsWorkshop, pos[0], pos[1]);
lorry->CreateContents(Barrel, 1);
lorry->CreateContents(Bucket, 1);
lorry->CreateContents(Loam, 4);
lorry->CreateContents(DynamiteBox, 1);
lorry->CreateContents(WallKit, 4);
//lorry->CreateContents(Boompack, 1);
}
return;
}
// Tries to find a position on the main island.
private func FindMainIslandPosition(int xpos, int sep, bool no_struct)
{
if (xpos == nil)
xpos = 0;
if (sep == nil)
sep = 250;
for (var i = 0; i < 100; i++)
{
var x = main_island_x + xpos + Random(sep*2+1)-sep;
var y = main_island_y / 2 - 220;
while (!GBackSolid(x, y) && y < LandscapeHeight()*3/4)
y++;
if (GetMaterial(x,y) == Material("Brick")) continue; // not on goal platform
if (!no_struct || !FindObject(Find_Or(Find_Category(C4D_Structure), Find_Func("IsFlagpole"), Find_ID(WindGenerator)), Find_Distance(60, x, y)))
break;
}
return [x, y];
}
/* Outro */
// Goal fulfilled
public func OnGoalsFulfilled()
{
SetNextMission("Missions.ocf/TreasureHunt.ocs");
GainScenarioAchievement("Done");
GainMissionAccess("S2Sea");
StartSequence("Outro", 0);
// Return true to force goal rule to not call GameOver() yet
return true;
}
// Intro helper
global func Particles_Smoke(...)
{
var p = inherited(...);
if (g_intro_sky_moving)
{
p.ForceX = -300;
p.DampingX = 800;
}
return p;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,5 +0,0 @@
[Landscape]
BottomOpen=0
TopOpen=1
MapZoom=20,0,5,20
Sky=Clouds2

View File

@ -1 +0,0 @@
Tuesday=Dienstag

View File

@ -1 +0,0 @@
Tuesday=Tuesday

View File

@ -1,48 +0,0 @@
// NPC Tuesday: Sits on the island and does nothing
// (except giving some hints)
#appendto Dialogue
func Dlg_Tuesday_1(object clonk)
{
MessageBox("$Tuesday1$", clonk, dlg_target);
return true;
}
func Dlg_Tuesday_2(object clonk)
{
var options = [["$TuesdayQCommunicator$", "Dlg_Tuesday_Communicator"], ["$TuesdayQGems$", "Dlg_Tuesday_Gems"], ["$TuesdayQFish$", "Dlg_Tuesday_Fish"], ["$TuesdayQWater$", "Dlg_Tuesday_Water"], ["$TuesdayQBye$", "StopDialogue()"]];
MessageBox("", clonk, clonk, nil, false, options);
SetDialogueProgress(1);
return true;
}
func Dlg_Tuesday_Communicator(object clonk, q)
{
SetDialogueProgress(2);
return MessageBox("$TuesdayACommunicator$", clonk);
}
func Dlg_Tuesday_Gems(object clonk, q)
{
SetDialogueProgress(2);
return MessageBox("$TuesdayAGems$", clonk);
}
func Dlg_Tuesday_Fish(object clonk, q)
{
SetDialogueProgress(2);
return MessageBox("$TuesdayAFish$", clonk);
}
func Dlg_Tuesday_Water(object clonk, q)
{
SetDialogueProgress(10);
return MessageBox("$TuesdayAWater$", clonk);
}
func Dlg_Tuesday_10(object clonk)
{
SetDialogueProgress(2);
return MessageBox("$TuesdayAWater2$", clonk);
}

View File

@ -1,2 +0,0 @@
#appendto Ruby
func IsValuable(){}

View File

@ -1,271 +0,0 @@
#appendto Sequence
static g_intro_sky_moving;
static npc_tuesday;
func Intro_Start()
{
// Intro starts high up in the clouds
LoadScenarioSection("Intro");
SetPlayList("skyisland", NO_OWNER, true);
SetWind(-100);
SetSkyParallax(0, 20, 20, -10, 0);
this.plane = CreateObjectAbove(Plane, 500, 200);
this.plane->SetColor(0xa04000);
this.pilot = CreateObjectAbove(Clonk, 100, 100, NO_OWNER);
this.pilot->MakeInvincible();
this.pilot->MakeNonFlammable();
this.pilot->SetSkin(2);
this.pilot->Enter(this.plane);
this.pilot->SetAction("Walk");
this.pilot->SetName("Pyrit");
this.pilot->SetColor(0xff0000);
this.pilot->SetDir(DIR_Left);
this.pilot->SetObjectLayer(this.pilot);
this.plane.FxIntPlaneTimer = this.Intro_PlaneTimer;
RemoveEffect("IntPlane", this.plane);
AddEffect("IntPlane",this.plane,1,1,this.plane);
this.plane->FaceRight();
this.plane->StartInstantFlight(90, 0);
g_intro_sky_moving = true;
SetViewTarget(this.plane);
return ScheduleNext(100, 1);
}
func Intro_PlaneTimer(...)
{
// Plane flight overload: Just move sky and have plane do turbulent movement during initial part of intro
var rv = Call(Plane.FxIntPlaneTimer, ...);
if (g_intro_sky_moving)
{
if (!Random(4)) this.rdir = BoundBy((80+Random(21)-GetR())/5,-1,1);
SetXDir(); SetYDir(GetR()*2-GetY()+Random(5),10);
}
return rv;
}
func Intro_JoinPlayer(int plr)
{
if (g_intro_done) return false; // too late for join - just join on island
for(var index = 0, crew; crew = GetCrew(plr, index); ++index) crew->Enter(this);
return true;
}
func Intro_1()
{
MessageBoxAll("$Intro1$", GetHero(), true); // y clouds?
return ScheduleNext(200);
}
func Intro_2()
{
MessageBoxAll("$Intro2$", this.pilot, true); // cuz u told me
return ScheduleNext(300);
}
func Intro_3()
{
MessageBoxAll("$Intro3$", GetHero(), true); // 2 turbulent
return ScheduleNext(200);
}
func Intro_4()
{
MessageBoxAll("$Intro4$", this.pilot, true); // cuz condensation
return ScheduleNext(380);
}
func Intro_5()
{
MessageBoxAll("$Intro5$", GetHero(), true); // go lower now
return ScheduleNext(300);
}
func Intro_6()
{
MessageBoxAll("$Intro6$", this.pilot, true); // fuk u
return ScheduleNext(450);
}
func Intro_7()
{
MessageBoxAll("$Intro7$", this.pilot, true); // u fly
return ScheduleNext(200);
}
func Intro_8()
{
MessageBoxAll("$Intro8$", GetHero(), true); // ...
return ScheduleNext(100);
}
func Intro_9()
{
MessageBoxAll("$Intro9$", GetHero(), true); // ok
return ScheduleNext(150);
}
func Intro_10()
{
g_intro_sky_moving = false;
this.plane.rdir = 0;
this.plane->StartInstantFlight(this.plane->GetR(), 15);
MessageBoxAll("$Intro10$", GetHero(), true); // aaaah
for (var i=0,plr; i<GetPlayerCount(C4PT_User); ++i)
{
plr = GetPlayerByIndex(i, C4PT_User);
for(var index = 0, crew; crew = GetCrew(plr, index); ++index)
{
crew->Exit();
crew->SetPosition(this.plane->GetX()+10, this.plane->GetY());
crew->SetAction("Tumble");
if (!index) SetPlrView(plr, crew);
}
}
SetViewTarget();
return ScheduleNext(200, 11);
}
func Intro_11()
{
g_intro_done = true;
LoadScenarioSection("main");
SetWind(0);
SetSkyParallax(0, 20, 20, 0, 0);
GameCall("PostIntroInitialize");
for (var i=0,plr; i<GetPlayerCount(C4PT_User); ++i)
{
plr = GetPlayerByIndex(i, C4PT_User);
GameCall("InitializePlayer", plr);
for(var index = 0, crew; crew = GetCrew(plr, index); ++index)
{
crew->SetPosition(g_tuesday_pos[0],-100);
crew->SetXDir(-10); crew->SetYDir(-30);
if (!index) SetPlrView(plr, crew);
}
}
return ScheduleNext(200, 20);
}
func Intro_20()
{
MessageBoxAll("$Intro20$", GetHero(), true); // ouch
for (var i=0,plr; i<GetPlayerCount(C4PT_User); ++i)
{
plr = GetPlayerByIndex(i, C4PT_User);
for(var index = 0, crew; crew = GetCrew(plr, index); ++index)
{
crew->SetCommand("MoveTo", nil, g_tuesday_pos[0]-15+Random(20), g_tuesday_pos[1]);
}
}
return ScheduleNext(300);
}
func Intro_21()
{
Dialogue->SetSpeakerDirs(npc_tuesday, GetHero());
MessageBoxAll("$Intro21$", npc_tuesday, true); // hi friend
return ScheduleNext(300);
}
func Intro_22()
{
MessageBoxAll("$Intro22$", GetHero(), true); // where is plane?
return ScheduleNext(300);
}
func Intro_23()
{
MessageBoxAll("$Intro23$", npc_tuesday, true); // 2 cloudy 4 plane
return ScheduleNext(350);
}
func Intro_24()
{
MessageBoxAll("$Intro24$", GetHero(), true); // but i need plane
return ScheduleNext(250);
}
func Intro_25()
{
MessageBoxAll("$Intro25$", npc_tuesday, true); // u stay here
return ScheduleNext(300);
}
func Intro_26()
{
MessageBoxAll("$Intro26$", GetHero(), true); // make fire?
return ScheduleNext(300);
}
func Intro_27()
{
MessageBoxAll("$Intro27$", npc_tuesday, true); // fire sux
return ScheduleNext(400);
}
func Intro_28()
{
MessageBoxAll("$Intro28$", npc_tuesday, true); // use communicator
return ScheduleNext(300);
}
func Intro_29()
{
MessageBoxAll("$Intro29$", GetHero(), true); // ok. where?
return ScheduleNext(250);
}
func Intro_30()
{
MessageBoxAll("$Intro30$", npc_tuesday, true); // not built yet
return ScheduleNext(450);
}
func Intro_31()
{
MessageBoxAll("$Intro31$", npc_tuesday, true); // go east and finish it with metal+gems
return ScheduleNext(400);
}
func Intro_32()
{
MessageBoxAll("$Intro32$", GetHero(), true); // where gems?
return ScheduleNext(250);
}
func Intro_33()
{
MessageBoxAll("$Intro33$", npc_tuesday, true); // fish say gems in sea
return ScheduleNext(400);
}
func Intro_34()
{
MessageBoxAll("$Intro34$", GetHero(), true); // fish talk?
return ScheduleNext(150);
}
func Intro_35()
{
MessageBoxAll("$Intro35$", npc_tuesday, true); // coconut talk!
return ScheduleNext(200);
}
func Intro_36()
{
MessageBoxAll("$Intro36$", GetHero(), true); // ok...
return ScheduleNext(150);
}
func Intro_37()
{
npc_tuesday->SetDialogue("Tuesday", true);
return Stop();
}

View File

@ -1,106 +0,0 @@
#appendto Sequence
func Outro_Start()
{
this.communicator = FindObject(Find_Func("IsCrystalCommunicator"));
if (!this.communicator) return false; // what?
this.hero = this.communicator->FindObject(Find_ID(Clonk), Find_OCF(OCF_Alive), this.communicator->Sort_Distance());
SetViewTarget(this.communicator);
// Outro
ScheduleCall(nil, this.Outro_Fade2Darkness, 15, 32, {});
Dialogue->MessageBoxAll("$Outro1$", this.hero, true); // ok turn it on
return ScheduleNext(100);
}
func Outro_1()
{
this.communicator->StartCommunication(); // 250 frames
return ScheduleNext(650);
}
func Outro_2()
{
Dialogue->MessageBoxAll("$Outro2$", this.hero, true); // let's see if it works
return ScheduleNext(50);
}
func Outro_3()
{
this.communicator->SendCode("...---..."); // 159 frames
return ScheduleNext(200);
}
func Outro_4()
{
this.communicator->StopCommunication();
MessageBoxAll("$Outro3$", this.hero, true); // i wonder if anyone has heard us
this.plane = CreateObjectAbove(Plane, 100, main_island_y-100);
this.plane->SetContactDensity(85); // only collision with brick for proper landing
this.pilot = CreateObjectAbove(Clonk, 100, 100);
this.pilot->MakeInvincible();
this.pilot->SetSkin(2);
this.pilot->Enter(this.plane);
this.pilot->SetAction("Walk");
this.pilot->SetName("Pyrit");
this.pilot->SetColor(0xff0000);
this.pilot->SetDir(DIR_Right);
this.plane->FaceRight();
this.plane->StartInstantFlight(90, 15);
return ScheduleNext(5);
}
func Outro_5()
{
// Wait for plane to arrive
if (this.plane->GetX() < this.communicator->GetX() - 200) return ScheduleSame(5);
// Plane in range! Ensure players see it.
SetPlayerZoomByViewRange(NO_OWNER, 500, 350, PLRZOOM_Direct | PLRZOOM_LimitMax);
MessageBoxAll("$Outro4$", this.pilot, true); // hey, our friends!
return ScheduleNext(100);
}
func Outro_6()
{
MessageBoxAll("$Outro5$", this.hero, true); // we're saved!
this.plane->StartInstantFlight(245, 15);
this.plane->SetContactDensity(C4M_Solid);
return ScheduleNext(60);
}
func Outro_7()
{
this.plane->StartInstantFlight(280, 5);
return ScheduleNext(15);
}
func Outro_8()
{
this.plane->CancelFlight();
return ScheduleNext(40);
}
func Outro_9()
{
this.pilot->Exit();
MessageBoxAll("$Outro6$", this.pilot, true); // hop on everyone!
return ScheduleNext(100);
}
func Outro_10()
{
this.plane->FaceRight();
return ScheduleNext(100);
}
func Outro_11()
{
Sound("Fanfare");
return GameOver();
}
func Outro_Fade2Darkness(proplist v)
{
v.t += 8;
var fade_val = Max(0xff-v.t);
SetSkyAdjust(RGB(fade_val,fade_val,fade_val));
}

View File

@ -1,48 +0,0 @@
Intro1=Pyrit, warum müssen wir in den Wolken fliegen?
Intro2=Du sagtest, wir sollen höher fliegen, nachdem wir fast im Vulkan gelandet waren.
Intro3=Es ist schrecklich turbulent hier.
Intro4=Das liegt daran, dass die Kondensation des Wassers in den Wolken Wärme freisetzt, die die Luft weiter aufsteigen lässt.
Intro5=Danke für den Vortrag. Können wir nun bitte tiefer fliegen?
Intro6=Erst ist es zu turbulent wenn ich tief fliege. Nun ist es zu turbulent wenn ich hoch fliege.
Intro7=Warum steuerst du das Flugzeug nicht einfach selber?
Intro8=...
Intro9=Okay. Wir tauschen Plätze!
Intro10=Aaaah
Intro20=Autsch.
Intro21=Oh. Ein Freund ist vom Himmel gefallen. Hallo Freund! Mein Name ist Dienstag.
Intro22=Verdammt. Hast du unser Flugzeug gesehen? Ich bin versehentlich raus gefallen.
Intro23=Der Himmel ist hier stets bewölkt. Wir sehen kein Flugzeug und kein Flugzeug sieht uns.
Intro24=Ugh. Aber Pyrit muss uns irgendwie finden.
Intro25=Was auf der Insel strandet bleibt auf der Insel. Genau wie ich.
Intro26=Könnten wir nicht ein Feuer machen oder so?
Intro27=Feuer? Das wird nicht helfen. Kokosnussholz brennt schlecht und das Feuer würde durch die Wolken eh keiner sehen.
Intro28=Aber...wir könnten meinen <c ffff00>Kristallkommunikator</c> benutzen!
Intro29=Klingt gut. Wo steht der?
Intro30=Ich bin glücklich auf meiner Insel. Warum sollte ich ein Kommunikationsgerät bauen? Ich habe die Pläne fertig, aber mich nie bemüht es auch zu bauen.
Intro31=Die Baustelle und die Pläne befinden sich am Östlichen Ende dieser Insel. Du brauchst nur etwas Metall und ein paar Edelsteine.
Intro32=Metall klingt einfach. Aber wo finde ich Edelsteine?
Intro33=Die Fische sprechen manchmal über glitzernde Edelsteine tief unten im Meer. Vielleicht kannst du die abbauen?
Intro34=Fische sprechen mit dir?
Intro35=Wenn du nur zuhörst, sprechen sogar die Kokosnüsse!
Intro36=Okay...ich schaue mal nach der Baustelle.
Tuesday1=Hallo, Freund. Wie kann ich dir helfen?
TuesdayQCommunicator=Wie benutze ich den Kommunikator?
TuesdayACommunicator=Bringe einfach das nötige Material zur Baustelle. Es wird automatisch funktionieren, wenn du es fertig stellst.
TuesdayQGems=Wo finde ich Edelsteine?
TuesdayAGems=Tauche ins Meer hinein. Die größten Schätze lagern tief unten und sind schwer erreichbar.
TuesdayQFish=Kannst du die Fische weg schicken?
TuesdayAFish=Ich bin nur ein Zuhörer. Ich belaste die freien Geschöpfe dieser Welt nicht mit unnötigen Verboten.
TuesdayQWater=Das wasser ist zu Tief. Wie komme ich an die Edelsteine?
TuesdayAWater=Du könntest probieren, Wandbausätze{{WallKit}} in der Werkzeughütte{{ToolsWorkshop}} zu bauen. Wandbausätze können benutzt werden, um stabile Wände auch unter Wasser zu errichten. Diese schützen dich vor den Fischen und schaffen Raum zum Atmen.
TuesdayAWater2=Mit Wandbausätzen und einer Pumpe solltest du die Edelsteine erreichen können.
TuesdayQBye=Tschüss.
Outro1=Die Maschine ist fertig. Ob sie wohl funktioniert?
Outro2=Das sieht gut aus. Ich sende ein Notsignal...
Outro3=Hoffentlich hat uns jemand gehört.
Outro4=Hey, sind das nicht unsere gestrandeten Freunde?
Outro5=Hurra, wir sind gerettet!
Outro6=Alles einsteigen! Wir fliegen zum Goldenen Berg.

View File

@ -1,47 +0,0 @@
Intro1=Pyrit, why do we have to fly in the clouds?
Intro2=You told me to fly higher after we almost crashed into the volcano.
Intro3=It's horribly turbulent in here.
Intro4=That's because water condensation in the cloud frees up energy which heats up the air.
Intro5=Thanks for the lecture. Now can we please fly lower again?
Intro6=First it's too turbulent when i fly low over the volcano and now it's too turbulent when i fly up high in the clouds.
Intro7=Why don't you just steer the plane yourself?
Intro8=...
Intro9=Alright. Let's swap places.
Intro10=Aaaah
Intro20=Ouch.
Intro21=Oh, a friend has fallen from the sky. Hello friend! My name is Tuesday.
Intro22=Damn. Have you seen our airplane? I accidentally dropped out of it.
Intro23=The sky is always cloudy here. We won't see any planes and planes won't see us.
Intro24=Ugh. But i need Pyrit to find me somehow.
Intro25=What lands on the island stays on the island. Just like me.
Intro26=Isn't there any way we could make a fire or something?
Intro27=Fire? That will do no good. Coconut wood doesn't burn well and it won't be seen through the clouds.
Intro28=But...we could use my <c ffff00>crystal communicator</c>!
Intro29=Sounds good. Where is it?
Intro30=I'm happy on this island. Why should i build a communication device? I've made plans but I haven't bothered to finish building it.
Intro31=The site and plans how to finish it are at the eastern end of this island. It just needs some metal and some gems.
Intro32=Metal sounds easy enough? Where can I find the gems?
Intro33=The fish sometimes talk about gem reserves deep below the ocean ground. Maybe you can mine those?
Intro34=Fish talk to you?
Intro35=If you listen carefully, even coconuts talk!
Intro36=Okay...i'll see if i can finish this construction.
Tuesday1=Hello, friend. How can I help you?
TuesdayQCommunicator=How do I use the communicator?
TuesdayACommunicator=Just bring the necessery materials to the construction site. It should work automatically when completed.
TuesdayQGems=Where can I find gems?
TuesdayAGems=Try to dive underground.
TuesdayQFish=Can you tell the fish to go away?
TuesdayAFish=I'm a listener. I do not force restraints onto the free creatures of this world.
TuesdayQWater=The water is too deep. How can I reach the gems?
TuesdayAWater=You could try and build wall kits{{WallKit}} in the tools workshop{{ToolsWorkshop}}. Wall kits can be used to construct stable walls under water protect you from fish and give some room to breath.
TuesdayAWater2=Using wall kits and a pump, you might be able to solve your problems.
TuesdayQBye=Bye
Outro1=The machine is done! I wonder whether it works.
Outro2=Looks good. Let's send a rescue signal...
Outro3=I wonder if anyone has heard us.
Outro4=Hey, isn't that our stranded friends?
Outro5=Hooray, we're saved!
Outro6=Hop on everyone, we're heading for the Golden Mountain!

View File

@ -1,2 +0,0 @@
DE:AmbienceTest: Tiefseemine
US:AmbienceTest: Deep sea mining

View File

@ -2,6 +2,8 @@
func InitializeObjects()
{
CreateObject(Ambience);
CreateObject(Grass, 555, 550);
CreateObjectAbove(Grass, 533, 550);
CreateObjectAbove(Grass, 1306, 706);

View File

@ -33,6 +33,7 @@ protected func PostIntroInitialize()
}
// Initialize different parts of the scenario.
CreateObject(Ambience);
InitEnvironment();
InitVegetation();
InitAnimals();

View File

@ -4,6 +4,8 @@ static g_chemical, g_cabin, g_sawmill, g_workshop, g_windmill, g_flagpole, npc_n
func InitializeObjects()
{
CreateObject(Ambience);
var Rule_BaseRespawn001 = CreateObject(Rule_BaseRespawn);
Rule_BaseRespawn001->SetInventoryTransfer(true);
Rule_BaseRespawn001->SetFreeCrew(true);

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ PlaneNoOil=Das Flugzeug braucht noch Treibstoff. Ich muss irgendwo
Intro1=Autsch. Pyrit!
Intro2=Wie kann der Treibstoff leer sein? Du sagtest doch, ein Fass Öl bringt uns locker an unser Ziel und zurück!
Intro3=Dachte ich auch. Aber da wusste ich noch nicht, dass ich drei Tage über dem Ozean kreisen und auf einer einsamen Insel suchen müsste.
Intro3=Dachte ich auch. Aber da wusste ich noch nicht, dass ich drei Tage über dem Ozean kreisen und nach einer einsamen Insel suchen müsste.
Intro4=Das ist alles deine Schuld. Ich sags ja nur.
Intro5=Was können wir nun tun?
Intro6=Ich schlage vor du suchst neues Öl und ich repariere derweil das Flugzeug.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,8 +1,7 @@
[DefCore]
id=Ambience
Version=6,0
Category=C4D_StaticBack
Category=C4D_StaticBack | C4D_Environment
Width=1
Height=1
Offset=-1,-1

View File

@ -1,5 +1,381 @@
/**
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 player_environments; // array indexed by player number: array of derived environments with per-player data
local all_environments; // array of available environments for which it is checked if the player is in. sorted by priority.
// Initialization
protected func Initialize()
{
// Register default environments (overloadable)
this->InitializeEnvironments();
// Initial player data
player_environments = [];
for (var i=0; i<GetPlayerCount(C4PT_User); ++i)
InitializePlayer(GetPlayerByIndex(i, C4PT_User));
// Periodic execution of ambience events
AddTimer(this.Execute, 10);
return true;
}
func InitializeEnvironments()
{
// Register all standard environments
all_environments = [];
// Underwater: Clonk is swimming in water
var underwater = this.env_underwater = new Environment
{
Name = "Underwater",
CheckPlayer = this.EnvCheck_Underwater,
music = "underwater",
sound_modifier = UnderwaterModifier,
exclusive = true, // Do not player other stuff (such as cave sound) when diving.
};
AddEnvironment(underwater, 1400);
// City: Clonk is surrounded by buildings
var city = this.env_city = new Environment
{
Name = "City",
CheckPlayer = this.EnvCheck_City,
};
//AddEnvironment(city, 1200); - no music/sound for now
// Lava: Lava material is nearby
var lava = this.env_lava = new Environment
{
Name = "Lava",
CheckPlayer = this.EnvCheck_Lava,
min_change_delays = [1, 3], // Easy to miss lava on search.
};
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;
//AddEnvironment(lava, 1000); - no music/sound for now
// Underground: Clonk in front of tunnel
var underground = this.env_underground = new Environment
{
Name = "Underground",
CheckPlayer = this.EnvCheck_Underground,
music = "underground",
sound_modifier = CaveModifier,
};
AddEnvironment(underground, 800);
// Mountains: Overground and lots of rock around
var mountains = this.env_mountains = new Environment
{
Name = "Mountains",
CheckPlayer = this.EnvCheck_Mountains,
min_change_delay = [3, 3], // Pretty unstable condition
};
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;
//AddEnvironment(mountains, 600); - no music/sound for now
// Snow: It's snowing around the clonk
var snow = this.env_snow = new Environment
{
Name = "Snow",
CheckPlayer = this.EnvCheck_Snow,
min_change_delay = [1, 6], // Persist a while after snowing stopped
};
snow.mat = Material("Snow");
//AddEnvironment(snow, 400); - no music/sound for now
// Night: Sunlight blocked by planet
var night = this.env_night = new Environment
{
Name = "Night",
CheckPlayer = this.EnvCheck_Night,
music = "night",
};
AddEnvironment(night, 200);
// Day: Default environment
var day = this.env_day = new Environment
{
Name = "Day",
CheckPlayer = this.EnvCheck_Day,
music = "day",
};
AddEnvironment(day, 0);
return true;
}
private func Execute()
{
// Per-player execution every third timer (~.8 seconds)
var i=GetPlayerCount(C4PT_User), plr;
exec_counter += !(i%3);
while (i--) if (!(++exec_counter % 3))
{
plr = GetPlayerByIndex(i, C4PT_User);
ExecutePlayer(plr, player_environments[plr]);
}
return true;
}
private func ExecutePlayer(int plr, array environments)
{
var cursor = GetCursor(plr);
// Update active state of all player environments
if (cursor)
{
var x = cursor->GetX(), y = cursor->GetY();
for (var env in environments)
{
var was_active = env.is_active;
var is_active = env->CheckPlayer(cursor, x, y, was_active);
if (is_active == was_active)
{
// No change. Reset change delays.
env.change_delay = 0;
}
else
{
// Environment change. The change must persist for a while to become active.
if (++env.change_delay > env.min_change_delays[!is_active])
{
// Waited long enough. Activate or deactivate this environment.
env.is_active = is_active;
//Log("%s environment: %s set to %v", GetPlayerName(plr), env.Name, is_active);
}
}
}
// Sound and music by active environments
var has_music = false, sound_modifier = nil;
for (var env in environments) if (env.is_active)
{
// Music!
if (env.music && !has_music)
{
this->SetPlayList(env.music, plr, true, 3000);
has_music = true;
}
// Sound effects like cave reverb etc.
if (env.sound_modifier && !sound_modifier) sound_modifier = env.sound_modifier;
// Sounds and actions by environment
for (var action in env.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]);
}
}
// Does this stop all other environments?
if (env.is_exclusive) break;
}
// Apply or clear global sound modifier
SetGlobalSoundModifier(sound_modifier);
}
return true;
}
func InitializePlayer(int plr)
{
if (GetPlayerType(plr) == C4PT_User)
{
// Every player keeps a copy of the environment list to maintain delays
// Start with a large change delay to ensure first execution does set a proper environment
var n = GetLength(all_environments);
var envs = CreateArray(n);
for (var i=0; i < n; ++i)
envs[i] = new all_environments[i] { change_delay = 999, is_active = false };
player_environments[plr] = envs;
// 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
player_environments[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.all_environments[GetLength(all_environments)] = new_env;
SortArrayByProperty(this.all_environments, "Priority", true);
return true;
}
private func Env_AddSound(chance, string snd_name)
{
return Env_AddAction(chance ?? 50, Global.Sound, snd_name);
}
private func Env_AddAction(achance, afn, par0, par1, par2, par3, par4)
{
// Make sure to not write into prototype proplist.
if (this.actions == this.Prototype.actions) this.actions = [];
return this.actions[GetLength(this.actions)] = { chance=achance, fn=afn, par=[par0, par1, par2, par3, par4] };
}
/* 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 false;
// For initial change, clonk should also be diving: Check for breath below 95%
// Use > instead of >= to ensure 0-breath-clonks can also get the environment
if (!is_current && cursor->GetBreath() > cursor.MaxBreath*95/100) return false;
return true;
}
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 false;
return true;
}
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 true;
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 true;
}
}
// No lava found
return false;
}
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 false;
if (GetMaterial(x,y-30)<0) return false;
if (GetMaterial(x-10,y-20)<0) return false;
if (GetMaterial(x+10,y-20)<0) return false;
return true;
}
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 false;
return true;
}
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 false;
return true;
}
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 false;
return true;
}
private func EnvCheck_Day(object cursor, int x, int y, bool is_current)
{
// This is the fallback environment
return true;
}
/*-- Proplist --*/
local SoundModifier, CaveModifier, UnderwaterModifier;
func ReleaseSoundModifier() { return ChangeSoundModifier(this, true); }
func UpdateSoundModifier() { return ChangeSoundModifier(this, false); } // OpenAL-Soft implementation does not work for all modifiers
public func Definition(def)
{
// Base environment
def.Environment = {
actions = [],
min_change_delays = [1, 1],
AddSound = def.Env_AddSound,
AddAction = def.Env_AddAction,
};
// Base sound modifier
SoundModifier = {
Release = Ambience.ReleaseSoundModifier,
Update = Ambience.UpdateSoundModifier,
};
// Modifiers for different ambiences
CaveModifier = new SoundModifier {
Type = C4SMT_Reverb,
Reverb_Late_Reverb_Gain = 300,
Reverb_Late_Reverb_Delay = 10,
Reverb_Decay_HFRatio = 800,
};
UnderwaterModifier = nil; // not supported yet
return true;
}
local Environment;
local Name = "$Name$";
local Description = "$Description$";
/**
Ambience Objects
Cares about the placement of purely visual objects and handles sound ambience
The placement uses categories and thus is forward-compatible.
*/
@ -41,7 +417,6 @@ static const Environment_Attributes =
};
// provides a simple interface for creation of environment objects and decoration with standard values
// the objects are placed on a best-effort-basis. That means f.e. objects that rely on water will not be placed when there is no water in the landscape.
global func CreateEnvironmentObjects(
@ -106,23 +481,3 @@ CreateEnvironmentObjects("Temperate", Rectangle(LandscapeWidth()/2, 0, Landscape
p_id->Place(amount_percentage, area);
}
}
/* Sound ambience */
local SoundModifier, CaveModifier;
func ReleaseSoundModifier() { return ChangeSoundModifier(this, true); }
func UpdateSoundModifier() { return ChangeSoundModifier(this, false); } // do not use
func Definition()
{
// Base sound modifier
SoundModifier = {
Release = Ambience.ReleaseSoundModifier,
Update = Ambience.UpdateSoundModifier,
};
// Modifiers for different ambiences
CaveModifier = new SoundModifier {
Type = C4SMT_Reverb,
};
}