1 /**
2 	Flooded Veins
3 	A cave landscape where the player starts at the top and needs to retrieve some gems from
4 	caves filled with water.
5 
6 	@author Sven2, Maikel
7 */
8 
9 
10 // Whether the intro has been initialized.
11 static intro_init;
12 
13 // Whether the first players has been initialized.
14 static first_plr_init;
15 
Initialize()16 protected func Initialize()
17 {
18 	// Show wealth in HUD.
19 	GUI_Controller->ShowWealth();
20 
21 	// Rules: team account and buying at flagpole.
22 	CreateObject(Rule_TeamAccount);
23 	CreateObject(Rule_BuyAtFlagpole);
24 
25 	// Goal: Sell a certain amount of gems dependent on difficulty.
26 	var goal = CreateObject(Goal_SellGems);
27 	goal->SetTargetAmount(10 * SCENPAR_Difficulty);
28 
29 	// Initialize different parts of the scenario.
30 	InitEnvironment(SCENPAR_Difficulty);
31 	InitVegetation(SCENPAR_MapSize);
32 	InitAnimals(SCENPAR_MapSize, SCENPAR_Difficulty);
33 	return;
34 }
35 
OnGoalsFulfilled()36 protected func OnGoalsFulfilled()
37 {
38 	// Give the remaining players their achievement.
39 	GainScenarioAchievement("Done", BoundBy(SCENPAR_Difficulty, 1, 3));
40 	return false;
41 }
42 
43 
44 /*-- Player Initialization --*/
45 
InitializePlayer(int plr)46 protected func InitializePlayer(int plr)
47 {
48 	// Harsh zoom range.
49 	SetPlayerZoomByViewRange(plr, 300, nil, PLRZOOM_Direct | PLRZOOM_LimitMax);
50 	SetPlayerViewLock(plr, true);
51 
52 	// First player inits the base.
53 	if (!first_plr_init)
54 	{
55 		InitBase(plr, 4 - SCENPAR_Difficulty);
56 		first_plr_init = true;
57 	}
58 
59 	// Position and materials for the crew.
60 	var crew;
61 	for (var i = 0; crew = GetCrew(plr, i); ++i)
62 	{
63 		crew->SetPosition(20 + Random(32), 160 - 10);
64 		crew->CreateContents(Shovel);
65 		if (i == 0)
66 			crew->CreateContents(Hammer);
67 		else
68 			crew->CreateContents(Axe);
69 	}
70 
71 	// Give the player basic and pumping knowledge.
72 	GivePlayerBasicKnowledge(plr);
73 	GivePlayerPumpingKnowledge(plr);
74 	GivePlayerSpecificKnowledge(plr, [WallKit, WindBag, TeleGlove]);
75 
76 	// Give the player the elementary base materials.
77 	GivePlayerElementaryBaseMaterial(plr);
78 
79 	// Initialize the intro sequence if not yet started.
80 	if (!intro_init)
81 	{
82 		StartSequence("Intro", 0);
83 		intro_init = true;
84 	}
85 	return;
86 }
87 
InitBase(int owner,int amount)88 private func InitBase(int owner, int amount)
89 {
90 	var y = 160;
91 
92 	// The basic settlement consists of a flagpole and a wind generator.
93 	var x = 232;
94 	while ((!GBackSky(x, y - 62) || !GBackSky(x, y - 66)) && x < 400)
95 		x++;
96 	CreateObjectAbove(WindGenerator, x + 4, y, owner);
97 	CreateObjectAbove(Flagpole, x - 24, y, owner);
98 
99 	// Additional material includes a foundry and tools workshop.
100 	if (amount >= 2)
101 	{
102 		var foundry = CreateObjectAbove(Foundry, x + 38, y, owner);
103 		foundry->CreateContents(Coal, 4);
104 		foundry->CreateContents(Metal, 2);
105 		var workshop = CreateObjectAbove(ToolsWorkshop, x - 56, y, owner);
106 		workshop->CreateContents(Wood, 4);
107 		workshop->CreateContents(Metal, 2);
108 		var lorry = CreateObjectAbove(Lorry, x - 56, 155);
109 
110 		// And even more material includes explosives and food.
111 		if (amount >= 3)
112 		{
113 			var chemicallab = CreateObjectAbove(ChemicalLab, x + 84, y, owner);
114 			chemicallab->CreateContents(Dynamite, 4);
115 			chemicallab->CreateContents(DynamiteBox, 4);
116 			workshop->CreateContents(Wood, 4);
117 			workshop->CreateContents(Metal, 2);
118 			workshop->CreateContents(Pickaxe, 1);
119 			lorry->CreateContents(Bread, 4);
120 		}
121 	}
122 	return;
123 }
124 
125 
126 /*-- Scenario Initialization --*/
127 
InitEnvironment(int difficulty)128 private func InitEnvironment(int difficulty)
129 {
130 	// Sky has some parallax.
131 	SetSkyParallax(1, 20, 20);
132 
133 	// Some earthquakes if difficulty prescribes it.
134 	if (difficulty >= 2)
135 		Earthquake->SetChance(4 * (difficulty - 1));
136 
137 	// On insane difficulty the water level keeps rising.
138 	if (difficulty >= 3)
139 		AddEffect("RisingWater", nil, 100, 1);
140 	return;
141 }
142 
FxRisingWaterTimer(object target,proplist effect)143 global func FxRisingWaterTimer(object target, proplist effect)
144 {
145 	// Insert pixel on the water surface if there is tunnel above.
146 	for (var x = Random(120); x < LandscapeWidth(); x += RandomX(80, 120))
147 	{
148 		// Find first tunnel from the bottom.
149 		var y = LandscapeHeight();
150 		while (GBackSemiSolid(x, y) && y > 0)
151 			y -= 1;
152 		// Check if there is liquid below the tunnel.
153 		if (GBackLiquid(x, y + 2))
154 			InsertMaterial(Material("Water"), x, y + 4);
155 	}
156 	return FX_OK;
157 }
158 
InitVegetation(int map_size)159 private func InitVegetation(int map_size)
160 {
161 	var wdt = LandscapeWidth();
162 	var hgt = LandscapeHeight();
163 
164 	// Cave mushrooms scattered around the top and middle sections.
165 	LargeCaveMushroom->Place(10, Shape->Rectangle(0, 0, wdt, hgt / 8), { terraform = false });
166 	LargeCaveMushroom->Place(15, Shape->Rectangle(0, hgt / 8, wdt, hgt / 8), { terraform = false });
167 	LargeCaveMushroom->Place(15, Shape->Rectangle(0, 2 * hgt / 8, wdt, hgt / 8), { terraform = false });
168 
169 	// Cave entrance covered with mushrooms and bushes.
170 	SproutBerryBush->Place(4, Shape->Rectangle(0, 120, 100, 40));
171 	Fern->Place(4, Shape->Rectangle(0, 120, 100, 40));
172 	Mushroom->Place(6, Shape->Rectangle(0, 120, 100, 40));
173 
174 	// The cavern has some grass wherever possible.
175 	PlaceGrass(100);
176 
177 	// Entrance also location for a small cemetary.
178 	CreateObjectAbove(Column, 64, 160)->SetObjDrawTransform(400, 0, 0, 0, 400, 0);
179 	for (var x = 72; x < 104; x += RandomX(6, 14))
180 		CreateObjectAbove(Clonk_Grave, x, 160)->SetInscriptionMessage("R.I.P.");
181 	CreateObjectAbove(Column, 112, 160)->SetObjDrawTransform(400, 0, 0, 0, 400, 0);
182 
183 	// Some ferns and mushrooms scattered around the top and middle sections.
184 	Fern->Place(12, Shape->Rectangle(0, 0, wdt, 3 * hgt / 8));
185 	Mushroom->Place(14, Shape->Rectangle(0, 0, wdt, 3 * hgt / 8));
186 
187 	// Create earth materials in big clusters so the whole object arrangement looks a bit less uniform and more interesting.
188 	PlaceBatches([Firestone], 3, 100, 5);
189 	PlaceBatches([Rock, Loam, Loam], 10, 200, 10);
190 
191 	// Place some underwater vegetation in the flooded caves.
192 	var place_rect = Shape->Rectangle(50, hgt / 2, wdt - 100, hgt / 2);
193 	Seaweed->Place(16 + 4 * map_size, place_rect);
194 	Coral->Place(16 + 8 * map_size, place_rect);
195 	return;
196 }
197 
InitAnimals(int map_size,int difficulty)198 private func InitAnimals(int map_size, int difficulty)
199 {
200 	var wdt = LandscapeWidth();
201 	var hgt = LandscapeHeight();
202 
203 	// Place some fishes and piranhas as difficulty prescribes it.
204 	var place_rect = Shape->Rectangle(50, hgt / 2, wdt - 100, hgt / 2);
205 	var fish_count = 10 + 10 * map_size;
206 	Fish->Place(fish_count * (3 - difficulty), place_rect);
207 	Piranha->Place(fish_count * (difficulty - 1), place_rect);
208 	return;
209 }
210 
211 
212 /*-- Some helper functions --*/
213 
PlaceBatches(array item_ids,int n_per_batch,int batch_radius,int n_batches)214 private func PlaceBatches(array item_ids, int n_per_batch, int batch_radius, int n_batches)
215 {
216 	// place a number (n_batches) of batches of objects of types item_ids. Each batch has n_per_batch objects.
217 	// fewer batches and/or objects may be placed if no space is found
218 	var loc, loc2, n_item_ids = GetLength(item_ids), n_created = 0, obj;
219 	for (var i = 0; i < n_batches; ++i)
220 		if (loc = FindLocation(Loc_Material("Earth")))
221 			for (var j = 0; j < n_per_batch; ++j)
222 				if (loc2 = FindLocation(Loc_InRect(loc.x - batch_radius,loc.y - batch_radius, batch_radius * 2, batch_radius * 2), Loc_Material("Earth")))
223 					if (obj = CreateObjectAbove(item_ids[Random(n_item_ids)], loc2.x, loc2.y))
224 					{
225 						obj->SetPosition(loc2.x, loc2.y);
226 						++n_created;
227 					}
228 	return n_created;
229 }
230 
TestGemCount()231 global func TestGemCount()
232 {
233 	var pos;
234 	while (pos = FindLocation(Loc_Or(Loc_Material("Ruby"), Loc_Material("Amethyst"))))
235 	{
236 		var pos = CreateObjectAbove(Rock, pos.x, pos.y)->Explode(100);
237 	}
238 	var gem_count = ObjectCount(Find_Or(Find_ID(Ruby), Find_ID(Amethyst)));
239 	return gem_count;
240 }
241