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() func InitializeObjects()
{ {
CreateObject(Ambience);
CreateObject(Grass, 555, 550); CreateObject(Grass, 555, 550);
CreateObjectAbove(Grass, 533, 550); CreateObjectAbove(Grass, 533, 550);
CreateObjectAbove(Grass, 1306, 706); CreateObjectAbove(Grass, 1306, 706);

View File

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

View File

@ -4,6 +4,8 @@ static g_chemical, g_cabin, g_sawmill, g_workshop, g_windmill, g_flagpole, npc_n
func InitializeObjects() func InitializeObjects()
{ {
CreateObject(Ambience);
var Rule_BaseRespawn001 = CreateObject(Rule_BaseRespawn); var Rule_BaseRespawn001 = CreateObject(Rule_BaseRespawn);
Rule_BaseRespawn001->SetInventoryTransfer(true); Rule_BaseRespawn001->SetInventoryTransfer(true);
Rule_BaseRespawn001->SetFreeCrew(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! 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! 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. Intro4=Das ist alles deine Schuld. Ich sags ja nur.
Intro5=Was können wir nun tun? Intro5=Was können wir nun tun?
Intro6=Ich schlage vor du suchst neues Öl und ich repariere derweil das Flugzeug. 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] [DefCore]
id=Ambience id=Ambience
Version=6,0 Version=6,0
Category=C4D_StaticBack Category=C4D_StaticBack | C4D_Environment
Width=1 Width=1
Height=1 Height=1
Offset=-1,-1 Offset=-1,-1

View File

@ -1,5 +1,381 @@
/** /**
Ambience 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 Cares about the placement of purely visual objects and handles sound ambience
The placement uses categories and thus is forward-compatible. 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 // 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. // 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( global func CreateEnvironmentObjects(
@ -106,23 +481,3 @@ CreateEnvironmentObjects("Temperate", Rectangle(LandscapeWidth()/2, 0, Landscape
p_id->Place(amount_percentage, area); 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,
};
}