1 /*
2 	Guardians of the Windmills
3 	Authors: Randrian, Newton, Clonkonaut
4 
5 	Defend the windmills against waves of enemies
6 */
7 
8 
9 static g_wave; // index of current wave
10 static g_spawned_enemies;
11 static g_relaunchs; // array of relaunch counts
12 static g_scores; // array of player scores
13 static g_ai; // derived from AI; contains changes for this scenario
14 static g_lost; // True if all windmills are destroyed
15 static const ENEMY = 10; // player number of enemy
16 static const ALLOW_DEBUG_COMMANDS = true;
17 
18 static const MAX_RELAUNCH = 10;
19 
20 static shared_wealth_remainder;
21 
22 //======================================================================
23 /* Initialization */
24 
Initialize()25 func Initialize()
26 {
27 	// dev stuff (we will forget to turn this off for release)
28 	//AddMsgBoardCmd("waveinfo", "GameCall(\"ShowWaveInfo\")");
29 	//AddMsgBoardCmd("next", "GameCall(\"SetNextWave\", \"%s\")");
30 	//AddMsgBoardCmd("nextwait", "GameCall(\"SetNextWave\", \"%s\", true)");
31 	//AddMsgBoardCmd("scrooge", "GameCall(\"DoWealthForAll\", 1000000000)");
32 	// Wealth shown at all time
33 	GUI_Controller->ShowWealth();
34 	// static variable init
35 	g_homebases = [];
36 	InitWaveData();
37 }
38 
InitializePlayer(int plr,int iX,int iY,object pBase,int iTeam)39 func InitializePlayer(int plr, int iX, int iY, object pBase, int iTeam)
40 {
41 	if (GetPlayerType(plr) != C4PT_User) return;
42 
43 	if (g_lost) { EliminatePlayer(plr); return; } // no post-elimination join
44 	if (!g_relaunchs)
45 	{
46 		g_relaunchs = [];
47 		g_scores = [];
48 		Scoreboard->Init([{key = "relaunchs", title = Rule_Relaunch, sorted = true, desc = true, default = "", priority = 75},
49 	                    {key = "score", title = Nugget, sorted = true, desc = true, default = "0", priority = 100}]);
50 	}
51 	g_relaunchs[plr] = MAX_RELAUNCH;
52 	g_scores[plr] = 0;
53 	Scoreboard->NewPlayerEntry(plr);
54 	Scoreboard->SetPlayerData(plr, "relaunchs", g_relaunchs[plr]);
55 	Scoreboard->SetPlayerData(plr, "score", g_scores[plr]);
56 
57 	CreateObject(Homebase, 0,0, plr);
58 
59 	SetPlayerZoomByViewRange(plr, 1200, 0, PLRZOOM_LimitMax);
60 
61 	//DoWealth(plr, 10000000);
62 
63 	JoinPlayer(plr);
64 	if (!g_wave) StartGame();
65 }
66 
RemovePlayer(int plr)67 func RemovePlayer(int plr)
68 {
69 	Scoreboard->SetPlayerData(plr, "relaunchs", Icon_Cancel);
70 	// Split player's wealth among the remaining players
71 	ScheduleCall(nil, Scenario.DoSharedWealth, 50, 1, GetWealth(plr));
72 }
73 
TransferInventory(object from,object to)74 private func TransferInventory(object from, object to)
75 {
76 	// Drop some items that cannot be transferred (such as connected pipes and dynamite igniters)
77 	var i = from->ContentsCount(), contents;
78 	while (i--)
79 		if (contents = from->Contents(i))
80 			if (contents->~IsDroppedOnDeath(from))
81 				contents->Exit();
82 	return to->GrabContents(from);
83 }
84 
JoinPlayer(plr,prev_clonk)85 func JoinPlayer(plr, prev_clonk)
86 {
87 	var x=991,y = 970;
88 	var clonk = GetCrew(plr);
89 	if (clonk)
90 	{
91 		clonk->SetPosition(x,y-10);
92 	}
93 	else
94 	{
95 		clonk = CreateObjectAbove(Clonk, x,y, plr);
96 		clonk->MakeCrewMember(plr);
97 	}
98 	SetCursor(plr, clonk);
99 	clonk->DoEnergy(1000);
100 	// contents
101 	clonk.MaxContentsCount = 1;
102 	if (prev_clonk) TransferInventory(prev_clonk, clonk);
103 	if (!clonk->ContentsCount())
104 	{
105 		clonk->CreateContents(Bow);
106 		var arrow = CreateObjectAbove(Arrow);
107 		clonk->Collect(arrow);
108 		arrow->SetInfiniteStackCount();
109 	}
110 	clonk->~CrewSelection(); // force update HUD
111 	// Make this work under the friendly fire rule.
112 	for (var obj in [g_windgen1, g_windgen2, g_windgen3, g_windmill])
113 		if (obj)
114 			obj->SetOwner(plr);
115 }
116 
117 // Enter all buyable things into the homebase
FillHomebase(object homebase)118 func FillHomebase(object homebase)
119 {
120 	// Quick buy items on hotkeys
121 	homebase->SetQuickbuyItems([WindBag, Bow, Javelin, Blunderbuss, GrenadeLauncher, nil, nil, nil, nil, nil]);
122 
123 	// Buy menu entries
124 	homebase->AddCaption("$HomebaseWeapons$");
125 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Weapon     { item = Bow,                         ammo = Arrow,    desc = "$HomebaseDescBow$" });
126 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Weapon     { item = Javelin,         cost = 10,                   desc = "$HomebaseDescJavelin$"                                            , infinite = true});
127 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Weapon     { item = Blunderbuss,          cost = 50,  ammo = LeadBullet, desc = "$HomebaseDescBlunderbuss$",          requirements = ["AdvancedWeapons"] });
128 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Weapon     { item = GrenadeLauncher,             ammo = IronBomb, desc = "$HomebaseDescGrenadeLauncher$", requirements = ["MasterWeapons"] });
129 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Weapon     { item = WindBag,         cost = 500,                  desc = "$HomebaseDescWindBag$", requirements = ["MasterWeapons"] });
130 
131 	homebase->AddCaption("$HomebaseItems$");
132 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Consumable { item = Bread,     cost = 5  });
133 //	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Weapon { item = Hammer,    cost = 1000, desc = "$HomebaseDescHammer$", extra_width = 1 });
134 
135 	homebase->AddCaption("$HomebaseTechnology$");
136 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Technology { name="$HomebaseAdvancedWeapons$", item = Icon_World,cost = 100, desc="$HomebaseDescAdvancedWeapons$", tech = "AdvancedWeapons" });
137 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Technology { name="$HomebaseMasterWeapons$", item = Icon_World,cost = 1000, desc = "$HomebaseDescMasterWeapons$", tech = "MasterWeapons", requirements = ["AdvancedWeapons"] });
138 
139 	homebase->AddCaption("$HomebaseUpgrades$");
140 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Technology { name="$HomebaseLoadSpeed$", item = Homebase_Icon, graphics="LoadSpeed%d", costs = [100, 500, 1000], desc = "$HomebaseDescLoadSpeed$", tech = "LoadSpeed", tiers=3 });
141 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Technology { name="$HomebaseShootingStrength$", item = Homebase_Icon, graphics="ShootingStrength%d", costs = [50, 150, 350], desc = "$HomebaseDescShootingStrength$", tech = "ShootingStrength", tiers=3 });
142 	homebase->AddHomebaseItem(new Homebase.ITEMTYPE_Technology { name="$HomebaseLife$", item = Homebase_Icon, graphics="Life%d", costs = [10, 50, 100], desc = "$HomebaseDescLife$", tech = "Life", tiers=3 });
143 	homebase->AddCaption("$HomebaseArtifacts$");
144 }
145 
146 // Clonk death callback
OnClonkDeath(clonk,killed_by)147 func OnClonkDeath(clonk, killed_by)
148 {
149 	// Player died?
150 	if (!clonk) return;
151 	var plr = clonk->GetOwner();
152 	if (GetPlayerType(plr) == C4PT_User)
153 	{
154 		// Relaunch count
155 		if (!g_relaunchs[plr])
156 		{
157 			Log("$MsgOutOfRelaunchs$", GetTaggedPlayerName(plr));
158 			Scoreboard->SetPlayerData(plr, "relaunchs", Icon_Cancel);
159 			EliminatePlayer(plr);
160 			return false;
161 		}
162 		// Relaunch count
163 		--g_relaunchs[plr];
164 		Scoreboard->SetPlayerData(plr, "relaunchs", g_relaunchs[plr]);
165 		Log("$MsgRelaunch$", GetTaggedPlayerName(plr));
166 		JoinPlayer(plr, clonk);
167 	}
168 	else
169 	{
170 		// Enemy clonk death
171 		// Remove inventory
172 		var i = clonk->ContentsCount(), obj;
173 		while (i--) if (obj=clonk->Contents(i))
174 			if (!obj->~OnContainerDeath())
175 				obj->RemoveObject();
176 		// Clear enemies from list
177 		i = GetIndexOf(g_spawned_enemies, clonk);
178 		if (i>=0)
179 		{
180 			g_spawned_enemies[i] = nil;
181 			// Kill bounty
182 			if (killed_by>=0)
183 			{
184 				Scoreboard->SetPlayerData(killed_by, "score", ++g_scores[killed_by]);
185 				DoWealth(killed_by, clonk.Bounty);
186 			}
187 			else
188 			{
189 				// Killer could not be determined. Just give gold to everyone.
190 				DoSharedWealth(clonk.Bounty);
191 			}
192 		}
193 	}
194 	return;
195 }
196 
197 //======================================================================
198 /* The Game */
199 
StartGame()200 func StartGame()
201 {
202 	// Init objects to defend
203 	var obj;
204 	for (obj in [g_windgen1, g_windgen2, g_windgen3, g_windmill]) if (obj)
205 	{
206 		obj->SetCategory(C4D_Living);
207 		obj->SetAlive(true);
208 		obj.MaxEnergy = 800000;
209 		obj->DoEnergy(obj.MaxEnergy/1000);
210 		obj->AddEnergyBar();
211 		GameCallEx("OnCreationRuleNoFF", obj);
212 	}
213 	// Launch first wave!
214 	g_wave = 1;
215 	ScheduleCall(nil, Scenario.LaunchWave, 50, 1, g_wave);
216 	return true;
217 }
218 
WindmillDown(object windmill)219 public func WindmillDown(object windmill)
220 {
221 	if (g_windgen1 == windmill) g_windgen1 = nil;
222 	if (g_windgen2 == windmill) g_windgen2 = nil;
223 	if (g_windgen3 == windmill) g_windgen3 = nil;
224 	if (g_windmill == windmill) g_windmill = nil;
225 
226 	Sound("Objects::Plane::PlaneCrash", true);
227 
228 	// Nothing left to defend?
229 	if (!g_windgen1 && !g_windgen2 && !g_windgen3 && !g_windmill)
230 	{
231 		// Fail!
232 		var i=GetPlayerCount(C4PT_User);
233 		while (i--) EliminatePlayer(GetPlayerByIndex(i, C4PT_User));
234 		g_lost = true;
235 		ScheduleCall(nil, Global.GameOver, 50, 1);
236 	}
237 }
238 
DoSharedWealth(int amount)239 public func DoSharedWealth(int amount)
240 {
241 	// Split gold among all players. Keep track of remainder and use it next time
242 	shared_wealth_remainder += amount;
243 	var cnt = GetPlayerCount(C4PT_User);
244 	if (cnt)
245 	{
246 		var wealth_add = shared_wealth_remainder / cnt;
247 		if (wealth_add)
248 		{
249 			shared_wealth_remainder -= wealth_add * cnt;
250 			DoWealthForAll(wealth_add);
251 		}
252 	}
253 	return true;
254 }
255 
DoWealthForAll(int amount)256 public func DoWealthForAll(int amount)
257 {
258 	// Add wealth to all players
259 	for (var iplr = 0; iplr < GetPlayerCount(C4PT_User); ++iplr)
260 		DoWealth(GetPlayerByIndex(iplr, C4PT_User), amount);
261 	return true;
262 }
263 
264 //======================================================================
265 /* Enemy waves */
266 
LaunchWave(int wave)267 func LaunchWave(int wave)
268 {
269 	// * Schedules spawning of all enemies
270 	// * Schedules call to LaunchWaveDone() after last enemy has been spawned
271 	var wave_data = ENEMY_WAVE_DATA[g_wave];
272 	g_spawned_enemies = [];
273 	if (wave_data)
274 	{
275 		var wave_spawn_time = 0;
276 		CustomMessage(Format("$MsgWave$: %s", wave, wave_data.Name));
277 		Sound("UI::Ding");
278 		if (wave_data.Chest != nil && g_chest)
279 		{
280 			var item = g_chest->CreateContents(wave_data.Chest.Item);
281 			if (item)
282 			{
283 				if (item->GetID() == GoldBar)
284 				{
285 					item->SetValue(wave_data.Chest.Value);
286 					g_chest->SetMeshMaterial("GoldenChest", 0);
287 				}
288 			}
289 		}
290 		for (var enemy in ForceVal2Array(wave_data.Enemies)) if (enemy)
291 		{
292 			if (enemy.Delay)
293 				ScheduleCall(nil, Scenario.ScheduleLaunchEnemy, enemy.Delay, 1, enemy);
294 			else
295 				ScheduleLaunchEnemy(enemy);
296 			wave_spawn_time = Max(wave_spawn_time, enemy.Delay + enemy.Interval * enemy.Num);
297 		}
298 		for (var arrow in ForceVal2Array(wave_data.Arrows))
299 		{
300 			CreateArrowForPlayers(arrow.X, arrow.Y);
301 		}
302 		ScheduleCall(nil, Scenario.LaunchWaveDone, wave_spawn_time+5, 1, wave);
303 		return true;
304 	}
305 	return false;
306 }
307 
CreateArrowForPlayers(int x,int y)308 func CreateArrowForPlayers(int x, int y)
309 {
310 	for (var i = 0; i < GetPlayerCount(C4PT_User); i++)
311 	{
312 		var plr = GetPlayerByIndex(i, C4PT_User);
313 		var cursor = GetCursor(plr);
314 		if (!cursor) continue;
315 		var arrow = CreateObject(GUI_GoalArrow, cursor->GetX(), cursor->GetY(), plr);
316 		if (!arrow) continue;
317 		arrow->SetAction("Show", cursor);
318 		arrow->SetR(Angle(cursor->GetX(), cursor->GetY(), x, y));
319 		arrow->SetClrModulation(RGBa(255, 50, 0, 128));
320 		Schedule(arrow, "RemoveObject()", 100);
321 	}
322 }
323 
ScheduleLaunchEnemy(proplist enemy)324 func ScheduleLaunchEnemy(proplist enemy)
325 {
326 	// Schedules spawning of enemy definition
327 	// Spawn on ground or in air?
328 	var xmin, xmax, ymin, ymax;
329 	var def = enemy.Type ?? enemy.Vehicle;
330 	if (!def) def = Clonk;
331 	var width = def->GetDefWidth();
332 	var height = def->GetDefWidth();
333 
334 	xmin = BoundBy(enemy.PosX - 100, 0 + width/2, LandscapeWidth() - width/2);
335 	xmax = BoundBy(enemy.PosX + 100, 0 + width/2, LandscapeWidth() - width/2);
336 	ymin = BoundBy(enemy.PosY - 100, 0 + height/2, LandscapeHeight() - height/2);
337 	ymax = BoundBy(enemy.PosY + 100, 0 + height/2, LandscapeHeight() - height/2);
338 
339 	ScheduleCall(nil, CustomAI.LaunchEnemy, Max(enemy.Interval,1), Max(enemy.Num,1), enemy, xmin, xmax - xmin, ymin, ymax - ymin);
340 	return true;
341 }
342 
LaunchWaveDone(int wave)343 func LaunchWaveDone(int wave)
344 {
345 	// All enemies spawned! Now start timer to check whether they are all dead
346 	ScheduleCall(nil, Scenario.CheckWaveCleared, 20, 9999999, wave);
347 	return true;
348 }
349 
CheckWaveCleared(int wave)350 func CheckWaveCleared(int wave)
351 {
352 	// Check timer to determine if enemy wave has been cleared.
353 	// Enemies nil themselves when they're dead. So clear out nils and we're done when the list is empty
354 	var nil_idx;
355 	while ( (nil_idx=GetIndexOf(g_spawned_enemies))>=0 )
356 	{
357 		var l = GetLength(g_spawned_enemies) - 1;
358 		if (nil_idx<l) g_spawned_enemies[nil_idx] = g_spawned_enemies[l];
359 		SetLength(g_spawned_enemies, l);
360 	}
361 	if (!GetLength(g_spawned_enemies))
362 	{
363 		// All enemies dead!
364 		ClearScheduleCall(nil, Scenario.CheckWaveCleared);
365 		OnWaveCleared(wave);
366 	}
367 }
368 
OnWaveCleared(int wave)369 func OnWaveCleared(int wave)
370 {
371 	var bounty = ENEMY_WAVE_DATA[g_wave].Bounty, bounty_msg = "";
372 	if (bounty)
373 	{
374 		bounty = bounty * 4 / BoundBy(GetPlayerCount(C4PT_User), 1, 4); // Carefully tested balancing
375 		bounty_msg = Format("|<c ffff00>+%d</c>{{Icon_Wealth}}", bounty);
376 		DoWealthForAll(bounty);
377 	}
378 	CustomMessage(Format("$MsgWaveCleared$%s|                                                             ", wave, bounty_msg));
379 	Sound("UI::NextWave");
380 	// Fade out stuff
381 	Airship->AllStop();
382 	if (g_object_fade)
383 		for (var obj in FindObjects(Find_Or(Find_And(Find_ID(Clonk), Find_Not(Find_OCF(OCF_Alive))), Find_ID(Catapult), Find_ID(Airship))))
384 			obj->AddEffect("IntFadeOut", obj, 100, 1, g_object_fade, Rule_ObjectFade);
385 	// Next wave!
386 	++g_wave;
387 	if (ENEMY_WAVE_DATA[g_wave])
388 		ScheduleCall(nil, Scenario.LaunchWave, 500, 1, g_wave);
389 	else
390 	{
391 		// There is no next wave? Game done D:
392 		ScheduleCall(nil, Scenario.OnAllWavesCleared, 50, 1);
393 	}
394 }
395 
GiveRandomAttackTarget(object attacker)396 public func GiveRandomAttackTarget(object attacker)
397 {
398 	return GetRandomWindmill();
399 }
400 
401 //======================================================================
402 /* Game end */
403 
OnAllWavesCleared()404 func OnAllWavesCleared()
405 {
406 	// Success!
407 	if (g_goal) g_goal.is_fulfilled = true;
408 	if (GetPlayerType(ENEMY) == C4PT_Script) EliminatePlayer(ENEMY);
409 	GainScenarioAchievement("Done");
410 	GameOver();
411 	return true;
412 }
413 
414 //======================================================================
415 /* Wave and enemy definitions */
416 
417 static ENEMY_WAVE_DATA;
418 
419 static const g_respawning_weapons = [Firestone, Rock];
420 
InitWaveData()421 func InitWaveData()
422 {
423 	// Define different enemy types
424 	var pilot       = { Name="$EnemyPilot$",     Inventory=Rock,          Energy=30, Bounty=20, Color=0xff0000ff, Skin=CSKIN_Alchemist, Backpack=0, Vehicle=Airship };
425 	var swordman    = { Name="$EnemyCrewman$",   Inventory=Sword,         Energy=50, Bounty=15, Color=0xffff0000, Skin=CSKIN_Default,   Backpack=0,                    IsCrew=true };
426 	var defender    = { Name="$EnemyDefender$",  Inventory=[Shield, Axe], Energy=50, Bounty=10, Color=0xff00ff00, Skin=CSKIN_Farmer,    Backpack=0,                    IsCrew=true };
427 	var bowman      = { Name="$EnemyBow$",       Inventory=[Bow, Arrow],  Energy=30, Bounty=10, Color=0xff80ff80, Skin=CSKIN_Steampunk, Backpack=0,                    IsCrew=true };
428 	var artillery   = { Name="$EnemyArtillery$", Inventory=Firestone,     Energy=10, Bounty=25, Color=0xffffff80, Skin=CSKIN_Steampunk, Backpack=0, Vehicle=Catapult,  IsCrew=true };
429 	var ballooner   = { Name="$EnemyBalloon$",   Inventory=Sword,         Energy=30, Bounty=15, Color=0xff008000, Skin=CSKIN_Default,               Vehicle=Balloon };
430 	var rocketeer   = { Name="$EnemyRocket$",    Inventory=[Bow, Arrow],  Energy=15, Bounty=15, Color=0xffffffff, Skin=CSKIN_Steampunk,             Vehicle=DefenseBoomAttack };
431 	var boomattack  = { Type=DefenseBoomAttack, Bounty=2 };
432 	var boomattackf = { Type=DefenseBoomAttack, Bounty=15, Speed=300 };
433 
434 	// Define composition of waves
435 	ENEMY_WAVE_DATA = [nil,
436 		{ Name = "$WaveFirst$", Bounty = 1, Enemies =
437 			new boomattack   {  Num= 1, Interval=10, PosX = 0, PosY = 500 },
438 			Arrows = { X = 0, Y = 500 },
439 			Chest = { Item = GoldBar, Value = 25 }
440 		}, { Name = "$WaveSecond$", Bounty = 30, Enemies =
441 			[new boomattack  {  Num= 3, Interval=10, PosX = 0,    PosY = 500 },
442 			new boomattack   {  Num= 3, Interval=10, PosX = 2000, PosY = 500 },],
443 			Arrows = [{ X = 0, Y = 500 },{ X = 2000, Y = 500 }]
444 		}, { Name = "$WaveThird$", Bounty = 10, Enemies =
445 			new rocketeer    {  Num= 8, PosX = 0, PosY = 500 },
446 			Arrows = { X = 0, Y = 500 }
447 		}, { Name = "$WaveFourth$", Bounty = 15, Enemies =
448 			[new rocketeer   {  Num= 8, Interval=10, PosX = 0,    PosY = 500 },
449 			new rocketeer    {  Num= 8, Interval=10, PosX = 2000, PosY = 500 },],
450 			Arrows = [{ X = 0, Y = 500 },{ X = 2000, Y = 500 }]
451 		}, { Name = "$WaveFifth$", Bounty = 20, Enemies =
452 			[new boomattack  {  Num= 10,           PosX = 1000, PosY = 2000 },
453 			new pilot        {  Num= 1, Delay = 1, PosX = 2000, PosY = 750  },
454 			new defender     {  Num= 1, Delay = 2, PosX = 2000, PosY = 750  },
455 			new pilot        {  Num= 1, Delay = 3, PosX = 0,    PosY = 750  },
456 			new defender     {  Num= 1, Delay = 4, PosX = 0,    PosY = 750  },],
457 			Arrows = [{ X = 0, Y = 750 },{ X = 2000, Y = 750 },{ X = 1000, Y = 2000 }]
458 		}, { Name = "$WaveSixth$", Bounty = 20, Enemies =
459 			[new pilot       {  Num= 1, Delay = 1, PosX = 2000, PosY = 1250 },
460 			new defender     {  Num= 2, Delay = 2, PosX = 2000, PosY = 1250 },
461 			new bowman       {  Num= 2, Delay = 2, PosX = 2000, PosY = 1250 },
462 			new swordman     {  Num= 1, Delay = 2, PosX = 2000, PosY = 1250 },
463 			new pilot        {  Num= 1, Delay = 3, PosX = 0,    PosY = 1250 },
464 			new defender     {  Num= 2, Delay = 4, PosX = 0,    PosY = 1250 },
465 			new bowman       {  Num= 2, Delay = 4, PosX = 0,    PosY = 1250 },
466 			new swordman     {  Num= 1, Delay = 4, PosX = 0,    PosY = 1250 },],
467 			Arrows = [{ X = 0, Y = 1250 },{ X = 2000, Y = 1250 }],
468 			Chest = { Item = GoldBar, Value = 100 }
469 		}, { Name = "$WaveSeventh$", Bounty = 50, Enemies =
470 			new ballooner    {  Num= 10, PosX = 1000, PosY = 0 },
471 			Arrows = { X = 1000, Y = 0 }
472 		}, { Name = "$WaveEighth$", Bounty = 50, Enemies =
473 			[new boomattack  {  Num= 15, Interval =  5,             PosX = 500,  PosY =    0 },
474 			new pilot        {  Num=  1,                Delay = 80, PosX = 0,    PosY = 1250 },
475 			new defender     {  Num=  3,                Delay = 81, PosX = 0,    PosY = 1250 },
476 			new bowman       {  Num=  3,                Delay = 81, PosX = 0,    PosY = 1250 },
477 			new pilot        {  Num=  1,                Delay = 82, PosX = 2000, PosY = 1250 },
478 			new defender     {  Num=  3,                Delay = 83, PosX = 2000, PosY = 1250 },
479 			new bowman       {  Num=  3,                Delay = 83, PosX = 2000, PosY = 1250 },
480 			new pilot        {  Num=  1,                Delay = 84, PosX = 200,  PosY = 2000 },
481 			new defender     {  Num=  3,                Delay = 85, PosX = 200,  PosY = 2000 },
482 			new swordman     {  Num=  3,                Delay = 85, PosX = 200,  PosY = 2000 },
483 			new pilot        {  Num=  1,                Delay = 86, PosX = 1800, PosY = 2000 },
484 			new defender     {  Num=  3,                Delay = 87, PosX = 1800, PosY = 2000 },
485 			new swordman     {  Num=  3,                Delay = 87, PosX = 1800, PosY = 2000 },],
486 			Arrows = [{ X = 500, Y = 0 },{ X = 0, Y = 1250 },{ X = 2000, Y = 1250 },{ X = 200, Y = 2000 },{ X = 1800, Y = 2000 }]
487 		}, { Name = "$WaveNinth$", Bounty = 100, Enemies =
488 			[new ballooner   {  Num= 10, Interval = 10, Delay = 350, PosX = 1000, PosY =   0 },
489 			new boomattackf  {  Num= 8,  Interval =  1,              PosX = 0,    PosY = 300 },
490 			new boomattackf  {  Num= 8,  Interval =  1,              PosX = 2000, PosY = 300 },],
491 			Arrows = [{ X = 1000, Y = 0 },{ X = 0, Y = 300 },{ X = 2000, Y = 300 }]
492 		}, { Name = "$WaveTenth$", Bounty = 1000, Enemies =
493 			[new boomattack  {  Num=  7, Interval =  1,              PosX = 0,    PosY =    0 },
494 			new boomattack   {  Num=  7, Interval =  1,              PosX = 2000, PosY =    0 },
495 			new rocketeer    {  Num=  4, Interval = 10,              PosX = 0,    PosY =  300 },
496 			new rocketeer    {  Num=  4, Interval = 10,              PosX = 2000, PosY =  300 },
497 			new ballooner    {  Num= 10, Interval =  5, Delay = 100, PosX = 1000, PosY =    0 },
498 			new pilot        {  Num=  1,                Delay =  80, PosX = 0,    PosY = 1250 },
499 			new defender     {  Num=  3,                Delay =  81, PosX = 0,    PosY = 1250 },
500 			new bowman       {  Num=  4,                Delay =  81, PosX = 0,    PosY = 1250 },
501 			new pilot        {  Num=  1,                Delay =  82, PosX = 2000, PosY = 1250 },
502 			new defender     {  Num=  3,                Delay =  83, PosX = 2000, PosY = 1250 },
503 			new bowman       {  Num=  4,                Delay =  83, PosX = 2000, PosY = 1250 },
504 			new pilot        {  Num=  1,                Delay =  84, PosX = 200,  PosY = 2000 },
505 			new defender     {  Num=  3,                Delay =  85, PosX = 200,  PosY = 2000 },
506 			new swordman     {  Num=  4,                Delay =  85, PosX = 200,  PosY = 2000 },
507 			new pilot        {  Num=  1,                Delay =  86, PosX = 1800, PosY = 2000 },
508 			new defender     {  Num=  3,                Delay =  87, PosX = 1800, PosY = 2000 },
509 			new swordman     {  Num=  4,                Delay =  87, PosX = 1800, PosY = 2000 },
510 			new pilot        {  Num=  1,                Delay =  88, PosX = 880,  PosY = 2000 },
511 			new bowman       {  Num=  3,                Delay =  89, PosX = 880,  PosY = 2000 },
512 			new swordman     {  Num=  3,                Delay =  89, PosX = 880,  PosY = 2000 },
513 			new pilot        {  Num=  1,                Delay =  88, PosX = 1120, PosY = 2000 },
514 			new bowman       {  Num=  3,                Delay =  89, PosX = 1120, PosY = 2000 },
515 			new swordman     {  Num=  3,                Delay =  89, PosX = 1120, PosY = 2000 },],
516 			Arrows = [{ X = 0, Y = 0 },{ X = 2000, Y = 0 },{ X = 0, Y = 300 },{ X = 2000, Y = 300 },{ X = 1000, Y = 0 },{ X = 0, Y = 1250 },{ X = 2000, Y = 1250 },{ X = 200, Y = 2000 },{ X = 1800, Y = 2000 },{ X = 880, Y = 2000 },{ X = 1120, Y = 2000 }]
517 		}];
518 	return true;
519 }