/** Aerobatics Several small sky islands form a chaotic parkour with lots of usable and respawning items. @author Maikel */ protected func Initialize() { // Create the parkour goal. var goal = CreateObject(Goal_Parkour); goal->TransferContentsOnRelaunch(true); // Iterate over checkpoint list and create the checkpoints. // Also add items spawns close to the checkpoints. var map_zoom = GetScenarioVal("MapZoom", "Landscape"); for (var index = 0; index < GetLength(checkpoint_locations); index++) { var cp = checkpoint_locations[index]; var x = map_zoom * cp[0] + map_zoom / 2; var y = map_zoom * cp[1] + map_zoom / 2 - 20; if (index == 0) { goal->SetStartpoint(x, y); ItemSpawn->Create(Dynamite, x - 30, y); ItemSpawn->Create(Loam, x + 30, y); continue; } if (index == GetLength(checkpoint_locations) - 1) { goal->SetFinishpoint(x, y); if (SCENPAR_GameMode == 1) ItemSpawn->Create(Balloon, x + 30, y); continue; } var mode = PARKOUR_CP_Check | PARKOUR_CP_Ordered | PARKOUR_CP_Respawn | PARKOUR_CP_Bonus; goal->AddCheckpoint(x - 10, y, mode); var spawn_id = GetItemSpawnType(index, y); ItemSpawn->Create(spawn_id, x + 20, y); } // Rules: no power and restart with keeping inventory. CreateObject(Rule_NoPowerNeed); // Initialize parts of the scenario. var amount = BoundBy(SCENPAR_NrCheckPoints, 6, 20); InitMaterials(amount); InitEnvironment(amount); InitVegetation(amount); InitAnimals(amount); // Start the intro sequence. StartSequence("Intro", 0, checkpoint_locations[0], checkpoint_locations[-1]); return; } private func GetItemSpawnType(int cp_number, int height) { // For normal checkpoints the item type depends on the height and is random. var spawn_items = [Dynamite, Loam]; if (cp_number >= 3) { if (height < LandscapeHeight() / 2) var spawn_items = [Dynamite, Loam, DynamiteBox]; else var spawn_items = [Dynamite, Loam, Loam, Boompack]; } return RandomElement(spawn_items); } private func InitMaterials(int amount) { // In material objects. PlaceObjects(Dynamite, 4 * amount, "Earth"); PlaceObjects(Loam, 4 * amount, "Earth"); PlaceObjects(Metal, 2 * amount, "Earth"); PlaceObjects(Cloth, amount, "Earth"); // Additional item spawns. if (SCENPAR_GameMode == 2) { // There is no balloon in the horizontal mode at the finish so place one randomly in the first part of the map. var pos = FindLocation(Loc_Sky(), Loc_Space(30), Loc_InRect(LandscapeWidth() / 6, 150, LandscapeWidth() / 6, LandscapeHeight() - 300)); if (pos) ItemSpawn->Create(Balloon, pos.x, pos.y); } // Place chests on several of the sky islands. for (var count = 0; count < amount / 2; count++) { var pos = FindIslandLocation(true); if (!pos) continue; var chest = CreateObjectAbove(Chest, pos.x, pos.y); chest->CreateContents(Dynamite, 4); chest->CreateContents(Club, 4); chest->CreateContents(Blunderbuss)->CreateContents(LeadBullet); chest->CreateContents(Blunderbuss)->CreateContents(LeadBullet); chest->CreateContents(Blunderbuss)->CreateContents(LeadBullet); chest->CreateContents(IronBomb, 4); chest->CreateContents(GrenadeLauncher)->CreateContents(IronBomb); chest->CreateContents(GrenadeLauncher)->CreateContents(IronBomb); if (!Random(2)) chest->CreateContents(Boompack); if (!Random(2)) chest->CreateContents(WallKit); } // Load all weapons in the chests. for (var weapon in FindObjects(Find_Or(Find_ID(Blunderbuss), Find_ID(GrenadeLauncher)))) weapon->SetLoaded(); // Place some catapults. for (var count = 0; count < amount / 4; count++) { var pos = FindIslandLocation(); if (pos) CreateObjectAbove(Catapult, pos.x, pos.y); } // Place some cannons. for (var count = 0; count < amount / 4; count++) { var pos = FindIslandLocation(); if (pos) { var cannon = CreateObjectAbove(Cannon, pos.x, pos.y); cannon->CreateContents(PowderKeg, 1); } } // An inventor's lab on its island. var map_zoom = GetScenarioVal("MapZoom", "Landscape"); var lab = CreateObjectAbove(InventorsLab, inventorslab_location[0] * map_zoom, inventorslab_location[1] * map_zoom); lab->MakeInvincible(); return; } private func FindIslandLocation(bool is_chest) { var map_zoom = GetScenarioVal("MapZoom", "Landscape"); var pos = {x = inventorslab_location[0] * map_zoom, y = inventorslab_location[1] * map_zoom}; for (var tries = 0; tries < 200; tries++) { pos = FindLocation(Loc_Sky(), Loc_Wall(CNAT_Bottom)); if (!pos) continue; if (FindObject(Find_Category(C4D_Vehicle), Find_Distance(30, pos.x, pos.y))) continue; if (is_chest && FindObject(Find_Or(Find_ID(ParkourCheckpoint), Find_ID(Chest)), Find_Distance(30, pos.x, pos.y))) continue; break; } return pos; } private func InitEnvironment(int amount) { for (var count = 0; count < amount / 2; count++) { var pos = FindLocation(Loc_Sky(), Loc_Space(60), Loc_InRect(100, 100, LandscapeWidth() - 200, LandscapeHeight() - 200)); if (!pos) return; var pos2 = FindLocation(Loc_Sky(), Loc_Space(40), Loc_InRect(pos.x - 20, pos.y - 60, 40, 40)); if (!pos2) return; JetStream->CreateLine([[pos.x, pos.y + 20], [pos.x + RandomX(-8, 8), pos.y - 30 - RandomX(-8, 8)], [pos2.x, pos2.y - 10]], nil, RandomX(32, 48), RandomX(40, 60)); } return; } private func InitVegetation(int amount) { Grass->Place(100); Branch->Place(3 * amount); Mushroom->Place(amount); Fern->Place(amount); Flower->Place(3 * amount / 2); Tree_Deciduous->Place(amount); Tree_Coniferous2->Place(amount / 4); Tree_Coniferous3->Place(amount / 4); Cotton->Place(amount / 2); Vine->Place(amount); return; } private func InitAnimals(int amount) { Butterfly->Place(amount); Mosquito->Place(amount / 2); Zaphive->Place(amount / 4); return; } /*-- Player Control --*/ protected func InitializePlayer(int plr) { // Make the player enemy to all other players. Goal_Melee->MakeHostileToAll(plr, GetPlayerTeam(plr)); // Set large zoom ranges for the player. SetPlayerZoomByViewRange(plr, 1200, nil, PLRZOOM_LimitMax); // Give the player knowledge for items in the inventor's lab. SetPlrKnowledge(plr, WindBag); SetPlrKnowledge(plr, WallKit); SetPlrKnowledge(plr, Balloon); return; } public func OnPlayerRespawn(int plr, object cp) { var crew = GetCrew(plr); if (!crew) return; // Ensure at least a shovel, loam and dynamite on respawn when items not present and there is space in inventory. if (!FindObject(Find_ID(Shovel), Find_Container(crew)) && crew->ContentsCount() < crew.MaxContentsCount) crew->CreateContents(Shovel); if (!FindObject(Find_ID(Loam), Find_Container(crew)) && crew->ContentsCount() < crew.MaxContentsCount) crew->CreateContents(Loam); if (!FindObject(Find_ID(Dynamite), Find_Container(crew)) && crew->ContentsCount() < crew.MaxContentsCount) crew->CreateContents(Dynamite); // Give the crew more HP, so that it can survive more flint jumps. crew.MaxEnergy = 80000; crew->DoEnergy(crew.MaxEnergy / 1000); return; } // Give the player a bonus when he reaches a new checkpoint for the first time and is behind the leader. public func GivePlrBonus(int plr, object cp) { var crew = GetCrew(plr); if (!crew) return; var goal = cp->GetCPController(); if (!goal) return; var cp_behind = goal->GetLeaderClearedCheckpoints() - goal->GetPlayerClearedCheckpoints(plr); if (cp_behind <= 2) return; var windbag = FindObject(Find_ID(WindBag), Find_Container(crew)); if (windbag) { windbag->SetUsageCount(windbag->GetUsageCount() + cp_behind); } else { if (crew->ContentsCount() < crew.MaxContentsCount) windbag = crew->CreateContents(WindBag); else windbag = CreateObjectAbove(WindBag, cp->GetX(), cp->GetY() + cp->GetBottom(), plr); windbag->SetUsageCount(2 * cp_behind); } return; }