forked from Mirrors/openclonk
262 lines
7.5 KiB
C
262 lines
7.5 KiB
C
/**
|
|
Dark Mine
|
|
Dark caves with narrow connections set the stage for this battle. Every player
|
|
only has a single clonk and no relaunches, so caution is needed.
|
|
|
|
@author Maikel
|
|
*/
|
|
|
|
|
|
// List for storing the different large caves.
|
|
static cave_list;
|
|
|
|
protected func Initialize()
|
|
{
|
|
// Goals and rules.
|
|
CreateObject(Goal_LastManStanding);
|
|
CreateObject(Rule_KillLogs);
|
|
CreateObject(Rule_Gravestones);
|
|
|
|
// Rescale cave coordinates with map zoom and shuffle them.
|
|
var mapzoom = GetScenarioVal("MapZoom", "Landscape");
|
|
for (var cave in cave_list)
|
|
{
|
|
cave[0] *= mapzoom;
|
|
cave[1] *= mapzoom;
|
|
}
|
|
ShuffleArray(cave_list);
|
|
// Then add a nil entry at position one as a separator.
|
|
PushFront(cave_list, nil);
|
|
|
|
// Initialize different parts of the scenario.
|
|
// Amount of things depends on the map size.
|
|
var plr_cnt = GetStartupPlayerCount();
|
|
var map_size = BoundBy(120 + plr_cnt * 10, 140, 240);
|
|
InitVegetation(map_size);
|
|
InitMaterials(map_size);
|
|
InitEnvironment(map_size);
|
|
InitLorries();
|
|
return;
|
|
}
|
|
|
|
// Callback from the last man standing goal.
|
|
protected func RelaunchCount()
|
|
{
|
|
// Relaunch count depends on scenario setting.
|
|
return BoundBy(SCENPAR_NrRelaunches, 0, 3);
|
|
}
|
|
|
|
// Callback from the last man standing goal.
|
|
protected func KillsToRelaunch()
|
|
{
|
|
// No relaunches awarded for kills.
|
|
return 0;
|
|
}
|
|
|
|
// Callback from the last man standing goal.
|
|
// Takes over the role of initializing the player.
|
|
protected func OnPlayerRelaunch(int plr, int relaunch_cnt)
|
|
{
|
|
var is_relaunch = relaunch_cnt != RelaunchCount();
|
|
// Get the only clonk of the player.
|
|
var clonk = GetCrew(plr);
|
|
|
|
// Players start in a random small cave, the cave depends on whether it is a relaunch.
|
|
var cave = FindStartCave(plr, is_relaunch);
|
|
clonk->SetPosition(cave[0], cave[1]);
|
|
|
|
// Players start with a shovel, a pickaxe and two firestones.
|
|
clonk->CreateContents(Shovel);
|
|
clonk->CreateContents(Pickaxe);
|
|
clonk->CreateContents(Torch);
|
|
// Better weapons after relaunching.
|
|
if (!is_relaunch)
|
|
{
|
|
clonk->CreateContents(Firestone, 2);
|
|
}
|
|
else
|
|
{
|
|
clonk->CreateContents(Javelin);
|
|
clonk->CreateContents(BombArrow);
|
|
}
|
|
|
|
// Set the zoom range to be standard low, but allow for zooming out
|
|
// such that light sources a bit further away can be spotted.
|
|
SetPlayerZoomByViewRange(plr, 300, nil, PLRZOOM_Direct);
|
|
SetPlayerZoomByViewRange(plr, 600, nil, PLRZOOM_LimitMax);
|
|
SetPlayerViewLock(plr, true);
|
|
return;
|
|
}
|
|
|
|
// Finds a start cave which is furthest away from the center and from other already used start caves.
|
|
private func FindStartCave(int plr, bool is_relaunch)
|
|
{
|
|
var wdt = LandscapeWidth() / 2;
|
|
var hgt = LandscapeHeight() / 2;
|
|
// Find the already used caves.
|
|
var used_caves = [];
|
|
var index_av;
|
|
for (index_av = 0; index_av < GetLength(cave_list); index_av++)
|
|
{
|
|
if (cave_list[index_av] == nil)
|
|
break;
|
|
PushBack(used_caves, cave_list[index_av]);
|
|
}
|
|
// Then iterate over all still available caves and find the one furthest away from all other caves.
|
|
var best_index;
|
|
var max_dist = 0;
|
|
for (var index = index_av + 1; index < GetLength(cave_list); index++)
|
|
{
|
|
var cave = cave_list[index];
|
|
// Also furthest away from center cave.
|
|
var dist = Distance(cave[0], cave[1], wdt, hgt);
|
|
// For a normal cave take distance to used caves, for a relaunch to alive clonks.
|
|
if (!is_relaunch)
|
|
{
|
|
for (var comp_cave in used_caves)
|
|
dist = Min(dist, Distance(cave[0], cave[1], comp_cave[0], comp_cave[1]));
|
|
}
|
|
else
|
|
{
|
|
for (var clonk in FindObjects(Find_OCF(OCF_CrewMember), Find_Not(Find_Owner(plr))))
|
|
dist = Min(dist, Distance(cave[0], cave[1], clonk->GetX(), clonk->GetY()));
|
|
}
|
|
if (dist > max_dist)
|
|
{
|
|
best_index = index;
|
|
max_dist = dist;
|
|
}
|
|
}
|
|
// If no cave has found, spawn in the large cave.
|
|
if (best_index == nil)
|
|
return [wdt, hgt];
|
|
// Determine found cave and move it in front of the separator if it is not a relaunch.
|
|
var found_cave = cave_list[best_index];
|
|
if (!is_relaunch)
|
|
{
|
|
RemoveArrayIndex(cave_list, best_index);
|
|
PushFront(cave_list, found_cave);
|
|
}
|
|
// Return the location of the found cave.
|
|
return found_cave;
|
|
}
|
|
|
|
|
|
/*-- Scenario Initiliaztion --*/
|
|
|
|
private func InitVegetation(int map_size)
|
|
{
|
|
// Place some cave mushrooms for cover.
|
|
LargeCaveMushroom->Place(map_size - 20, nil, { terraform = false });
|
|
|
|
// Some mushrooms to regain health.
|
|
Mushroom->Place(map_size / 2);
|
|
Fern->Place(map_size / 3);
|
|
|
|
// Place some branches and trunks around the map.
|
|
Branch->Place(map_size / 2);
|
|
Trunk->Place(map_size / 4, nil, { size = [60, 80] });
|
|
return;
|
|
}
|
|
|
|
private func InitMaterials(int map_size)
|
|
{
|
|
// Some objects in the earth or rock material.
|
|
PlaceObjects(Loam, map_size / 3, "Earth");
|
|
PlaceObjects(Firestone, map_size / 3, "Earth");
|
|
PlaceObjects(Dynamite, map_size / 5, "Earth");
|
|
PlaceObjects(DynamiteBox, map_size / 6, "Rock");
|
|
PlaceObjects(PowderKeg, map_size / 10, "Rock");
|
|
|
|
// Some pickaxes, shovels in the tunnels.
|
|
for (var i = 0; i < map_size / 5; i++)
|
|
{
|
|
var loc = FindLocation(Loc_Tunnel(), Loc_Wall(CNAT_Bottom));
|
|
if (!loc)
|
|
continue;
|
|
CreateObjectAbove([Shovel, Pickaxe, GoldBar][Random(3)], loc.x, loc.y)->SetR(Random(360));
|
|
}
|
|
return;
|
|
}
|
|
|
|
private func InitEnvironment(int map_size)
|
|
{
|
|
var wdt = LandscapeWidth();
|
|
var hgt = LandscapeHeight();
|
|
// Some lights in the main cave as a strategic element.
|
|
for (var side = -1; side <= 1; side += 2)
|
|
{
|
|
// Both sides of the cave are lighted for all players.
|
|
var torch = CreateObjectAbove(Torch, wdt / 2 + side * 50, hgt / 2 + 32);
|
|
torch->AttachToWall(true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
private func InitLorries()
|
|
{
|
|
var wdt = LandscapeWidth();
|
|
var hgt = LandscapeHeight();
|
|
// Create lorries at random small caves, but only for half of them.
|
|
for (var index = GetLength(cave_list) - 1; index >= GetLength(cave_list) / 2; index--)
|
|
{
|
|
var cave = cave_list[index];
|
|
var lorry = CreateObjectAbove(Lorry, cave[0], cave[1]);
|
|
// Basic objects which are in every lorry.
|
|
lorry->CreateContents(Dynamite, RandomX(2, 4));
|
|
lorry->CreateContents(Loam, RandomX(2, 4));
|
|
// Objects which are only in half of the lorries.
|
|
if (!Random(2))
|
|
lorry->CreateContents(DynamiteBox, RandomX(1, 2));
|
|
if (!Random(2))
|
|
lorry->CreateContents(Shield);
|
|
if (!Random(2))
|
|
lorry->CreateContents(Torch, 2);
|
|
// Objects which are only in one third of the lorries.
|
|
if (!Random(3))
|
|
lorry->CreateContents(GrappleBow, RandomX(1, 2));
|
|
if (!Random(3))
|
|
lorry->CreateContents(Bread, RandomX(1, 2));
|
|
if (!Random(3))
|
|
{
|
|
lorry->CreateContents(Bow);
|
|
lorry->CreateContents([Arrow, FireArrow, FireArrow][Random(3)], 2);
|
|
}
|
|
// Objects which are only in one fifth of the lorries.
|
|
if (!Random(5))
|
|
lorry->CreateContents(Javelin, RandomX(1, 2));
|
|
if (!Random(5))
|
|
lorry->CreateContents(Club, RandomX(1, 2));
|
|
if (!Random(5))
|
|
{
|
|
var barrel = lorry->CreateContents(Barrel);
|
|
barrel->SetFilled("Water", Barrel->BarrelMaxFillLevel());
|
|
}
|
|
// Objects which are only in one eighth of the lorries.
|
|
if (!Random(8))
|
|
lorry->CreateContents(IronBomb, RandomX(1, 2));
|
|
if (!Random(8))
|
|
lorry->CreateContents(WallKit, 1);
|
|
if (!Random(8))
|
|
{
|
|
lorry->CreateContents(Musket);
|
|
lorry->CreateContents(LeadShot);
|
|
}
|
|
}
|
|
// Create two lorries at the main cave.
|
|
for (var side = -1; side <= 1; side += 2)
|
|
{
|
|
// Create lorry with useful tools and weapons.
|
|
var lorry = CreateObjectAbove(Lorry, wdt / 2 + side * 50, hgt / 2 + 44);
|
|
lorry->CreateContents(Bow, 2);
|
|
lorry->CreateContents(BombArrow, 4);
|
|
lorry->CreateContents(Boompack, 2);
|
|
lorry->CreateContents(PowderKeg, 2);
|
|
lorry->CreateContents(TeleGlove, 1);
|
|
lorry->CreateContents(WindBag, 1);
|
|
lorry->CreateContents(GrenadeLauncher);
|
|
lorry->CreateContents(IronBomb, 4);
|
|
}
|
|
return;
|
|
}
|