openclonk/planet/Worlds.ocf/Clonkomotive.ocs/Script.c

316 lines
8.8 KiB
C

/**
Clonkomotive
Drive the train from left to right in tough terrain.
@author Pyrit, Maikel
*/
// Whether the intro has been initialized.
static intro_init;
// Bat cave [x, y] coordinates.
static bat_cave;
public func Initialize()
{
// Show wealth in HUD.
GUI_Controller->ShowWealth();
// Rules: team account and buying at flagpole.
CreateObject(Rule_TeamAccount);
CreateObject(Rule_BuyAtFlagpole);
// Create goal: transport train to other village.
var goal = CreateObject(Goal_LocomotiveTransport);
goal->SetGoalRect(LandscapeWidth() - 180, FindHeight(LandscapeWidth() - 240) - 200, 180, 200);
// Rescale map coordinates.
var map_zoom = GetScenarioVal("MapZoom", "Landscape");
bat_cave[0] = bat_cave[0] * map_zoom + map_zoom / 2;
bat_cave[1] = bat_cave[1] * map_zoom + map_zoom / 2;
// Initialize different parts of the scenario.
InitEnvironment(SCENPAR_MapSize, SCENPAR_Difficulty);
InitVegetation(SCENPAR_MapSize, SCENPAR_Difficulty);
InitAnimals(SCENPAR_MapSize, SCENPAR_Difficulty);
InitMaterial(4 - SCENPAR_Difficulty);
InitCity();
return;
}
protected func OnGoalsFulfilled()
{
// Give the remaining players their achievement.
GainScenarioAchievement("Done", BoundBy(SCENPAR_Difficulty, 1, 3));
return false;
}
/*-- Player Initialization --*/
public func InitializePlayer(plr)
{
// Move clonks to location and give them a shovel.
var index = 0, crew;
while (crew = GetCrew(plr, index))
{
var x = 60;
var y = FindHeight(x);
crew->SetPosition(x, y - 12);
// First clonk can construct, others can chop.
crew->CreateContents(Shovel);
if (index == 0)
crew->CreateContents(Hammer);
else
crew->CreateContents(Axe);
index++;
}
// Give the player its knowledge.
GivePlayerBasicKnowledge(plr);
GivePlayerPumpingKnowledge(plr);
GivePlayerWeaponryKnowledge(plr);
GivePlayerAdvancedKnowledge(plr);
GivePlayerFarmingKnowledge(plr);
GivePlayerAirKnowledge(plr);
GivePlayerSpecificKnowledge(plr, [WoodenBridge]);
RemovePlayerSpecificKnowledge(plr, [WallKit]);
// Give the player the elementary base materials and some tools.
GivePlayerElementaryBaseMaterial(plr);
GivePlayerToolsBaseMaterial(plr);
// Take over small village at the start of the map.
for (var building in FindObjects(Find_Or(Find_ID(Sawmill), Find_ID(WindGenerator), Find_ID(ToolsWorkshop))))
{
building->SetOwner(plr);
}
// Set zoom range.
SetPlayerZoomByViewRange(plr, 600, nil, PLRZOOM_Direct | PLRZOOM_LimitMax);
SetPlayerViewLock(plr, true);
// Initialize the intro sequence if not yet started.
if (!intro_init)
{
StartSequence("Intro", 0);
intro_init = true;
}
return;
}
/*-- Scenario Initialization --*/
private func InitEnvironment(int map_size, int difficulty)
{
// Sky scrolling.
SetSkyParallax(0, 75, 75, nil, nil, nil, -200);
// Set time of day to evening and create some clouds and celestials.
Cloud->Place(16);
Cloud->SetPrecipitation("Water", 30 * difficulty);
if (difficulty == 3)
{
Cloud->SetLightning(20);
Meteor->SetChance(4);
}
Time->Init();
Time->SetTime(60 * 12);
Time->SetCycleSpeed(20);
// Place stars manually to not overlap with mountain background and fit map size.
Time->PlaceStars(LandscapeWidth(), 180);
// Add an effect which controls the rock fall in this round.
AddEffect("ScenarioRockFall", nil, 100, 36, nil, nil, difficulty);
return;
}
// Callback from time controller: this scenario has no celestials.
public func HasNoCelestials() { return true; }
private func InitVegetation(int map_size, int difficulty)
{
var wdt = LandscapeWidth();
var hgt = LandscapeHeight();
// Trees.
Tree_Deciduous->Place(30 + 10 * map_size, Rectangle(wdt / 6, 0, 4 * wdt / 6, 4 * hgt / 9));
Tree_Coniferous2->Place(4 + 2 * map_size, Rectangle(wdt / 6, 0, 4 * wdt / 6, 4 * hgt / 9));
Tree_Coniferous3->Place(4 + 2 * map_size, Rectangle(wdt / 6, 0, 4 * wdt / 6, 4 * hgt / 9));
LargeCaveMushroom->Place(12 + 2 * map_size, Rectangle(wdt / 6, 4 * hgt / 9, 4 * wdt / 6, 5 * hgt / 9));
// Smaller vegetation.
Grass->Place(100);
Wheat->Place(10);
SproutBerryBush->Place(3);
Flower->Place(20 + 4 * map_size);
Mushroom->Place(20 + 4 * map_size);
Trunk->Place(6 + 2 * map_size);
Fern->Place(20 + 4 * map_size);
Branch->Place(20 + 4 * map_size);
Cotton->Place(8, Rectangle(wdt - 600, 0, 600, hgt));
// Under water vegetation.
Seaweed->Place(15 + 5 * map_size);
Coral->Place(10 + 2 * map_size);
// Some objects in the earth.
PlaceObjects(Rock, 30 + 20 * map_size + Random(10),"Earth");
PlaceObjects(Firestone, 30 + 10 * map_size + Random(5), "Earth");
PlaceObjects(Loam, 4 + 2 * map_size, "Earth");
return;
}
private func InitAnimals(int map_size, int difficulty)
{
// Place some fish, sharks and piranhas.
Fish->Place(20 + 4 * map_size);
Piranha->Place((10 + 4 * map_size) * (difficulty - 1));
Shark->Place(difficulty - 1);
// Some insects: zaps, mosquitos, butterflies, fireflies.
Zaphive->Place(2 + 4 * difficulty);
Mosquito->Place(4 + 2 * map_size);
Butterfly->Place(16 + 4 * map_size);
for (var cnt = 0; cnt < 4 + map_size; cnt++)
{
var tree = FindObject(Find_ID(Tree_Deciduous), Sort_Random());
Firefly->SpawnSwarm(tree, RandomX(6, 12));
}
// Bats in the bat cave.
Bat->Place(4 + 2 * difficulty, Rectangle(bat_cave[0] - 20, bat_cave[1] - 20, 40, 40));
return;
}
private func InitMaterial(int amount)
{
// Train on the start cliff.
var train = CreateObjectAbove(Locomotive, 60, FindHeight(60) - 2);
train->SetDir(DIR_Right);
// Lorry with materials on normal and hard.
if (amount >= 2)
{
var lorry = CreateObject(Lorry, 120, FindHeight(120) - 2);
lorry->SetPosition(train->GetX()-25, train->GetY());
lorry->CreateContents(Metal, 4);
lorry->CreateContents(Wood, 4);
}
// Small settlement on normal.
if (amount >= 3)
{
CreateObjectAbove(WindGenerator, 180, FindHeight(180));
CreateObjectAbove(Sawmill, 210, FindHeight(210));
CreateObjectAbove(ToolsWorkshop, 140, FindHeight(140));
}
// Always a catapult on the second cliff.
CreateObjectAbove(Catapult, 880, FindHeight(880));
// A lorry with rewards in the bat cave.
var lorry = CreateObjectAbove(Lorry, bat_cave[0], bat_cave[1]);
lorry->CreateContents(Pickaxe, amount);
lorry->CreateContents(WallKit, 2 * amount);
lorry->CreateContents(Wood, 2 * amount);
return;
}
private func InitCity()
{
var wdt = LandscapeWidth();
CreateObjectAbove(WoodenCabin, wdt - 60, FindHeight(wdt - 60))->MakeInvincible();
CreateObjectAbove(WoodenCabin, wdt - 190, FindHeight(wdt - 190))->MakeInvincible();
CreateObjectAbove(Windmill, wdt - 120, FindHeight(wdt - 120))->MakeInvincible();
return;
}
/*-- Rockfall --*/
global func FxScenarioRockFallStart(object target, proplist effect, int temp, int diff)
{
if (temp)
return FX_OK;
effect.difficulty = diff;
// Find the range for the rockfall to fall in (the last valley).
var range_end;
for (var x = LandscapeWidth(); x > 0; x -= 5)
{
var y = FindHeight(x) + 5;
if (GetMaterial(x, y) == Material("Water"))
{
range_end = x;
break;
}
}
var end_y = FindHeight(range_end) + 5;
var range_start = range_end;
while (GetMaterial(range_start, end_y) == Material("Water") && range_start > 0)
range_start--;
while (GetMaterial(range_end, end_y) == Material("Water") && range_end < LandscapeWidth())
range_end++;
effect.range = [range_start + 60, range_end - 60];
return FX_OK;
}
global func FxScenarioRockFallTimer(object target, proplist effect, int time)
{
if (Random(600 / effect.difficulty**2) || GetEffect("LaunchRockFall"))
return FX_OK;
// Launch rock fall.
var rockfall_effect = AddEffect("LaunchRockFall", nil, 100, 1, nil);
rockfall_effect.range = effect.range;
rockfall_effect.duration = 36 * (10 + 5 * effect.difficulty);
return FX_OK;
}
global func FxLaunchRockFallStart(object target, proplist effect, int temp)
{
if (temp)
return FX_OK;
Sound("Environment::Disasters::Earthquake", true, 100, nil, 1);
return FX_OK;
}
global func FxLaunchRockFallTimer(object target, proplist effect, int time)
{
// Kill the effect once its duration is reached.
if (time > effect.duration)
return FX_Execute_Kill;
var crumbs =
{
Size = PV_Random(1, 3),
Phase = PV_Random(0, 2),
Alpha = PV_KeyFrames(0, 0, 255, 900, 255, 1000, 0),
CollisionVertex = 500,
OnCollision = PC_Bounce(20),
ForceY = PV_Gravity(50),
ForceX = PV_Random(-5, 5, 15),
Rotation = PV_Direction(PV_Random(750, 1250)),
Attach = ATTACH_Front
};
if (!Random(3))
CreateParticle("RockFragment", RandomX(effect.range[0], effect.range[1]), 5, PV_Random(-5, 5), 0, 36 * 5, crumbs, 1);
if (time > 36 * 3 && !Random(10))
CastObjects(RockFragment, 1, 10, RandomX(effect.range[0], effect.range[1]), 5, 180, 90);
return FX_OK;
}
global func FxLaunchRockFallStop(object target, proplist effect, int reason, bool temp)
{
if (temp)
return FX_OK;
// Stop sound.
Sound("Environment::Disasters::Earthquake", true, 100, nil, -1);
Sound("Environment::Disasters::EarthquakeEnd",true);
return FX_OK;
}