forked from Mirrors/openclonk
379 lines
10 KiB
C
379 lines
10 KiB
C
/**
|
|
Tutorial 07: Airborne Mining
|
|
Author: Maikel
|
|
|
|
Mine valuable gems from a ruby stalactite.
|
|
|
|
Following controls and concepts are explained:
|
|
* Airship controls
|
|
* Rope ladders
|
|
* Gem materials
|
|
* Selling at the flagpole
|
|
*/
|
|
|
|
static guide; // guide object
|
|
|
|
protected func Initialize()
|
|
{
|
|
// Tutorial goal.
|
|
var goal = CreateObject(Goal_Tutorial);
|
|
goal.Name = "$MsgGoalName$";
|
|
goal.Description = "$MsgGoalDescription$";
|
|
CreateObject(Rule_NoPowerNeed);
|
|
|
|
// Place objects in different sections.
|
|
InitMountain();
|
|
InitRubyIsland();
|
|
InitIslands();
|
|
InitVegetation();
|
|
InitAnimals();
|
|
InitAI();
|
|
|
|
// Environment.
|
|
Time->Init();
|
|
Time->SetTime(22 * 60 + 30);
|
|
Time->SetCycleSpeed(0);
|
|
|
|
// Wealth shown at all time
|
|
GUI_Controller->ShowWealth();
|
|
|
|
// Dialogue options -> repeat round.
|
|
SetNextScenario("Tutorials.ocf\\Tutorial07.ocs", "$MsgRepeatRound$", "$MsgRepeatRoundDesc$");
|
|
return;
|
|
}
|
|
|
|
// Gamecall from goals, set next mission.
|
|
protected func OnGoalsFulfilled()
|
|
{
|
|
// Achievement: Tutorial completed.
|
|
GainScenarioAchievement("TutorialCompleted", 3);
|
|
// Dialogue options -> next round.
|
|
SetNextScenario("Tutorials.ocf\\Tutorial08.ocs", "$MsgNextTutorial$", "$MsgNextTutorialDesc$");
|
|
// Normal scenario ending by goal library.
|
|
return false;
|
|
}
|
|
|
|
private func InitMountain()
|
|
{
|
|
// Shipyard with lantern and flagpole.
|
|
CreateObjectAbove(Flagpole, 55, 504)->MakeInvincible();
|
|
var shipyard = CreateObjectAbove(Shipyard, 90, 504);
|
|
shipyard->MakeInvincible();
|
|
var lamp = shipyard->CreateContents(Lantern);
|
|
lamp->TurnOn();
|
|
// Indestructible airship.
|
|
var airship = CreateObjectAbove(Airship, 120, 504);
|
|
airship->MakeInvincible();
|
|
// Lorry with dynamite and tools.
|
|
var lorry = CreateObjectAbove(Lorry, 120, 498);
|
|
for (var cnt = 0; cnt < 4; cnt ++)
|
|
{
|
|
var dynamite_box = lorry->CreateContents(DynamiteBox);
|
|
dynamite_box->SetDynamiteCount(3);
|
|
}
|
|
lorry->CreateContents(Pickaxe);
|
|
lorry->CreateContents(TeleGlove);
|
|
lorry->MakeInvincible();
|
|
return;
|
|
}
|
|
|
|
private func InitRubyIsland()
|
|
{
|
|
// Two rope ladders give access to the ruby stalactite.
|
|
CreateObjectAbove(Ropeladder, 740, 160)->Unroll(-1, COMD_Up);
|
|
CreateObjectAbove(Ropeladder, 780, 160)->Unroll(-1, COMD_Up);
|
|
return;
|
|
}
|
|
|
|
private func InitIslands()
|
|
{
|
|
// Armory on the lower island with weaponry to kill bats.
|
|
CreateObjectAbove(WindGenerator, 700, 648)->MakeInvincible();
|
|
var armory = CreateObjectAbove(Armory, 654, 648);
|
|
armory->CreateContents(Wood, 10);
|
|
armory->CreateContents(Metal, 5);
|
|
armory->MakeInvincible();
|
|
// Flowers on two of the islands.
|
|
Flower->Place(12, Rectangle(200, 100, 300, 200));
|
|
Flower->Place(8, Rectangle(300, 300, 200, 200));
|
|
return;
|
|
}
|
|
|
|
// Vegetation throughout the scenario.
|
|
private func InitVegetation()
|
|
{
|
|
PlaceGrass(85);
|
|
PlaceObjects(Firestone, 25 + Random(5), "Earth");
|
|
PlaceObjects(Loam, 15 + Random(5), "Earth");
|
|
Mushroom->Place(10);
|
|
Branch->Place(40);
|
|
Trunk->Place(10);
|
|
Tree_Deciduous->Place(40);
|
|
return;
|
|
}
|
|
|
|
private func InitAnimals()
|
|
{
|
|
// The sky islands are a natural place for bats to hide.
|
|
var bats = Bat->Place(6);
|
|
// Make the bats a bit weaker so that they are killed with a single arrow.
|
|
for (var bat in bats)
|
|
{
|
|
bat.MaxEnergy = 7000;
|
|
bat->DoEnergy(bat.MaxEnergy - bat->GetEnergy());
|
|
}
|
|
// Some fireflies attracted to trees on two islands.
|
|
Firefly->Place(2, nil, Rectangle(200, 100, 300, 200));
|
|
Firefly->Place(2, nil, Rectangle(300, 300, 200, 200));
|
|
return;
|
|
}
|
|
|
|
// Initializes the AI: which is all defined in System.ocg
|
|
private func InitAI()
|
|
{
|
|
// A pilot npc for explaining the sky islands.
|
|
var npc_pilot = CreateObjectAbove(Clonk, 108, 496);
|
|
npc_pilot->SetColor(0xffff00);
|
|
npc_pilot->SetName("Chris");
|
|
npc_pilot->SetObjectLayer(npc_pilot);
|
|
npc_pilot->SetSkin(2);
|
|
npc_pilot->SetDir(DIR_Right);
|
|
npc_pilot->SetDialogue("Pilot", true);
|
|
return;
|
|
}
|
|
|
|
/*-- Player Handling --*/
|
|
|
|
protected func InitializePlayer(int plr)
|
|
{
|
|
// Position player's clonk.
|
|
var clonk = GetCrew(plr, 0);
|
|
clonk->SetPosition(60, 494);
|
|
var effect = AddEffect("ClonkRestore", clonk, 100, 10);
|
|
effect.to_x = 60;
|
|
effect.to_y = 494;
|
|
|
|
// Items for the clonk.
|
|
clonk->CreateContents(Shovel);
|
|
var dynamite_box = clonk->CreateContents(DynamiteBox);
|
|
dynamite_box->SetDynamiteCount(3);
|
|
|
|
// Take ownership of the flags.
|
|
for (var flag in FindObjects(Find_Or(Find_Func("IsFlagpole"), Find_ID(WindGenerator))))
|
|
flag->SetOwner(plr);
|
|
|
|
// Knowledge to construct bow and arrow.
|
|
SetPlrKnowledge(plr, Bow);
|
|
SetPlrKnowledge(plr, Arrow);
|
|
|
|
// Add an interaction to call the airship.
|
|
Helper_CallAirship->Create(clonk, Dialogue->FindByName("Pilot")->GetDialogueTarget(), FindObject(Find_ID(Airship)));
|
|
|
|
// Add an effect to the clonk to track the goal.
|
|
var track_goal = AddEffect("TrackGoal", nil, 100, 2);
|
|
track_goal.plr = plr;
|
|
|
|
// Standard player zoom for tutorials, player is not allowed to zoom in/out.
|
|
SetPlayerViewLock(plr, true);
|
|
SetPlayerZoomByViewRange(plr, 400, nil, PLRZOOM_Direct | PLRZOOM_LimitMax);
|
|
|
|
// Determine player movement keys.
|
|
var interact_prev = GetPlayerControlAssignment(plr, CON_InteractNext_Right, true, true);
|
|
var interact_next = GetPlayerControlAssignment(plr, CON_InteractNext_Left, true, true);
|
|
var interact_cycle = GetPlayerControlAssignment(plr, CON_InteractNext_CycleObject, true, true);
|
|
var interact_cancel = GetPlayerControlAssignment(plr, CON_InteractNext_Stop, true, true);
|
|
|
|
// Create tutorial guide, add messages, show first.
|
|
guide = CreateObjectAbove(TutorialGuide, 0, 0, plr);
|
|
guide->AddGuideMessage(Format("$MsgTutorialTalkToPilot$", interact_prev, interact_next, interact_cycle, interact_cancel));
|
|
guide->ShowGuideMessage();
|
|
var effect = AddEffect("TutorialTalkedToPilot", nil, 100, 2);
|
|
effect.plr = plr;
|
|
return;
|
|
}
|
|
|
|
|
|
/*-- Intro, Tutorial Goal & Outro --*/
|
|
|
|
global func FxTrackGoalTimer(object target, proplist effect, int time)
|
|
{
|
|
if (GetWealth(effect.plr) >= 250)
|
|
{
|
|
var outro = AddEffect("GoalOutro", target, 100, 5);
|
|
outro.plr = effect.plr;
|
|
return FX_Execute_Kill;
|
|
}
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxGoalOutroStart(object target, proplist effect, int temp)
|
|
{
|
|
if (temp)
|
|
return FX_OK;
|
|
// Show guide message congratulating.
|
|
guide->AddGuideMessage("$MsgTutorialCompleted$");
|
|
guide->ShowGuideMessage();
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxGoalOutroTimer(object target, proplist effect, int time)
|
|
{
|
|
if (time >= 60)
|
|
{
|
|
var goal = FindObject(Find_ID(Goal_Tutorial));
|
|
goal->Fulfill();
|
|
return FX_Execute_Kill;
|
|
}
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxGoalOutroStop(object target, proplist effect, int reason, bool temp)
|
|
{
|
|
if (temp)
|
|
return FX_OK;
|
|
return FX_OK;
|
|
}
|
|
|
|
|
|
/*-- Guide Messages --*/
|
|
|
|
public func OnHasTalkedToPilot()
|
|
{
|
|
RemoveEffect("TutorialTalkedToPilot");
|
|
return;
|
|
}
|
|
|
|
global func FxTutorialTalkedToPilotTimer()
|
|
{
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxTutorialTalkedToPilotStop(object target, proplist effect, int reason, bool temp)
|
|
{
|
|
if (temp)
|
|
return FX_OK;
|
|
|
|
// Determine player movement keys.
|
|
var left = GetPlayerControlAssignment(effect.plr, CON_Left, true, true);
|
|
var right = GetPlayerControlAssignment(effect.plr, CON_Right, true, true);
|
|
var up = GetPlayerControlAssignment(effect.plr, CON_Up, true, true);
|
|
var down = GetPlayerControlAssignment(effect.plr, CON_Down, true, true);
|
|
var interact = GetPlayerControlAssignment(effect.plr, CON_Interact, true, true);
|
|
var control_keys = Format("[%s] [%s] [%s] [%s]", up, left, down, right);
|
|
|
|
guide->AddGuideMessage(Format("$MsgTutorialFindRubies$", interact, control_keys));
|
|
guide->ShowGuideMessage();
|
|
var new_effect = AddEffect("TutorialFoundStalactite", nil, 100, 2);
|
|
new_effect.plr = effect.plr;
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxTutorialFoundStalactiteTimer(object target, proplist effect, int timer)
|
|
{
|
|
var clonk = FindObject(Find_ID(Clonk), Find_Distance(150, 780, 200));
|
|
if (clonk)
|
|
{
|
|
guide->AddGuideMessage(Format("$MsgTutorialParkAirship$"));
|
|
guide->ShowGuideMessage();
|
|
AddEffect("TutorialAirshipParked", nil, 100, 2);
|
|
return FX_Execute_Kill;
|
|
}
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxTutorialAirshipParkedTimer(object target, proplist effect, int timer)
|
|
{
|
|
var clonk = FindObject(Find_ID(Clonk), Find_Distance(30, 688, 200));
|
|
var airship = FindObject(Find_ID(Airship), Find_Distance(30, 688, 200));
|
|
if (clonk && airship)
|
|
{
|
|
var plr = clonk->GetOwner();
|
|
var left = GetPlayerControlAssignment(plr, CON_Left, true, true);
|
|
var right = GetPlayerControlAssignment(plr, CON_Right, true, true);
|
|
guide->AddGuideMessage(Format("$MsgTutorialLadderJump$", left, right));
|
|
guide->ShowGuideMessage();
|
|
AddEffect("TutorialOnStalactite", nil, 100, 2);
|
|
return FX_Execute_Kill;
|
|
}
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxTutorialOnStalactiteTimer(object target, proplist effect, int timer)
|
|
{
|
|
if (FindObject(Find_ID(Clonk), Find_InRect(810, 150, 24, 72)))
|
|
{
|
|
guide->AddGuideMessage("$MsgTutorialBlastGems$");
|
|
guide->ShowGuideMessage();
|
|
AddEffect("TutorialCollectGems", nil, 100, 2);
|
|
return FX_Execute_Kill;
|
|
}
|
|
return FX_OK;
|
|
}
|
|
|
|
global func FxTutorialCollectGemsTimer(object target, proplist effect, int timer)
|
|
{
|
|
if (FindObject(Find_ID(Ruby)))
|
|
{
|
|
guide->AddGuideMessage("$MsgTutorialCollectGems$");
|
|
guide->ShowGuideMessage();
|
|
return FX_Execute_Kill;
|
|
}
|
|
return FX_OK;
|
|
}
|
|
|
|
protected func OnGuideMessageShown(int plr, int index)
|
|
{
|
|
// Show airship parking space.
|
|
if (index == 2)
|
|
TutArrowShowPos(688, 220, 135);
|
|
// Show dynamite placement location.
|
|
if (index == 4)
|
|
TutArrowShowPos(800, 200, 60);
|
|
return;
|
|
}
|
|
|
|
protected func OnGuideMessageRemoved(int plr, int index)
|
|
{
|
|
TutArrowClear();
|
|
return;
|
|
}
|
|
|
|
|
|
/*-- Clonk restoring --*/
|
|
|
|
global func FxClonkRestoreTimer(object target, proplist effect, int time)
|
|
{
|
|
// Respawn clonk to new location if reached certain position.
|
|
return FX_OK;
|
|
}
|
|
|
|
// Relaunches the clonk, from death or removal.
|
|
global func FxClonkRestoreStop(object target, effect, int reason, bool temporary)
|
|
{
|
|
if (reason == 3 || reason == 4)
|
|
{
|
|
var restorer = CreateObject(ObjectRestorer, 0, 0, NO_OWNER);
|
|
var x = BoundBy(target->GetX(), 0, LandscapeWidth());
|
|
var y = BoundBy(target->GetY(), 0, LandscapeHeight());
|
|
restorer->SetPosition(x, y);
|
|
var to_x = effect.to_x;
|
|
var to_y = effect.to_y;
|
|
var airship = FindObject(Find_ID(Airship));
|
|
if (airship)
|
|
{
|
|
to_x = airship->GetX();
|
|
to_y = airship->GetY();
|
|
}
|
|
// Respawn new clonk.
|
|
var plr = target->GetOwner();
|
|
var clonk = CreateObject(Clonk, 0, 0, plr);
|
|
clonk->GrabObjectInfo(target);
|
|
Rule_Relaunch->TransferInventory(target, clonk);
|
|
SetCursor(plr, clonk);
|
|
clonk->DoEnergy(100000);
|
|
// Add an interaction to call the airship.
|
|
Helper_CallAirship->Create(clonk, Dialogue->FindByName("Pilot")->GetDialogueTarget(), FindObject(Find_ID(Airship)));
|
|
restorer->SetRestoreObject(clonk, nil, to_x, to_y, 0, "ClonkRestore");
|
|
}
|
|
return FX_OK;
|
|
} |