openclonk/planet/Tutorials.ocf/Tutorial05.ocs/Script.c

386 lines
10 KiB
C

/**
Tutorial 05: Robber's Cave
Author: Maikel
Fight your way through a cave with robbers on the way to a fiendly village.
*/
static guide; // guide object.
protected func Initialize()
{
// Tutorial goal.
var goal = CreateObject(Goal_Tutorial);
goal.Name = "$MsgGoalName$";
goal.Description = "$MsgGoalDescription$";
// No power need for the elevator.
CreateObject(Rule_NoPowerNeed);
// Place objects in different sections.
InitBeforeCave();
InitCave();
InitAfterCave();
InitVegetation();
InitAnimals();
InitAI();
// Dialogue options -> repeat round.
SetNextMission("Tutorials.ocf\\Tutorial05.ocs", "$MsgRepeatRound$", "$MsgRepeatRoundDesc$");
return;
}
// Gamecall from goals, set next mission.
protected func OnGoalsFulfilled()
{
// Achievement: Tutorial completed.
GainScenarioAchievement("TutorialCompleted", 3);
// Dialogue options -> next round.
SetNextMission("Tutorials.ocf\\Tutorial07.ocs", "$MsgNextTutorial$", "$MsgNextTutorialDesc$");
// Normal scenario ending by goal library.
return false;
}
private func InitBeforeCave()
{
// Wooden cabin ruin for the old man.
CreateObjectAbove(Ruin_WoodenCabin, 252, 384)->MakeInvincible();
// A small forest at the start.
Tree_Deciduous->Place(7, Rectangle(0, 260, 100, 160));
Tree_Coniferous2->Place(2, Rectangle(0, 260, 100, 160));
Tree_Coniferous2->Place(2, Rectangle(0, 260, 100, 160));
// A grain field near the wooden cabin.
Wheat->Place(24, Rectangle(100, 260, 100, 160));
CreateObjectAbove(StrawMan, 140, 384);
CreateObjectAbove(StrawMan, 170, 384);
// Create door to block cave entrance until destroyed straw men.
var door = CreateObject(StoneDoor, 396, 362);
door->MakeInvincible();
door.Visibility = VIS_None;
return;
}
private func InitCave()
{
// A chest with javelins to kill the first robber.
var chest = CreateObjectAbove(Chest, 544, 480);
chest->CreateContents(Javelin, 2);
// Some torches to see the enemies.
CreateObjectAbove(Torch, 500, 474)->AttachToWall(true);
CreateObjectAbove(Torch, 506, 628)->AttachToWall(true);
CreateObjectAbove(Torch, 728, 696)->AttachToWall(true);
// A hidden chest with a musket.
var chest = CreateObjectAbove(Chest, 10, 526);
var musket = chest->CreateContents(Musket);
musket->CreateContents(LeadShot);
return;
}
private func InitAfterCave()
{
// A small forest at the start.
Tree_Deciduous->Place(10, Rectangle(640, 200, 140, 160));
Tree_Coniferous2->Place(3, Rectangle(640, 200, 140, 160));
Tree_Coniferous3->Place(5, Rectangle(640, 200, 140, 160));
// Elevator with a shaft down and the case already down.
var elevator = CreateObjectAbove(Elevator, 836, 264);
elevator->SetDir(DIR_Right);
elevator->CreateShaft(440);
var case = elevator->GetCase();
case->SetPosition(case->GetX(), 640);
elevator->MakeInvincible();
case->MakeInvincible();
// Create a column at map end.
CreateObjectAbove(Column, 960, 264);
return;
}
private func InitVegetation()
{
Grass->Place(85);
PlaceObjects(Loam, 10 + Random(5), "Earth");
PlaceObjects(Rock, 25 + Random(5), "Earth");
Branch->Place(28);
Mushroom->Place(24);
Flower->Place(10);
Fern->Place(10);
Seaweed->Place(8);
Coral->Place(4);
return;
}
private func InitAnimals()
{
// Some butterflies as atmosphere.
Butterfly->Place(25);
// Fish in the body of water.
Fish->Place(12);
return;
}
// Initializes the AI: which is all defined in System.ocg
private func InitAI()
{
// An old man without home npc to point to robbers.
var npc_homeless = CreateObjectAbove(Clonk, 220, 384);
npc_homeless->SetName("Dirk");
npc_homeless->SetObjectLayer(npc_homeless);
npc_homeless->SetDir(DIR_Left);
npc_homeless->SetDialogue("Homeless");
npc_homeless->SetAlternativeSkin("Beggar");
// Village head.
var npc_head = CreateObjectAbove(Clonk, 20, 384);
npc_head->SetName("Archibald");
npc_head->SetObjectLayer(npc_head);
npc_head->SetDir(DIR_Left);
npc_head->SetDialogue("VillageHead");
npc_head->SetAlternativeSkin("Sage");
// Robbers.
var robber1 = CreateObjectAbove(Clonk, 490, 524);
robber1->CreateContents(Bow)->CreateContents(Arrow);
robber1->CreateContents(Arrow);
robber1->CreateContents(Shield);
AI->AddAI(robber1);
AI->SetHome(robber1, 490, 514, DIR_Left);
AI->SetGuardRange(robber1, 300, 400, 400, 200);
AI->SetAllyAlertRange(robber1, 60);
robber1->SetDir(DIR_Left);
robber1->SetAlternativeSkin("Youngster");
robber1.first_robber = true;
var robber2 = CreateObjectAbove(Clonk, 470, 644);
robber2->CreateContents(Sword);
robber2->CreateContents(Shield);
robber2->CreateContents(Bread);
AI->AddAI(robber2);
AI->SetHome(robber2, 470, 634, DIR_Right);
AI->SetGuardRange(robber2, 300, 560, 400, 200);
AI->SetAllyAlertRange(robber2, 60);
robber2->SetDir(DIR_Right);
robber2->SetAlternativeSkin("YoungsterBlond");
robber2.second_robber = true;
var robber3 = CreateObjectAbove(Clonk, 800, 706);
robber3->CreateContents(Javelin, 3);
robber3->CreateContents(Shield);
AI->AddAI(robber3);
AI->SetHome(robber3, 800, 696, DIR_Left);
AI->SetGuardRange(robber3, 600, 600, 400, 200);
AI->SetAllyAlertRange(robber3, 60);
robber3->SetDir(DIR_Left);
robber3.last_robber = true;
var robber4 = CreateObjectAbove(Clonk, 830, 706);
robber4->CreateContents(Sword);
robber4->CreateContents(Shield);
AI->AddAI(robber4);
AI->SetHome(robber4, 830, 696, DIR_Left);
AI->SetGuardRange(robber4, 600, 600, 400, 200);
AI->SetAllyAlertRange(robber4, 60);
robber4->SetDir(DIR_Left);
robber4.last_robber = true;
return;
}
/*-- Player Handling --*/
protected func InitializePlayer(int plr)
{
// Position player's clonk.
var clonk = GetCrew(plr);
clonk->SetPosition(10, 374);
clonk->CreateContents(Shovel);
var effect = AddEffect("ClonkRestore", clonk, 100, 10);
effect.to_x = 300;
effect.to_y = 374;
// Standard player zoom for tutorials.
SetPlayerViewLock(plr, true);
SetPlayerZoomByViewRange(plr, 400, nil, PLRZOOM_Direct | PLRZOOM_Set | PLRZOOM_LimitMax);
// Start the intro sequence.
StartSequence("Intro", 0, plr);
// Create tutorial guide and control effect.
guide = CreateObject(TutorialGuide, 0, 0, plr);
guide->HideGuide();
var effect = AddEffect("TutorialTalkedToVillageHead", nil, 100, 5);
effect.plr = plr;
return;
}
/*-- Guide Messages --*/
public func OnIntroSequenceFinished()
{
guide->AddGuideMessage("$MsgTutorialTalkToHead$");
guide->ShowGuide();
guide->ShowGuideMessage();
return;
}
public func OnHasTalkedToVillageHead()
{
RemoveEffect("TutorialTalkedToVillageHead");
return;
}
global func FxTutorialTalkedToVillageHeadTimer()
{
return FX_OK;
}
global func FxTutorialTalkedToVillageHeadStop(object target, proplist effect, int reason, bool temp)
{
if (temp)
return FX_OK;
var use = GetPlayerControlAssignment(effect.plr, CON_Use, true);
guide->AddGuideMessage(Format("$MsgTutorialNotHelpful$", use));
guide->ShowGuideMessage();
var new_effect = AddEffect("TutorialDestroyedStrawMen", nil, 100, 5);
new_effect.plr = effect.plr;
return FX_OK;
}
global func FxTutorialDestroyedStrawMenTimer(object target, proplist effect)
{
if (!FindObject(Find_ID(StrawMan)))
{
// Remove door blocking cave entrance.
RemoveAll(Find_ID(StoneDoor));
guide->AddGuideMessage("$MsgTutorialFirstEnemy$");
guide->ShowGuideMessage();
var new_effect = AddEffect("TutorialKilledFirstRobber", nil, 100, 5);
new_effect.plr = effect.plr;
return FX_Execute_Kill;
}
return FX_OK;
}
public func OnClonkRestore()
{
if (FindObject(Find_OCF(OCF_CrewMember), Find_Property("first_robber")))
{
guide->AddGuideMessage("$MsgTutorialOnRespawn$");
guide->ShowGuideMessage();
}
return;
}
global func FxTutorialKilledFirstRobberTimer(object target, proplist effect)
{
if (!FindObject(Find_OCF(OCF_CrewMember), Find_Property("first_robber")))
{
// Show previous guide message about alternative way to kill the first robber.
if (guide->GetMessageCount() <= 3)
guide->AddGuideMessage("$MsgTutorialOnRespawn$");
guide->AddGuideMessage("$MsgTutorialKilledFirst$");
guide->ShowGuideMessage();
var new_effect = AddEffect("TutorialKilledSecondRobber", nil, 100, 5);
new_effect.plr = effect.plr;
return FX_Execute_Kill;
}
return FX_OK;
}
global func FxTutorialKilledSecondRobberTimer(object target, proplist effect)
{
if (!FindObject(Find_OCF(OCF_CrewMember), Find_Property("second_robber")))
{
var use = GetPlayerControlAssignment(effect.plr, CON_Use, true);
guide->AddGuideMessage(Format("$MsgTutorialKilledSecond$", use));
guide->ShowGuideMessage();
var new_effect = AddEffect("TutorialKilledLastRobbers", nil, 100, 5);
new_effect.plr = effect.plr;
return FX_Execute_Kill;
}
return FX_OK;
}
global func FxTutorialKilledLastRobbersTimer(object target, proplist effect)
{
if (!FindObject(Find_OCF(OCF_CrewMember), Find_Property("last_robber")))
{
// Start the intro sequence.
guide->HideGuide();
StartSequence("Outro", 0, effect.plr);
return FX_Execute_Kill;
}
return FX_OK;
}
public func ShowLastGuideMessage()
{
guide->AddGuideMessage("$MsgTutorialCompleted$");
guide->ShowGuideMessage();
guide->ShowGuide();
return;
}
protected func OnGuideMessageShown(int plr, int index)
{
// Show the village head.
if (index == 0)
{
TutArrowShowTarget(Dialogue->FindByName("VillageHead")->GetDialogueTarget());
}
// Show the strawmen.
if (index == 1)
{
for (var strawman in FindObjects(Find_ID(StrawMan)))
TutArrowShowTarget(strawman);
}
// Show the first robber.
if (index == 2)
TutArrowShowTarget(FindObject(Find_OCF(OCF_CrewMember), Find_Property("first_robber")));
// Show path to dig and chest.
if (index == 3)
{
TutArrowShowTarget(FindObject(Find_ID(Chest), Sort_Distance(540, 480)));
TutArrowShowPos(490, 420, 135);
}
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;
// Respawn new clonk.
var plr = target->GetOwner();
var clonk = CreateObject(Clonk, 0, 0, plr);
clonk->GrabObjectInfo(target);
Rule_BaseRespawn->TransferInventory(target, clonk);
SetCursor(plr, clonk);
clonk->DoEnergy(100000);
restorer->SetRestoreObject(clonk, nil, to_x, to_y, 0, "ClonkRestore");
GameCall("OnClonkRestore", clonk);
}
return FX_OK;
}