openclonk/planet/Parkour.ocf/Aerobatics.ocs/Script.c

251 lines
7.6 KiB
C

/**
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;
}