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