1 /**
2 	Horrid Highway
3 	Players need to completely excavate the sky island far away.
4 
5 	@author Maikel
6 */
7 
8 
9 // Whether the intro has been initialized.
10 static intro_init;
11 
Initialize()12 protected func Initialize()
13 {
14 	// Show wealth in HUD.
15 	GUI_Controller->ShowWealth();
16 
17 	// Goal: locomotive highway.
18 	var goal = CreateObject(Goal_LocomotiveHighway);
19 	goal.LocomotiveGoal = 8 * SCENPAR_Difficulty;
20 
21 	// Rules: team account and buying at flagpole.
22 	CreateObject(Rule_TeamAccount);
23 	CreateObject(Rule_BuyAtFlagpole);
24 
25 	// Allow for base respawns.
26 	var relaunch_rule = GetRelaunchRule();
27 	relaunch_rule->SetInventoryTransfer(false);
28 	relaunch_rule->SetLastClonkRespawn(true);
29 	relaunch_rule->SetFreeCrew(false);
30 	relaunch_rule->SetAllowPlayerRestart(true);
31 	relaunch_rule->SetBaseRespawn(true);
32 	relaunch_rule->SetRespawnDelay(0);
33 
34 	// Initialize different parts of the scenario.
35 	InitEnvironment(SCENPAR_Difficulty);
36 	InitVegetation();
37 	InitAnimals(SCENPAR_Difficulty);
38 	InitBridges();
39 	InitLeftIsland();
40 	InitMiddleIsland();
41 	InitRightIsland();
42 	InitDisasters(SCENPAR_Difficulty);
43 	return;
44 }
45 
OnGoalsFulfilled()46 protected func OnGoalsFulfilled()
47 {
48 	// Give the remaining players their achievement.
49 	GainScenarioAchievement("Done", BoundBy(SCENPAR_Difficulty, 1, 3));
50 	return false;
51 }
52 
53 
54 /*-- Player Initialization --*/
55 
InitializePlayer(int plr)56 protected func InitializePlayer(int plr)
57 {
58 	// Zoom range.
59 	SetPlayerZoomByViewRange(plr, 1200, nil, PLRZOOM_LimitMax);
60 	SetPlayerZoomByViewRange(plr, 500, nil, PLRZOOM_Direct | PLRZOOM_Set);
61 	SetPlayerViewLock(plr, true);
62 
63 	// Position and materials.
64 	var i, crew;
65 	for (i = 0; crew = GetCrew(plr, i); ++i)
66 	{
67 		crew->SetPosition(100, LandscapeHeight() / 2 - 10);
68 		crew->CreateContents(Shovel);
69 	}
70 
71 	// Give the player its knowledge.
72 	GivePlayerBasicKnowledge(plr);
73 	GivePlayerPumpingKnowledge(plr);
74 	GivePlayerFarmingKnowledge(plr);
75 	GivePlayerWeaponryKnowledge(plr);
76 	GivePlayerArtilleryKnowledge(plr);
77 	GivePlayerAdvancedKnowledge(plr);
78 	GivePlayerAirKnowledge(plr);
79 
80 	// Give the player its base materials.
81 	GivePlayerElementaryBaseMaterial(plr);
82 	GivePlayerToolsBaseMaterial(plr);
83 	GivePlayerSpecificBaseMaterial(plr, [[Dynamite, 20, 10]]);
84 
85 	// Claim ownership of structures, last player who joins owns all the flags.
86 	for (var structure in FindObjects(Find_Or(Find_Category(C4D_Structure), Find_Func("IsFlagpole"))))
87 		structure->SetOwner(plr);
88 
89 	// Ensure mimimum player wealth.
90 	var add_wealth = Max(0, 75 - 25 * SCENPAR_Difficulty - GetWealth(plr));
91 	DoWealth(plr, add_wealth);
92 
93 	// Initialize the intro sequence if not yet started.
94 	if (!intro_init)
95 	{
96 		StartSequence("Intro", 0);
97 		intro_init = true;
98 	}
99 	return;
100 }
101 
102 
103 /*-- Scenario Initialization --*/
104 
105 // Initializes environment and disasters.
InitEnvironment(int difficulty)106 private func InitEnvironment(int difficulty)
107 {
108 	// Init time and have normal cycle.
109 	Time->Init();
110 
111 	// Set a certain parallax.
112 	SetSkyParallax(0, 20, 20);
113 
114 	// Adjust sky dependent on difficulty setting.
115 	if (difficulty == 3)
116 		SetSkyAdjust(RGBa(225, 255, 205, 191), RGB(63, 200, 0));
117 
118 	// Clouds and rain.
119 	Cloud->Place(15);
120 	Cloud->SetPrecipitation("Water", 60);
121 	if (difficulty == 3)
122 		Cloud->SetPrecipitation("Acid", 60);
123 	return;
124 }
125 
126 // Initializes grass, trees and in-earth objects.
InitVegetation()127 private func InitVegetation()
128 {
129 	// Grass on all islands.
130 	Grass->Place(100);
131 
132 	// Vegetation around all islands.
133 	Flower->Place(20);
134 	Mushroom->Place(20);
135 	Fern->Place(12);
136 	Branch->Place(30);
137 	Trunk->Place(8);
138 	Vine->Place(18, nil, {attach_material = Loc_Or(Loc_Material("Granite"), Loc_Material("Rock"), Loc_Material("Everrock"), Loc_Material("Gold"))});
139 	Cotton->Place(8);
140 	Wheat->Place(8);
141 
142 	// Some objects in the earth.
143 	PlaceObjects(Rock, 40, "Earth");
144 	PlaceObjects(Firestone, 40, "Earth");
145 	PlaceObjects(Loam, 30, "Earth");
146 
147 	// Place some trees.
148 	Tree_Deciduous->Place(20, Rectangle(0, 0, 600, LandscapeHeight() / 3));
149 	Tree_Deciduous->Place(20, Rectangle(LandscapeWidth() - 600, 0, 600, LandscapeHeight() / 3));
150 	Tree_Coniferous2->Place(12, Rectangle(0, 0, 600, LandscapeHeight() / 3));
151 	Tree_Coniferous2->Place(12, Rectangle(LandscapeWidth() - 600, 0, 600, LandscapeHeight() / 3));
152 	return;
153 }
154 
155 // Initializes animals.
InitAnimals(int difficulty)156 private func InitAnimals(int difficulty)
157 {
158 	// Some fireflies attracted to trees.
159 	Firefly->Place(6);
160 	// Place some bats depending on difficulties.
161 	var bat_region = Rectangle(LandscapeWidth() - 600, 0, 600, LandscapeHeight());
162 	if (difficulty >= 2)
163 		bat_region = Rectangle(LandscapeWidth() / 2 - 300, 0, LandscapeWidth() / 2 + 300, LandscapeHeight());
164 	Bat->Place(10 * difficulty + 5 * difficulty**2, bat_region, {tunnel_only = true});
165 	// Place zaps on higher difficulties.
166 	if (difficulty >= 2)
167 		Zaphive->Place(2 * difficulty);
168 	return;
169 }
170 
InitBridges()171 private func InitBridges()
172 {
173 	// Create four indestructible bridges to connect.
174 	var height = LandscapeHeight() / 2 + 6;
175 	var x4 = 4;
176 	while (GetMaterial(x4, height) == Material("Brick"))
177 		x4 += 8;
178 	var x1 = x4;
179 	while (GetMaterial(x4, height) != Material("Brick") || x4 < LandscapeWidth() / 4)
180 		x4 += 8;
181 	var x2 = x4;
182 	while (GetMaterial(x4, height) == Material("Brick") || x4 < LandscapeWidth() / 2)
183 		x4 += 8;
184 	var x3 = x4;
185 	while (GetMaterial(x4, height) != Material("Brick") || 3 * x4 < LandscapeWidth() / 4)
186 		x4 += 8;
187 	var bridge;
188 	bridge = CreateObject(WoodenBridge, x1 + 20, height);
189 	bridge->MakeInvincible();
190 	bridge->SetClrModulation(RGB(80, 120, 200));
191 	bridge = CreateObject(WoodenBridge, x2 - 20, height);
192 	bridge->MakeInvincible();
193 	bridge->SetClrModulation(RGB(80, 120, 200));
194 	bridge = CreateObject(WoodenBridge, x3 + 20, height);
195 	bridge->MakeInvincible();
196 	bridge->SetClrModulation(RGB(80, 120, 200));
197 	bridge = CreateObject(WoodenBridge, x4 - 20, height);
198 	bridge->MakeInvincible();
199 	bridge->SetClrModulation(RGB(80, 120, 200));
200 	return;
201 }
202 
InitLeftIsland()203 private func InitLeftIsland()
204 {
205 	var switch = CreateObjectAbove(Switch, 20, LandscapeHeight() / 2);
206 	var goal = FindObject(Find_ID(Goal_LocomotiveHighway));
207 	goal->SetPlrViewOnSignalChange(false);
208 	switch->SetSwitchTarget(goal);
209 	switch->SetSwitchDir(-1);
210 
211 	var guidepost = CreateObjectAbove(EnvPack_Guidepost2, 40, LandscapeHeight() / 2);
212 	guidepost->SetInscription("$MsgHorridHighwayEast$");
213 	guidepost.MeshTransformation = EnvPack_Guidepost2.MeshTransformation;
214 
215 	var lorry = CreateObjectAbove(Lorry, 80, LandscapeHeight() / 2 - 2);
216 	lorry->CreateContents(Shovel, 2);
217 	lorry->CreateContents(Hammer, 2);
218 	lorry->CreateContents(Axe, 2);
219 	lorry->CreateContents(Barrel, 2);
220 
221 	var elevator = CreateObjectAbove(Elevator, 180, 384);
222 	elevator->CreateShaft(196);
223 	CreateObjectAbove(Compensator, 230, 384);
224 
225 	var workshop = CreateObjectAbove(ToolsWorkshop, 156, 480);
226 	workshop->CreateContents(Wood, 8);
227 	workshop->CreateContents(Metal, 8);
228 
229 	var chemical_lab = CreateObjectAbove(ChemicalLab, 160, 584);
230 	chemical_lab->CreateContents(Firestone, 8);
231 	chemical_lab->CreateContents(Coal, 8);
232 	CreateObjectAbove(Flagpole, 120, 584);
233 
234 	var bridge = FindObject(Find_ID(WoodenBridge), Sort_Distance(0, LandscapeHeight() / 2));
235 	bridge->CreateObjectAbove(WindGenerator, 0, -4);
236 	bridge->CreateObjectAbove(Flagpole, -36, -4);
237 	bridge->CreateObjectAbove(Catapult, 6, -6);
238 	return;
239 }
240 
InitMiddleIsland()241 private func InitMiddleIsland()
242 {
243 	var lorry = CreateObjectAbove(Lorry, LandscapeWidth() / 2 + RandomX(-20, 20), LandscapeHeight() / 2 - 2);
244 	lorry->CreateContents(Firestone, 12);
245 	lorry->CreateContents(Dynamite, 12);
246 	lorry->CreateContents(DynamiteBox, 8);
247 	lorry->CreateContents(PowderKeg, 8);
248 	lorry->CreateContents(Pickaxe, 2);
249 
250 	var guidepost = CreateObjectAbove(EnvPack_Guidepost2, LandscapeWidth() / 2 + 40, LandscapeHeight() / 2);
251 	guidepost->SetInscription("$MsgResourcesWest$");
252 	guidepost.MeshTransformation = EnvPack_Guidepost2.MeshTransformation;
253 
254 	var diamond_cnt = 12;
255 	for (var cnt = 0; cnt < diamond_cnt; cnt++)
256 	{
257 		var pos = FindLocation(Loc_Material("Gold"));
258 		if (pos)
259 		{
260 			CreateObject(Diamond_Socket, pos.x, pos.y);
261 		}
262 	}
263 	return;
264 }
265 
InitRightIsland()266 private func InitRightIsland()
267 {
268 	var guidepost = CreateObjectAbove(EnvPack_Guidepost2, LandscapeWidth() - 40, LandscapeHeight() / 2);
269 	guidepost->SetInscription("$MsgHorridHighwayWest$");
270 	guidepost.MeshTransformation = EnvPack_Guidepost2.MeshTransformation;
271 
272 	var elevator = CreateObjectAbove(Elevator, LandscapeWidth() - 180, 384);
273 	elevator->CreateShaft(196);
274 
275 	var foundry = CreateObjectAbove(Foundry, LandscapeWidth() - 100, 384);
276 	foundry->CreateContents(Coal, 8);
277 
278 	CreateObjectAbove(WoodenCabin, LandscapeWidth() - 100, 480);
279 	return;
280 }
281 
InitDisasters(int difficulty)282 private func InitDisasters(int difficulty)
283 {
284 	// Lightning: clouds are already inited.
285 	Cloud->SetLightning(3 * difficulty**2 + difficulty + 2);
286 	// Rockfall: appears around the central sky island.
287 	if (difficulty >= 2)
288 	{
289 		Rockfall->SetChance(10 * (difficulty - 1));
290 		Rockfall->SetArea(Rectangle(LandscapeWidth() / 2 - 200, 0, 400, 20));
291 		if (difficulty >= 3)
292 			Rockfall->SetExplosiveness(100);
293 	}
294 	// Meteors: controlled by effect to happen between the main islands.
295 	CreateEffect(FxControlMeteors, 100, 1, difficulty);
296 	return;
297 }
298 
299 static const FxControlMeteors = new Effect
300 {
func(int difficulty)301 	Construction = func(int difficulty)
302 	{
303 		this.difficulty = difficulty;
304 		this.chance = 9 * difficulty + 3 * difficulty**2;
305 		this.Interval = 10;
306 		// Find spawn range according to outer bridges.
307 		var bridge_left = FindObject(Find_ID(WoodenBridge), Sort_Distance(0, LandscapeHeight() / 2));
308 		var bridge_right = FindObject(Find_ID(WoodenBridge), Sort_Distance(LandscapeWidth(), LandscapeHeight() / 2));
309 		this.spawn_range = [bridge_left->GetX() + 134, bridge_right->GetX() - 134];
310 		return FX_OK;
311 	},
312 	Timer = func(int time)
313 	{
314 		if (Random(100) >= 100 - this.chance)
315 		{
316 			var x = RandomX(this.spawn_range[0], this.spawn_range[1]);
317 			var spawn_id = nil;
318 			if (this.difficulty >= 3 && !Random(3))
319 				spawn_id = Chippie_Egg;
320 			LaunchMeteor(x, -12, RandomX(40, 60), RandomX(-15, 15), RandomX(40, 50), spawn_id);
321 		}
322 		return FX_OK;
323 	}
324 };
325 
326