Merge remote-tracking branch '_origin/master' into lights
Conflicts: src/c4group/C4Components.hissue1247
|
@ -1554,12 +1554,11 @@ add_custom_target(setup
|
|||
set(CPACK_PACKAGE_NAME "openclonk")
|
||||
set(CPACK_PACKAGE_VENDOR "${C4PROJECT_URL}")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenClonk - A game mix out of tactic, strategy and Jump & Run")
|
||||
set(CPACK_PACKAGE_VERSION "${C4XVER1}.${C4XVER2}.${C4XVER3}")
|
||||
set(CPACK_PACKAGE_VERSION "${C4XVER1}.${C4XVER2}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${C4XVER1}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${C4XVER2}")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "${C4XVER3}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "openclonk-${C4XVER1}.${C4XVER2}.${C4XVER3}")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "openclonk-src-${C4XVER1}.${C4XVER2}.${C4XVER3}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "openclonk-${C4XVER1}.${C4XVER2}")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "openclonk-src-${C4XVER1}.${C4XVER2}")
|
||||
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
|
||||
# Somebody who uses Debian/Ubuntu should set this
|
||||
#set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libgcc1, libx11, libXrandr, libXpm, libGLEW, libGL, libpng, libSDL-1.2, libSDL_mixer-1.2, gtk2, libjpeg, zlib")
|
||||
|
|
|
@ -11,9 +11,8 @@ SET(C4ENGINENAME "OpenClonk")
|
|||
SET(C4ENGINENICK "openclonk")
|
||||
SET(C4ENGINEID "${C4PROJECT_TLD}.${C4PROJECT_DOMAIN}.${C4ENGINENICK}")
|
||||
|
||||
SET(C4XVER1 5)
|
||||
SET(C4XVER2 5)
|
||||
SET(C4XVER3 1)
|
||||
SET(C4XVER1 6)
|
||||
SET(C4XVER2 0)
|
||||
|
||||
# C4VERSIONBUILDNAME should be witty and somewhat frequently changing
|
||||
# for alpha and beta releases, and meaningful and stable for stable releases.
|
||||
|
@ -46,7 +45,7 @@ SET(C4COPYRIGHT_YEAR ${YEARFIXED})
|
|||
SET(C4ENGINECAPTION "${C4ENGINENAME}{$C4VERSIONBUILDNAME}")
|
||||
set(C4ENGINEINFO "${C4ENGINENAME}")
|
||||
|
||||
set(C4VERSION "${C4XVER1}.${C4XVER2}.${C4XVER3}")
|
||||
set(C4VERSION "${C4XVER1}.${C4XVER2}")
|
||||
|
||||
if(C4XVER4 LESS 10)
|
||||
set(C4VERSION "${C4VERSION} [00${C4XVER4}]")
|
||||
|
|
|
@ -45,6 +45,10 @@
|
|||
<row>
|
||||
<literal_col>AM_DrawBefore</literal_col>
|
||||
<col>Normally the mesh of the object itself is drawn first and afterwards the attached mesh. If this flag is present then the attached mesh is drawn before the object's mesh is drawn. Note that normally this makes no difference since a Z buffer is used to make sure the drawing order is correct. However if one or both of the meshes make use of alpha blending then the drawing order becomes relevant so that a mesh is not hidden behind otherwise (partly) translucent parts of the other mesh.</col>
|
||||
</row>
|
||||
<row>
|
||||
<literal_col>AM_MatchSkeleton</literal_col>
|
||||
<col>Normally the attached mesh uses its own animations. If this flag is set, then the attached mesh uses the animation data of the parent mesh for all bones that have the same name as a bone in the parent mesh skeleton.</col>
|
||||
</row>
|
||||
</table>
|
||||
</desc>
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE funcs
|
||||
SYSTEM '../../../clonk.dtd'>
|
||||
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
|
||||
<funcs>
|
||||
<func>
|
||||
<title>GetPXSCount</title>
|
||||
<category>Landscape</category>
|
||||
<subcat>Material</subcat>
|
||||
<version>5.2 OC</version>
|
||||
<syntax>
|
||||
<rtype>int</rtype>
|
||||
<params>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>material</name>
|
||||
<desc>Index of material. If nil or -1, any material is counted.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>x</name>
|
||||
<desc>Left side of search rectangle. Offset in local calls. Set to nil to search the whole map.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>y</name>
|
||||
<desc>Top side of search rectangle. Offset in local calls.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>wdt</name>
|
||||
<desc>Width of search rectangle. Right border is not included in search.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>wdt</name>
|
||||
<desc>Height of search rectangle. Bottom border is not included in search.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Determines how many loose pixels (PXS) are present of a given material in a given rectangle.</desc>
|
||||
<examples>
|
||||
<example>
|
||||
<code><funclink>if</funclink>(GetPXSCount(<funclink>Material</funclink>("Snow"), -100,-100,200,100) > 5) <funclink>Message</funclink>("It is snowing!");</code>
|
||||
<text>Part of a clonk script: if snow is found around the clonk, he shows a message above his head.</text>
|
||||
</example>
|
||||
</examples>
|
||||
<related>
|
||||
<funclink>Material</funclink>
|
||||
<funclink>CastPXS</funclink>
|
||||
<funclink>InsertMaterial</funclink>
|
||||
<funclink>GetMaterial</funclink>
|
||||
</related>
|
||||
</func>
|
||||
<author>Sven2</author><date>2014-12</date>
|
||||
</funcs>
|
|
@ -22,6 +22,12 @@
|
|||
<desc>If 1, the specified track will be looped forever.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>fadetime_ms</name>
|
||||
<desc>Fading time between any currently playing piece and the newly selected song. Ignored if both are the same. Set to zero to avoid fading.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Plays a music track. The corresponding music file must be available in the active music group. If the loaded scenario contains music files, the scenario file will be the music group. Otherwise, the global file Music.ocg will be used.</desc>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<param>
|
||||
<type>string</type>
|
||||
<name>playlist</name>
|
||||
<desc>List of pieces of music to be played. The individual file names are separated with semicolons (";"). Wildcards are expanded. If the parameter is left out, the standard playlist is restored.</desc>
|
||||
<desc>List of pieces of music to be played. The individual file and category names are separated with semicolons (";"). Wildcards are expanded. If the parameter is left out, the standard playlist is restored.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
|
@ -22,9 +22,22 @@
|
|||
<desc>The playlist is changed only on clients where the player with this player number is local. If left out or NO_OWNER, the playlist is changed for all clients. If the player number is invalid, no playlists are changed.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
<type>bool</type>
|
||||
<name>force_change</name>
|
||||
<desc>If true and the new playlist does not contain the piece currently playing, a new song is selected and played.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>fadetime_ms</name>
|
||||
<desc>Fade time to the new piece if force_change is true and the current song is changed in response to this call. Set to zero to change music instantly.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Sets the play list of pieces of music to be played in random order, if music is activated. The actual number of pieces of music in the playlist is returned, or 0 in network mode.</desc>
|
||||
<remark>Pieces are identified either by matching file name or any matching category. Categories are only available for .ogg files and may be set as a semicolon-delimited list in the comment field. Matching is case-insensitive, but case insensitive matching does not work for extended UTF8 characters.</remark>
|
||||
<remark>SetPlayList does not activate music playback when the player has turned the music off. If the player enables music, the new playlist takes effect.</remark>
|
||||
<remark><code>SetPlayList("*.*")</code> is not identical to <code>SetPlayList()</code>. The former activates all music, the latter only those not starting with "@", the standard behavior.</remark>
|
||||
<related><funclink>Music</funclink></related>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Goal_FlagMeshes
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=30
|
||||
Height=40
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[Head]
|
||||
Icon=13
|
||||
Title=FrozenFortress
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
Difficulty=80
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=FrostboltScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=HardeningScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=WindScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Goal_FlagMeshes
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=30
|
||||
Height=40
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=LifeGem
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=PyreGem
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[Head]
|
||||
Icon=13
|
||||
Title=Hideout
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
Difficulty=90
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=ShieldGem
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=CrystalShield
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack
|
||||
Width=8
|
||||
Height=7
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=SlowGem
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Head]
|
||||
Title=HotIce
|
||||
Version=5,3,2
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
MaxPlayer=20
|
||||
Icon=21
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[DefCore]
|
||||
id=KingWeapons
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Head]
|
||||
Title=MoltenMonarch
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Icon=23
|
||||
MinPlayer=2
|
||||
Difficulty=70
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Head]
|
||||
Title=Overcast
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
Difficulty=50
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=FireballScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=TeleportScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=WindScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Head]
|
||||
Title=Bottom
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
Difficulty=20
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Head]
|
||||
Title=Ruins
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
Difficulty=30
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Grass
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack|C4D_Background
|
||||
Width=12
|
||||
Height=7
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Head]
|
||||
Title=ScorchedGardens
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
Difficulty=40
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Head]
|
||||
Title=Overcast
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
MinPlayer=2
|
||||
Difficulty=60
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=FireballScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=ThunderScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=WindScroll
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=TargetBalloon
|
||||
Version=4,9,8
|
||||
Version=6,0
|
||||
Category=C4D_Vehicle
|
||||
Width=64
|
||||
Height=64
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=BigBoomattack
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
ContactCalls=1
|
||||
Width=60
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Boomattack
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
ContactCalls=1
|
||||
Width=15
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Goal_SaveTheWindmills
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack|C4D_Goal
|
||||
Width=1
|
||||
Height=1
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[Head]
|
||||
Icon=39
|
||||
Title=Windmill
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Difficulty=10
|
||||
|
||||
[Definitions]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Deco_AltMaterials
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack
|
||||
Width=1
|
||||
Height=1
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Hat
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=LotsOfCoins
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack
|
||||
Width=55
|
||||
Height=17
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Bone
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Ruin_ChemicalLab
|
||||
Version=5,4,0,0
|
||||
Version=6,0
|
||||
Category=C4D_Structure
|
||||
Width=50
|
||||
Height=52
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Ruin_Windmill
|
||||
Version=5,4,0,0
|
||||
Version=6,0
|
||||
Category=C4D_Structure
|
||||
Width=80
|
||||
Height=96
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Ruin_WoodenCabin
|
||||
Version=5,4,0,0
|
||||
Version=6,0
|
||||
Category=C4D_Structure
|
||||
Width=94
|
||||
Height=43
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Skull
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=11
|
||||
Height=13
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=EnvPack_ManaAltar
|
||||
Version=4,10,0,0
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=StrawMan
|
||||
Version=5,4,0,0
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack
|
||||
Width=12
|
||||
Height=20
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[DefCore]
|
||||
id=Ambience
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack | C4D_Rule
|
||||
Picture=0,0,128,128
|
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,299 @@
|
|||
/**
|
||||
Ambience
|
||||
Controls sound and music depending on the environment the player is in
|
||||
|
||||
@author Sven2
|
||||
*/
|
||||
|
||||
local exec_counter; // counter to distribute execution of players across frames
|
||||
local last_environment; // array indexed by player number: pointer to environment the player was in last
|
||||
local environments; // array of available environments for which it is checked if the player is in. sorted by priority.
|
||||
|
||||
// Initialization
|
||||
protected func Initialize()
|
||||
{
|
||||
// Base environment
|
||||
Environment = {
|
||||
actions = [],
|
||||
min_change_delay = 1,
|
||||
min_initial_change_delay = 5,
|
||||
AddSound = this.Env_AddSound,
|
||||
AddAction = this.Env_AddAction,
|
||||
SetMusic = this.Env_SetMusic
|
||||
};
|
||||
// Register default environments (overloadable)
|
||||
this->InitializeEnvironments();
|
||||
// Periodic execution of ambience events
|
||||
last_environment = [];
|
||||
AddTimer(this.Execute, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
func InitializeEnvironments()
|
||||
{
|
||||
// Register all standard environments
|
||||
environments = [];
|
||||
// Underwater: Clonk is swimming in water
|
||||
var underwater = this.env_underwater = new Environment {};
|
||||
underwater->SetMusic("underwater");
|
||||
underwater.CheckPlayer = this.EnvCheck_Underwater;
|
||||
AddEnvironment(underwater, 1400);
|
||||
// City: Clonk is surrounded by buildings
|
||||
var city = this.env_city = new Environment {};
|
||||
city->SetMusic("city");
|
||||
city.CheckPlayer = this.EnvCheck_City;
|
||||
AddEnvironment(city, 1200);
|
||||
// Lava: Lava material is nearby
|
||||
var lava = this.env_lava = new Environment {};
|
||||
lava->SetMusic("lava");
|
||||
lava.CheckPlayer = this.EnvCheck_Lava;
|
||||
lava.mat_mask = CreateArray(); // material mask for lava materials. +1 cuz sky.
|
||||
lava.mat_mask[Material("Lava")+1] = true; // loop over materials and check incindiary instead? Whoever introduces the next lava type can do that...
|
||||
lava.mat_mask[Material("DuroLava")+1] = true;
|
||||
lava.min_change_delay = 3; // Easy to miss lava on search.
|
||||
AddEnvironment(lava, 1000);
|
||||
// Underground: Clonk in front of tunnel
|
||||
var underground = this.env_underground = new Environment {};
|
||||
underground->SetMusic("underground");
|
||||
underground.CheckPlayer = this.EnvCheck_Underground;
|
||||
AddEnvironment(underground, 800);
|
||||
// Mountains: Overground and lots of rock around
|
||||
var mountains = this.env_mountains = new Environment {};
|
||||
mountains->SetMusic("mountains");
|
||||
mountains.CheckPlayer = this.EnvCheck_Mountains;
|
||||
mountains.mat_mask = CreateArray(); // material mask for mountain materials. +1 cuz sky.
|
||||
mountains.mat_mask[Material("Rock")+1] = true;
|
||||
mountains.mat_mask[Material("Granite")+1] = true;
|
||||
mountains.mat_mask[Material("Ore")+1] = true;
|
||||
mountains.mat_mask[Material("Gold")+1] = true;
|
||||
mountains.min_change_delay = 3; // Pretty unstable condition
|
||||
AddEnvironment(mountains, 600);
|
||||
// Snow: It's snowing around the clonk
|
||||
var snow = this.env_snow = new Environment {};
|
||||
snow->SetMusic("snow");
|
||||
snow.CheckPlayer = this.EnvCheck_Snow;
|
||||
snow.min_change_delay = 6; // Persist a while after snowing stopped
|
||||
snow.mat = Material("Snow");
|
||||
AddEnvironment(snow, 400);
|
||||
// Night: Sunlight blocked by planet
|
||||
var night = this.env_night = new Environment {};
|
||||
night->SetMusic("night");
|
||||
night.CheckPlayer = this.EnvCheck_Night;
|
||||
AddEnvironment(night, 200);
|
||||
// Overground: Default environment
|
||||
var overground = this.env_overground = new Environment {};
|
||||
overground->SetMusic("overground");
|
||||
overground.CheckPlayer = this.EnvCheck_Overground;
|
||||
overground->AddSound("Ding", 100);
|
||||
AddEnvironment(overground, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private func Execute()
|
||||
{
|
||||
// Per-player execution every third timer (~.8 seconds)
|
||||
var i=GetPlayerCount(C4PT_User);
|
||||
while (i--) if (!(++exec_counter % 3))
|
||||
{
|
||||
ExecutePlayer(GetPlayerByIndex(i, C4PT_User));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private func ExecutePlayer(int plr)
|
||||
{
|
||||
var cursor = GetCursor(plr);
|
||||
// Determine environment the player is currently in
|
||||
var environment = nil;
|
||||
if (cursor)
|
||||
{
|
||||
var last_env = last_environment[plr];
|
||||
var x = cursor->GetX(), y = cursor->GetY();
|
||||
for (test_environment in environments)
|
||||
{
|
||||
if (environment = test_environment->CheckPlayer(cursor, x, y, test_environment == last_env))
|
||||
{
|
||||
// We've found a matchign environment.
|
||||
// Was it a change? Then check delays first
|
||||
if (test_environment != last_env)
|
||||
{
|
||||
if (last_env && last_env.no_change_delay)
|
||||
{
|
||||
// Environment should change but a delay is specified. Keep last environment for now.
|
||||
--last_env.no_change_delay;
|
||||
environment = last_env;
|
||||
break;
|
||||
}
|
||||
// New environment and change delay has passed.
|
||||
environment.no_change_delay = environment.min_initial_change_delay;
|
||||
Log("%s environment: %s", GetPlayerName(plr), environment.music);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Was no change: Reset change delays
|
||||
environment.no_change_delay = Max(environment.no_change_delay, environment.min_change_delay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
last_environment[plr] = environment;
|
||||
if (!environment) return true;
|
||||
// Music by environment
|
||||
this->SetPlayList(environment.music, plr, true, 3000);
|
||||
// Sounds and actions by environment
|
||||
for (var action in environment.actions)
|
||||
if (Random(1000) < action.chance)
|
||||
cursor->Call(action.fn, action.par[0], action.par[1], action.par[2], action.par[3], action.par[4]);
|
||||
return true;
|
||||
}
|
||||
|
||||
func InitializePlayer(int plr)
|
||||
{
|
||||
// Newly joining players should have set playlist immediately (so they don't start playing a random song just to switch it immediately)
|
||||
ExecutePlayer(plr);
|
||||
return true;
|
||||
}
|
||||
|
||||
func RemovePlayer(int plr)
|
||||
{
|
||||
// Ensure newly joining players don't check on another player's environment
|
||||
last_environment[plr] = nil;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected func Activate(int byplr)
|
||||
{
|
||||
MessageWindow(this.Description, byplr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Environment functions */
|
||||
|
||||
func AddEnvironment(proplist new_env, priority)
|
||||
{
|
||||
if (GetType(priority)) new_env.Priority = priority;
|
||||
this.environments[GetLength(environments)] = new_env;
|
||||
SortArrayByProperty(this.environments, "Priority", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private func Env_AddSound(string snd_name, chance)
|
||||
{
|
||||
return Env_AddAction(Global.Sound, snd_name, chance ?? 50);
|
||||
}
|
||||
|
||||
private func Env_AddAction(afn, par0, par1, par2, par3, par4)
|
||||
{
|
||||
return this.actions[GetLength(this.actions)] = { fn=afn, par=[par0, par1, par2, par3, par4] };
|
||||
}
|
||||
|
||||
private func Env_SetMusic(string playlist)
|
||||
{
|
||||
this.music = playlist;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Default environment checks */
|
||||
|
||||
private func EnvCheck_Underwater(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Clonk should be swimming
|
||||
if (cursor->GetProcedure() != "SWIM") return nil;
|
||||
// For initial change, clonk should also be diving: Check for breath below 80%
|
||||
// Use > instead of >= to ensure 0-breath-clonks can also get the environment
|
||||
if (!is_current && cursor->GetBreath() > cursor.MaxBreath*4/5) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_City(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// There must be buildings around the clonk
|
||||
var building_count = cursor->ObjectCount(cursor->Find_AtRect(-180,-100,360,200), Find_Func("IsStructure"));
|
||||
// 3 buildings to start the environment. Just 1 building to sustain it.
|
||||
if (building_count < 3-2*is_current) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Lava(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for lava pixels. First check if the last lava pixel we found is still in place.
|
||||
var search_range;
|
||||
if (is_current)
|
||||
{
|
||||
if (this.mat_mask[GetMaterial(this.last_x, this.last_y)+1])
|
||||
if (Distance(this.last_x, this.last_y, x, y) < 140)
|
||||
return this;
|
||||
search_range = 140;
|
||||
}
|
||||
else
|
||||
{
|
||||
search_range = 70;
|
||||
}
|
||||
// Now search for lava in search range
|
||||
var ang = Random(360);
|
||||
for (; search_range >= 0; search_range -= 10)
|
||||
{
|
||||
ang += 200;
|
||||
var x2 = x + Sin(ang, search_range);
|
||||
var y2 = y + Cos(ang, search_range);
|
||||
if (this.mat_mask[GetMaterial(x2, y2)+1])
|
||||
{
|
||||
// Lava found!
|
||||
this.last_x = x2;
|
||||
this.last_y = y2;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
// No lava found
|
||||
return nil;
|
||||
}
|
||||
|
||||
private func EnvCheck_Underground(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for underground: No sky at cursor or above
|
||||
if (GetMaterial(x,y)<0) return nil;
|
||||
if (GetMaterial(x,y-30)<0) return nil;
|
||||
if (GetMaterial(x-10,y-20)<0) return nil;
|
||||
if (GetMaterial(x+10,y-20)<0) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Mountains(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for mountains: Rock materials below
|
||||
var num_rock;
|
||||
for (var y2=0; y2<=45; y2+=15)
|
||||
for (var x2=-75; x2<=75; x2+=15)
|
||||
num_rock += this.mat_mask[GetMaterial(x+x2,y+y2)+1];
|
||||
// need 15pts on first check; 5 to sustain
|
||||
if (num_rock < 15-is_current*10) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Snow(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Must be snowing from above
|
||||
if (GetPXSCount(this.mat, x-300, y-200, 600, 300) < 20 - is_current*15) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Night(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Night time.
|
||||
var time = FindObject(Find_ID(Environment_Time));
|
||||
if (!time || !time->IsNight()) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Overground(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// This is the fallback environment
|
||||
return this;
|
||||
}
|
||||
|
||||
/*-- Proplist --*/
|
||||
|
||||
local Name = "$Name$";
|
||||
local Description = "$Description$";
|
||||
local Environment;
|
|
@ -0,0 +1,2 @@
|
|||
Name=Ambiente
|
||||
Description=Regelt die Geraeuschkulisse.
|
|
@ -0,0 +1,3 @@
|
|||
Name=Ambience
|
||||
Description=Controls environmental sounds and music.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=VisualPath
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack
|
||||
Width=1
|
||||
Height=1
|
||||
|
|
Before Width: | Height: | Size: 525 KiB After Width: | Height: | Size: 525 KiB |
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Hatch
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Structure
|
||||
Width=26
|
||||
Height=26
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Hatch_Graphic
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Width=26
|
||||
Height=26
|
||||
Offset=-13,-13
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=LiftTower
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Structure
|
||||
Width=23
|
||||
Height=68
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=LiftTower_Hook
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=LiftTower_Rope
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack
|
||||
Vertices=2
|
||||
Width=2
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Moss
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_Object
|
||||
Width=8
|
||||
Height=8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DefCore]
|
||||
id=Moss_Lichen
|
||||
Version=5,2,0,1
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack | C4D_Object
|
||||
Width=20
|
||||
Height=20
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[DefCore]
|
||||
id=Ambience
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack | C4D_Rule
|
||||
Picture=0,0,128,128
|
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,299 @@
|
|||
/**
|
||||
Ambience
|
||||
Controls sound and music depending on the environment the player is in
|
||||
|
||||
@author Sven2
|
||||
*/
|
||||
|
||||
local exec_counter; // counter to distribute execution of players across frames
|
||||
local last_environment; // array indexed by player number: pointer to environment the player was in last
|
||||
local environments; // array of available environments for which it is checked if the player is in. sorted by priority.
|
||||
|
||||
// Initialization
|
||||
protected func Initialize()
|
||||
{
|
||||
// Base environment
|
||||
Environment = {
|
||||
actions = [],
|
||||
min_change_delay = 1,
|
||||
min_initial_change_delay = 5,
|
||||
AddSound = this.Env_AddSound,
|
||||
AddAction = this.Env_AddAction,
|
||||
SetMusic = this.Env_SetMusic
|
||||
};
|
||||
// Register default environments (overloadable)
|
||||
this->InitializeEnvironments();
|
||||
// Periodic execution of ambience events
|
||||
last_environment = [];
|
||||
AddTimer(this.Execute, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
func InitializeEnvironments()
|
||||
{
|
||||
// Register all standard environments
|
||||
environments = [];
|
||||
// Underwater: Clonk is swimming in water
|
||||
var underwater = this.env_underwater = new Environment {};
|
||||
underwater->SetMusic("underwater");
|
||||
underwater.CheckPlayer = this.EnvCheck_Underwater;
|
||||
AddEnvironment(underwater, 1400);
|
||||
// City: Clonk is surrounded by buildings
|
||||
var city = this.env_city = new Environment {};
|
||||
city->SetMusic("city");
|
||||
city.CheckPlayer = this.EnvCheck_City;
|
||||
AddEnvironment(city, 1200);
|
||||
// Lava: Lava material is nearby
|
||||
var lava = this.env_lava = new Environment {};
|
||||
lava->SetMusic("lava");
|
||||
lava.CheckPlayer = this.EnvCheck_Lava;
|
||||
lava.mat_mask = CreateArray(); // material mask for lava materials. +1 cuz sky.
|
||||
lava.mat_mask[Material("Lava")+1] = true; // loop over materials and check incindiary instead? Whoever introduces the next lava type can do that...
|
||||
lava.mat_mask[Material("DuroLava")+1] = true;
|
||||
lava.min_change_delay = 3; // Easy to miss lava on search.
|
||||
AddEnvironment(lava, 1000);
|
||||
// Underground: Clonk in front of tunnel
|
||||
var underground = this.env_underground = new Environment {};
|
||||
underground->SetMusic("underground");
|
||||
underground.CheckPlayer = this.EnvCheck_Underground;
|
||||
AddEnvironment(underground, 800);
|
||||
// Mountains: Overground and lots of rock around
|
||||
var mountains = this.env_mountains = new Environment {};
|
||||
mountains->SetMusic("mountains");
|
||||
mountains.CheckPlayer = this.EnvCheck_Mountains;
|
||||
mountains.mat_mask = CreateArray(); // material mask for mountain materials. +1 cuz sky.
|
||||
mountains.mat_mask[Material("Rock")+1] = true;
|
||||
mountains.mat_mask[Material("Granite")+1] = true;
|
||||
mountains.mat_mask[Material("Ore")+1] = true;
|
||||
mountains.mat_mask[Material("Gold")+1] = true;
|
||||
mountains.min_change_delay = 3; // Pretty unstable condition
|
||||
AddEnvironment(mountains, 600);
|
||||
// Snow: It's snowing around the clonk
|
||||
var snow = this.env_snow = new Environment {};
|
||||
snow->SetMusic("snow");
|
||||
snow.CheckPlayer = this.EnvCheck_Snow;
|
||||
snow.min_change_delay = 6; // Persist a while after snowing stopped
|
||||
snow.mat = Material("Snow");
|
||||
AddEnvironment(snow, 400);
|
||||
// Night: Sunlight blocked by planet
|
||||
var night = this.env_night = new Environment {};
|
||||
night->SetMusic("night");
|
||||
night.CheckPlayer = this.EnvCheck_Night;
|
||||
AddEnvironment(night, 200);
|
||||
// Overground: Default environment
|
||||
var overground = this.env_overground = new Environment {};
|
||||
overground->SetMusic("overground");
|
||||
overground.CheckPlayer = this.EnvCheck_Overground;
|
||||
overground->AddSound("Ding", 100);
|
||||
AddEnvironment(overground, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private func Execute()
|
||||
{
|
||||
// Per-player execution every third timer (~.8 seconds)
|
||||
var i=GetPlayerCount(C4PT_User);
|
||||
while (i--) if (!(++exec_counter % 3))
|
||||
{
|
||||
ExecutePlayer(GetPlayerByIndex(i, C4PT_User));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private func ExecutePlayer(int plr)
|
||||
{
|
||||
var cursor = GetCursor(plr);
|
||||
// Determine environment the player is currently in
|
||||
var environment = nil;
|
||||
if (cursor)
|
||||
{
|
||||
var last_env = last_environment[plr];
|
||||
var x = cursor->GetX(), y = cursor->GetY();
|
||||
for (test_environment in environments)
|
||||
{
|
||||
if (environment = test_environment->CheckPlayer(cursor, x, y, test_environment == last_env))
|
||||
{
|
||||
// We've found a matchign environment.
|
||||
// Was it a change? Then check delays first
|
||||
if (test_environment != last_env)
|
||||
{
|
||||
if (last_env && last_env.no_change_delay)
|
||||
{
|
||||
// Environment should change but a delay is specified. Keep last environment for now.
|
||||
--last_env.no_change_delay;
|
||||
environment = last_env;
|
||||
break;
|
||||
}
|
||||
// New environment and change delay has passed.
|
||||
environment.no_change_delay = environment.min_initial_change_delay;
|
||||
Log("%s environment: %s", GetPlayerName(plr), environment.music);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Was no change: Reset change delays
|
||||
environment.no_change_delay = Max(environment.no_change_delay, environment.min_change_delay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
last_environment[plr] = environment;
|
||||
if (!environment) return true;
|
||||
// Music by environment
|
||||
this->SetPlayList(environment.music, plr, true, 3000);
|
||||
// Sounds and actions by environment
|
||||
for (var action in environment.actions)
|
||||
if (Random(1000) < action.chance)
|
||||
cursor->Call(action.fn, action.par[0], action.par[1], action.par[2], action.par[3], action.par[4]);
|
||||
return true;
|
||||
}
|
||||
|
||||
func InitializePlayer(int plr)
|
||||
{
|
||||
// Newly joining players should have set playlist immediately (so they don't start playing a random song just to switch it immediately)
|
||||
ExecutePlayer(plr);
|
||||
return true;
|
||||
}
|
||||
|
||||
func RemovePlayer(int plr)
|
||||
{
|
||||
// Ensure newly joining players don't check on another player's environment
|
||||
last_environment[plr] = nil;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected func Activate(int byplr)
|
||||
{
|
||||
MessageWindow(this.Description, byplr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Environment functions */
|
||||
|
||||
func AddEnvironment(proplist new_env, priority)
|
||||
{
|
||||
if (GetType(priority)) new_env.Priority = priority;
|
||||
this.environments[GetLength(environments)] = new_env;
|
||||
SortArrayByProperty(this.environments, "Priority", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private func Env_AddSound(string snd_name, chance)
|
||||
{
|
||||
return Env_AddAction(Global.Sound, snd_name, chance ?? 50);
|
||||
}
|
||||
|
||||
private func Env_AddAction(afn, par0, par1, par2, par3, par4)
|
||||
{
|
||||
return this.actions[GetLength(this.actions)] = { fn=afn, par=[par0, par1, par2, par3, par4] };
|
||||
}
|
||||
|
||||
private func Env_SetMusic(string playlist)
|
||||
{
|
||||
this.music = playlist;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Default environment checks */
|
||||
|
||||
private func EnvCheck_Underwater(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Clonk should be swimming
|
||||
if (cursor->GetProcedure() != "SWIM") return nil;
|
||||
// For initial change, clonk should also be diving: Check for breath below 80%
|
||||
// Use > instead of >= to ensure 0-breath-clonks can also get the environment
|
||||
if (!is_current && cursor->GetBreath() > cursor.MaxBreath*4/5) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_City(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// There must be buildings around the clonk
|
||||
var building_count = cursor->ObjectCount(cursor->Find_AtRect(-180,-100,360,200), Find_Func("IsStructure"));
|
||||
// 3 buildings to start the environment. Just 1 building to sustain it.
|
||||
if (building_count < 3-2*is_current) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Lava(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for lava pixels. First check if the last lava pixel we found is still in place.
|
||||
var search_range;
|
||||
if (is_current)
|
||||
{
|
||||
if (this.mat_mask[GetMaterial(this.last_x, this.last_y)+1])
|
||||
if (Distance(this.last_x, this.last_y, x, y) < 140)
|
||||
return this;
|
||||
search_range = 140;
|
||||
}
|
||||
else
|
||||
{
|
||||
search_range = 70;
|
||||
}
|
||||
// Now search for lava in search range
|
||||
var ang = Random(360);
|
||||
for (; search_range >= 0; search_range -= 10)
|
||||
{
|
||||
ang += 200;
|
||||
var x2 = x + Sin(ang, search_range);
|
||||
var y2 = y + Cos(ang, search_range);
|
||||
if (this.mat_mask[GetMaterial(x2, y2)+1])
|
||||
{
|
||||
// Lava found!
|
||||
this.last_x = x2;
|
||||
this.last_y = y2;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
// No lava found
|
||||
return nil;
|
||||
}
|
||||
|
||||
private func EnvCheck_Underground(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for underground: No sky at cursor or above
|
||||
if (GetMaterial(x,y)<0) return nil;
|
||||
if (GetMaterial(x,y-30)<0) return nil;
|
||||
if (GetMaterial(x-10,y-20)<0) return nil;
|
||||
if (GetMaterial(x+10,y-20)<0) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Mountains(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Check for mountains: Rock materials below
|
||||
var num_rock;
|
||||
for (var y2=0; y2<=45; y2+=15)
|
||||
for (var x2=-75; x2<=75; x2+=15)
|
||||
num_rock += this.mat_mask[GetMaterial(x+x2,y+y2)+1];
|
||||
// need 15pts on first check; 5 to sustain
|
||||
if (num_rock < 15-is_current*10) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Snow(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Must be snowing from above
|
||||
if (GetPXSCount(this.mat, x-300, y-200, 600, 300) < 20 - is_current*15) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Night(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// Night time.
|
||||
var time = FindObject(Find_ID(Environment_Time));
|
||||
if (!time || !time->IsNight()) return nil;
|
||||
return this;
|
||||
}
|
||||
|
||||
private func EnvCheck_Overground(object cursor, int x, int y, bool is_current)
|
||||
{
|
||||
// This is the fallback environment
|
||||
return this;
|
||||
}
|
||||
|
||||
/*-- Proplist --*/
|
||||
|
||||
local Name = "$Name$";
|
||||
local Description = "$Description$";
|
||||
local Environment;
|
|
@ -0,0 +1,2 @@
|
|||
Name=Ambiente
|
||||
Description=Regelt die Geraeuschkulisse.
|
|
@ -0,0 +1,3 @@
|
|||
Name=Ambience
|
||||
Description=Controls environmental sounds and music.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
[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
|
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 112 KiB |
|
@ -0,0 +1,357 @@
|
|||
/*--
|
||||
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 = CreateObject(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;
|
|
@ -0,0 +1,2 @@
|
|||
Name=Kristallkommunikator
|
||||
Description=Gerät zur Langstreckenkommunikation
|
|
@ -0,0 +1,2 @@
|
|||
Name=Crystal communicator
|
||||
Description=Device for long range communication.
|
|
@ -0,0 +1,5 @@
|
|||
[DefCore]
|
||||
id=Goal_BuildCrystalCommunicator
|
||||
Version=6,0
|
||||
Category=C4D_StaticBack|C4D_Goal
|
||||
Picture=0,0,128,128
|
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,61 @@
|
|||
/*--
|
||||
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$";
|
|
@ -0,0 +1,5 @@
|
|||
Name=Kristallkommunikator bauen
|
||||
|
||||
#Goal window
|
||||
MsgGoalFulfilled=Glückwunsch; der Kristallkommunikator steht.
|
||||
MsgGoalUnfulfilled=Bringe das nötige Baumaterial zur Baustelle des Kristallkommunikator!
|
|
@ -0,0 +1,5 @@
|
|||
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!
|
|
@ -0,0 +1,275 @@
|
|||
/**
|
||||
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) 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;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
[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
|
|
@ -0,0 +1,300 @@
|
|||
/**
|
||||
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 = false;
|
||||
|
||||
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 = CreateObject(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->CreateObject(Metal,-20);
|
||||
goal_site->CreateObject(Ruby,0);
|
||||
goal_site->CreateObject(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 = CreateObject(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)
|
||||
{
|
||||
StartSequence("Intro", 0, GetCrew(first_player));
|
||||
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);
|
||||
structure->~RefreshOwnershipOfSurrounding();
|
||||
}
|
||||
|
||||
// Should be done in OnOwnerChanged? It doesn't happen ATM.
|
||||
RedrawAllFlagRadiuses();
|
||||
|
||||
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
|
||||
CreateObject(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 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 = CreateObject(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);
|
||||
CreateObject(Flagpole, pos[0]-7, pos[1]);
|
||||
var rfp = CreateObject(Flagpole, pos[0]+7, pos[1]);
|
||||
rfp->SetNeutral(true);
|
||||
pos = FindMainIslandPosition(120, 20);
|
||||
CreateObject(Flagpole, pos[0], pos[1]);
|
||||
pos = FindMainIslandPosition(nil, nil, true);
|
||||
CreateObject(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);
|
||||
CreateObject(Sawmill, pos[0], pos[1]);
|
||||
pos = FindMainIslandPosition(nil, nil, true);
|
||||
CreateObject(ChemicalLab, pos[0], pos[1]);
|
||||
pos = FindMainIslandPosition(nil, nil, true);
|
||||
CreateObject(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;
|
||||
}
|
After Width: | Height: | Size: 4.5 KiB |
|
@ -0,0 +1,5 @@
|
|||
[Landscape]
|
||||
BottomOpen=0
|
||||
TopOpen=1
|
||||
MapZoom=20,0,5,20
|
||||
Sky=Clouds2
|
|
@ -0,0 +1 @@
|
|||
Tuesday=Dienstag
|
|
@ -0,0 +1 @@
|
|||
Tuesday=Tuesday
|
|
@ -0,0 +1,48 @@
|
|||
// 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);
|
||||
}
|