openclonk/planet/Worlds.ocf/FloodedVeins.ocs/Map.c

303 lines
12 KiB
C

/**
Flooded Veins
Dynamic map with a cave leading to gems filled with water deep down.
@author Sven2, Maikel
*/
#include Library_Map
// Called be the engine: draw the complete map here.
protected func InitializeMap(proplist map)
{
// Retrieve the settings according to the MapSize setting.
var map_size;
if (SCENPAR_MapSize == 1)
map_size = [100, 150];
if (SCENPAR_MapSize == 2)
map_size = [100, 175];
if (SCENPAR_MapSize == 3)
map_size = [100, 200];
// Set the map size.
map->Resize(map_size[0], map_size[1]);
// The map is completely covered in earth.
map->Draw("Earth");
// Draw a cavern somewhere at the top where the settlement is located.
// This takes up the top 25 landscape pixels.
DrawCavern(map);
// Draw the middle section of the map with useful resources.
// This takes 45, 50 or 55 landscape pixels.
DrawMiddle(map, 40 + 5 * SCENPAR_MapSize);
// Draw the gem veins including it being flooded at the bottom of the map.
// This takes up the remaining landscape pixels, which depends on the map size.
DrawGemVeins(map, 60 + 20 * SCENPAR_MapSize, SCENPAR_Difficulty);
// Return true to tell the engine a map has been successfully created.
return true;
}
// Draws the top cavern where the player starts it settlement.
public func DrawCavern(proplist map)
{
var wdt = map.Wdt;
var cavern_hgt = 20;
// Create a mask for the cavern part.
var map_top = {Algo = MAPALGO_Rect, X = 0, Y = 0, Wdt = wdt, Hgt = cavern_hgt};
// The top part is mainly granite and rock.
map->DrawMaterial("Earth-earth_root", map_top, 2, 16);
map->DrawMaterial("Earth-earth_spongy", map_top, 2, 16);
map->DrawMaterial("Earth-earth", map_top, 4, 12);
map->DrawMaterial("Granite", map_top, 3, 60);
map->DrawMaterial("Tunnel", map_top, 2, 16);
map->DrawMaterial("Rock-rock", map_top, 3, 20);
map->DrawMaterial("Rock", map_top, 3, 20);
// The cavern is located a bit to the left and can be entered from the left side of the map.
var cavern = {Algo = MAPALGO_Ellipsis, X = wdt / 2 - 10, Y = cavern_hgt - 7, Wdt = 20, Hgt = 7};
cavern = {Algo = MAPALGO_Or, Op = [cavern, {Algo = MAPALGO_Turbulence, Iterations = 4, Seed = Random(65536), Op = cavern}]};
cavern = {Algo = MAPALGO_Or, Op = [cavern, {Algo = MAPALGO_Rect, X = wdt / 2 - 30, Y = cavern_hgt - 7, Wdt = 40, Hgt = 7}]};
cavern = {Algo = MAPALGO_And, Op = [cavern, map_top]};
map->Draw("Tunnel", cavern);
map->DrawMaterial("Tunnel-brickback", cavern, 5, 32);
map->DrawMaterial("Sky", cavern, 5, 20);
map->DrawMaterial("Sky", cavern, 4, 20);
map->DrawMaterial("Sky", cavern, 3, 20);
var cavern_ground = {Algo = MAPALGO_Border, Bottom = -2, Op = cavern};
map->DrawMaterial("Brick", cavern_ground, 3, 80);
// Draw the left tunnel.
var tunnel_left = {Algo = MAPALGO_Polygon, X = [0, wdt / 2 - 30], Y = [cavern_hgt, cavern_hgt], Wdt = -6, Open = 1, Empty = 1};
tunnel_left = {Algo = MAPALGO_Or, Op = [tunnel_left, {Algo = MAPALGO_Turbulence, Amplitude = 6, Scale = 8, Iterations = 4, Seed = Random(65536), Op = tunnel_left}]};
tunnel_left = {Algo = MAPALGO_And, Op = [tunnel_left, map_top]};
map->Draw("Tunnel", tunnel_left);
map->DrawMaterial("Tunnel-brickback", tunnel_left, 3, 32);
map->DrawMaterial("Sky", tunnel_left, 5, 16);
map->DrawMaterial("Sky", tunnel_left, 3, 24);
var tunnel_entrance = {Algo = MAPALGO_And, Op = [tunnel_left, {Algo = MAPALGO_Rect, X = 0, Y = 0, Wdt = 2, Hgt = cavern_hgt}]};
map->Draw("Sky", tunnel_entrance);
var tunnel_left_ground = {Algo = MAPALGO_Border, Bottom = -2, Op = tunnel_left};
tunnel_left_ground = {Algo = MAPALGO_Or, Op = [tunnel_left, {Algo = MAPALGO_Turbulence, Amplitude = 6, Scale = 6, Iterations = 2, Seed = Random(65536), Op = tunnel_left_ground}]};
tunnel_left_ground = {Algo = MAPALGO_And, Op = [tunnel_left_ground, {Algo = MAPALGO_Not, Op = map_top}]};
map->DrawMaterial("Brick", tunnel_left_ground, 3, 24);
// Draw the right tunnel.
var tunnel_right = {Algo = MAPALGO_Polygon, X = [wdt / 2 + 10, wdt / 2 + 30, wdt / 2 + 36], Y = [cavern_hgt - 4, cavern_hgt - 8, cavern_hgt + 6], Wdt = -4, Open = 1, Empty = 1};
tunnel_right = {Algo = MAPALGO_Or, Op = [tunnel_right, {Algo = MAPALGO_Turbulence, Amplitude = 8, Scale = 6, Iterations = 4, Seed = Random(65536), Op = tunnel_right}]};
//tunnel_right = {Algo = MAPALGO_And, Op = [tunnel_right, map_top]};
map->Draw("Tunnel", tunnel_right);
map->DrawMaterial("Tunnel-brickback", tunnel_right, 3, 24);
return;
}
// Draws the middle section of the map with useful resources.
public func DrawMiddle(proplist map, int size)
{
var wdt = map.Wdt;
// Create a mask for the middle part.
var map_middle = {Algo = MAPALGO_Rect, X = 0, Y = 25, Wdt = wdt, Hgt = size};
// Fill the middle part with resources.
map->DrawMaterial("Earth-earth_root", map_middle, 2, 16);
map->DrawMaterial("Earth-earth_spongy", map_middle, 2, 16);
map->DrawMaterial("Earth-earth", map_middle, 4, 12);
map->DrawMaterial("Granite", map_middle, 3, 10);
map->DrawMaterial("Tunnel", map_middle, 2, 8);
map->DrawMaterial("Rock-rock", map_middle, 3, 8);
map->DrawMaterial("Rock", map_middle, 3, 8);
map->DrawMaterial("Ore", map_middle, 6, 16);
map->DrawMaterial("Firestone", map_middle, 5, 12);
map->DrawMaterial("Coal", map_middle, 6, 16);
// Create a tunnel system which covers the middle part.
var tunnel = {Algo = MAPALGO_Rect, X = 0, Y = 0, Wdt = 0, Hgt = 0};
for (var i = 0; i < 5; i++)
{
var x1 = RandomX(8, wdt - 8);
var y1 = RandomX(30, 10 + size);
var dir = RandomX(-15, 15) + Random(2) * 180;
var dist = RandomX(20, 28);
var x2 = BoundBy(x1 + Cos(dir, dist), 4, wdt - 4);
var y2 = BoundBy(y1 + Sin(dir, dist), 30, 15 + size);
var add_tunnel = {Algo = MAPALGO_Polygon, X = [x1, x2], Y = [y1, y2], Wdt = -RandomX(3, 4), Open = 1, Empty = 1};
var tunnel = {Algo = MAPALGO_Or, Op = [tunnel, add_tunnel]};
}
tunnel = {Algo = MAPALGO_Turbulence, Amplitude = 6, Scale = 8, Iterations = 4, Seed = Random(65536), Op = tunnel};
map->Draw("Tunnel", tunnel);
var tunnel_ground = {Algo = MAPALGO_Border, Bottom = -2, Op = tunnel};
map->Draw("Earth", tunnel_ground);
map->DrawMaterial("Earth-earth_root", tunnel_ground, 2, 16);
map->DrawMaterial("Earth-earth_spongy", tunnel_ground, 2, 16);
map->DrawMaterial("Earth-earth", tunnel_ground, 4, 12);
return;
}
// Draws the gem veins including it being flooded at the bottom of the map.
public func DrawGemVeins(proplist map, int size, int difficulty)
{
var wdt = map.Wdt;
var hgt = map.Hgt;
// Create a mask for the bottom part.
var map_bottom = {Algo = MAPALGO_Rect, X = 0, Y = hgt - size, Wdt = wdt, Hgt = size};
// Fill the bottom with mostly granite materials.
map->Draw("Granite", map_bottom);
map->DrawMaterial("Firestone", map_bottom, 2, 6);
map->DrawMaterial("Rock-rock", map_bottom, 3, 12);
map->DrawMaterial("Rock", map_bottom, 3, 12);
map->DrawMaterial("Earth-earth_root", map_bottom, 2, 8);
map->DrawMaterial("Earth-earth_spongy", map_bottom, 2, 8);
map->DrawMaterial("Earth-earth", map_bottom, 4, 6);
// Draw a labyrinth using nodes and connections and make it out of tunnel.
var nodes = FindVeinNodes(map, size + 12, size / 2);
var connections = FindNodeConnections(nodes, 24);
for (var con in connections)
map->Draw("Tunnel", con);
// Make three out of the five gems nodes contain gems and protect with granite.
var cnt = 0, i = 0;
while (cnt < 3 && i < 5)
{
var node = nodes[i];
i++;
if (node.conn_count == 0)
continue;
var tunnel = node.tunnels[0];
var gem_border = {Algo = MAPALGO_And, Op = [{Algo = MAPALGO_Border, Left = 2, Right = 2, Op = tunnel}, {Algo = MAPALGO_Rect, X = 0, Y = node.Y - 3, Wdt = wdt, Hgt = 9}]};
var granite_border = {Algo = MAPALGO_And, Op = [{Algo = MAPALGO_Border, Left = 2, Right = 2, Op = tunnel}, {Algo = MAPALGO_Rect, X = 0, Y = node.Y - 5, Wdt = wdt, Hgt = 2}]};
map->Draw(["Ruby", "Amethyst"][Random(2)], gem_border);
map->Draw("Granite", granite_border);
cnt++;
}
// Replace the tunnels with water up to a certain level, depends on difficulty.
var water_level = size - 8 * (3 - difficulty);
var tunnels = Duplicate("Tunnel");
var tunnels_algo = {Algo = MAPALGO_Layer, Layer = tunnels};
tunnels_algo = {Algo = MAPALGO_And, Op = [tunnels_algo, {Algo = MAPALGO_Rect, X = 0, Y = hgt - water_level, Wdt = wdt, Hgt = water_level}]};
map->Draw("Water", tunnels_algo);
// On insane the background material of the bottom part is water.
if (difficulty == 3)
{
for (var x = 0; x < wdt; x++)
{
for (var y = water_level; y < hgt; y++)
{
// TODO: wait for background pixel setting.
}
}
}
return;
}
/*-- Nodes and Connections --*/
public func FindVeinNodes(proplist map, int size, nr_nodes)
{
var wdt = map.Wdt;
var hgt = map.Hgt;
// Prepare a mask out of the map to search for nodes.
var mask = map->CreateLayer();
mask->Draw("Rock");
// Array for the nodes and definition for a minimum distance.
var nodes = [];
var node_dist = 8;
// The first five nodes are at the bottom of the map and are not to be connected.
for (var i = 0; i < 5; i++)
{
var node = {};
if (!mask->FindPosition(node, "Rock", [4, hgt - 10, wdt - 8, 6]))
continue;
mask->Draw("Tunnel", {Algo = MAPALGO_Ellipsis, X = node.X, Y = node.Y, Wdt = node_dist, Hgt = node_dist});
node.is_gem = true;
node.tunnels = [];
PushBack(nodes, node);
}
// The other nodes are scattered randomly in the lower areas
for (var i = 5; i < nr_nodes; i++)
{
var node = {};
if (!mask->FindPosition(node, "Rock", [4, hgt - size + 4, wdt - 8, size - 18]))
continue;
mask->Draw("Tunnel", {Algo = MAPALGO_Ellipsis, X = node.X, Y = node.Y, Wdt = node_dist, Hgt = node_dist});
node.tunnels = [];
PushBack(nodes, node);
}
return nodes;
}
public func FindNodeConnections(array nodes, int max_length)
{
var connections = [];
for (var node in nodes)
node.conn_count = 0;
for (var i = 0; i < GetLength(nodes) - 1; i++)
{
var from_node = nodes[i];
for (var j = i + 1; j < GetLength(nodes); j++)
{
var to_node = nodes[j];
// Check for the maximum connections per cave.
if (from_node.conn_count >= RandomX(3,4) || to_node.conn_count >= RandomX(3,4))
continue;
// Check for two gem nodes which may not connect.
if (from_node.is_gem && to_node.is_gem)
continue;
// Gem nodes may have at most one connection.
if ((from_node.is_gem && from_node.conn_count >= 1) || (to_node.is_gem && to_node.conn_count >= 1))
continue;
// Cave line parameters.
var fx = from_node.X;
var fy = from_node.Y;
var tx = to_node.X;
var ty = to_node.Y;
// Check for the maximum line distance.
if (Distance(fx, fy, tx, ty) > max_length)
continue;
// Check for overlap in existing connections.
var has_overlap = false;
for (var line in connections)
if (IsLineOverlap(fx, fy, tx, ty, line.Op.X[0], line.Op.Y[0], line.Op.X[1], line.Op.Y[1]))
{
has_overlap = true;
break;
}
if (has_overlap)
continue;
var tunnel_width = 2;
// Change tunnel width if not connecting to gem node.
if (!from_node.is_gem && !to_node.is_gem)
{
if (!Random(4))
tunnel_width = 3;
if (!Random(4))
tunnel_width = 1;
}
var tunnel = {Algo = MAPALGO_Polygon, X = [fx, tx], Y = [fy, ty], Wdt = tunnel_width, Open = 1, Empty = 1};
tunnel = {Algo = MAPALGO_Turbulence, Amplitude = 5, Scale = 3, Seed = Random(65536), Op = tunnel};
PushBack(connections, tunnel);
PushBack(from_node.tunnels, tunnel);
PushBack(to_node.tunnels, tunnel);
nodes[i].conn_count++;
nodes[j].conn_count++;
}
}
return connections;
}