2014-08-14 15:15:12 +00:00
|
|
|
/**
|
|
|
|
Sequence
|
|
|
|
Cutscene to be watched by all players.
|
|
|
|
Start calling global func StartSequence, stop using StopSequence
|
2015-03-01 13:01:26 +00:00
|
|
|
|
2016-07-13 21:18:08 +00:00
|
|
|
Can also be used as a trigger object for UserActions.
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
@author Sven
|
2014-08-14 15:15:12 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
local seq_name;
|
|
|
|
local seq_progress;
|
|
|
|
local started;
|
|
|
|
|
|
|
|
/* Start and stop */
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
public func Start(string name, int progress, ...)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
2015-03-01 13:01:26 +00:00
|
|
|
if (started)
|
|
|
|
Stop();
|
|
|
|
// Force global coordinates for the script execution.
|
|
|
|
SetPosition(0, 0);
|
|
|
|
// Store sequence name and progress.
|
2014-08-14 15:15:12 +00:00
|
|
|
this.seq_name = name;
|
|
|
|
this.seq_progress = progress;
|
2015-03-01 13:01:26 +00:00
|
|
|
// Call init function of this scene - difference to start function is that it is called before any player joins.
|
2014-08-26 14:06:53 +00:00
|
|
|
var fn_init = Format("~%s_Init", seq_name);
|
|
|
|
if (!Call(fn_init, ...))
|
|
|
|
GameCall(fn_init, this, ...);
|
2015-03-01 13:01:26 +00:00
|
|
|
// Join all players: disable player controls and call join player of this scene.
|
|
|
|
for (var i = 0; i < GetPlayerCount(C4PT_User); ++i)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
|
|
|
var plr = GetPlayerByIndex(i, C4PT_User);
|
|
|
|
JoinPlayer(plr);
|
|
|
|
}
|
|
|
|
started = true;
|
2015-03-01 13:01:26 +00:00
|
|
|
// Sound effect.
|
2015-12-13 21:14:55 +00:00
|
|
|
Sound("UI::Ding", true);
|
2015-03-01 13:01:26 +00:00
|
|
|
// Call start function of this scene.
|
2014-08-14 15:15:12 +00:00
|
|
|
var fn_start = Format("%s_Start", seq_name);
|
|
|
|
if (!Call(fn_start, ...))
|
|
|
|
GameCall(fn_start, this, ...);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
protected func InitializePlayer(int plr)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
2016-07-22 05:51:29 +00:00
|
|
|
if (seq_name)
|
|
|
|
{
|
|
|
|
// Scripted sequence
|
|
|
|
JoinPlayer(plr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Editor-made sequence
|
|
|
|
if (trigger && trigger.Trigger == "player_join") OnTrigger(nil, plr);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected func InitializePlayers(int plr)
|
|
|
|
{
|
|
|
|
if (!seq_name)
|
|
|
|
{
|
|
|
|
// Editor-made sequence
|
|
|
|
if (trigger && trigger.Trigger == "game_start") OnTrigger();
|
|
|
|
}
|
2014-08-14 15:15:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func RemovePlayer(int plr)
|
|
|
|
{
|
2016-07-22 05:51:29 +00:00
|
|
|
if (seq_name)
|
|
|
|
{
|
|
|
|
// Scripted sequence
|
|
|
|
// Called by sequence if it ends and by engine if player leaves.
|
|
|
|
var fn_remove = Format("~%s_RemovePlayer", seq_name);
|
|
|
|
if (!Call(fn_remove, plr))
|
|
|
|
GameCall(fn_remove, this, plr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Editor-made sequence
|
|
|
|
if (trigger && trigger.Trigger == "player_remove") OnTrigger(nil, plr);
|
|
|
|
}
|
2014-08-14 15:15:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func JoinPlayer(int plr)
|
|
|
|
{
|
2015-03-01 13:01:26 +00:00
|
|
|
var j = 0, crew;
|
2014-08-14 15:15:12 +00:00
|
|
|
while (crew = GetCrew(plr, j++))
|
|
|
|
{
|
|
|
|
//if (crew == GetCursor(plr)) crew.Sequence_was_cursor = true; else crew.Sequence_was_cursor = nil;
|
|
|
|
crew->SetCrewEnabled(false);
|
|
|
|
crew->CancelUse();
|
2015-03-01 13:01:26 +00:00
|
|
|
if (crew->GetMenu())
|
|
|
|
if (!crew->GetMenu()->~Uncloseable())
|
|
|
|
crew->CancelMenu();
|
2014-08-14 15:15:12 +00:00
|
|
|
crew->MakeInvincible();
|
|
|
|
crew->SetCommand("None");
|
|
|
|
crew->SetComDir(COMD_Stop);
|
2015-09-15 02:36:02 +00:00
|
|
|
crew.Sequence_stored_breath = crew->GetBreath();
|
2014-08-14 15:15:12 +00:00
|
|
|
}
|
2015-03-01 13:01:26 +00:00
|
|
|
// Per-player sequence callback.
|
2014-08-14 15:15:12 +00:00
|
|
|
var fn_join = Format("~%s_JoinPlayer", seq_name);
|
|
|
|
if (!Call(fn_join, plr))
|
|
|
|
GameCall(fn_join, this, plr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func Stop(bool no_remove)
|
|
|
|
{
|
|
|
|
if (started)
|
|
|
|
{
|
2014-08-23 16:44:28 +00:00
|
|
|
SetViewTarget(nil);
|
2015-03-01 13:01:26 +00:00
|
|
|
// Reenable crew and reset cursor.
|
|
|
|
for (var i = 0; i<GetPlayerCount(C4PT_User); ++i)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
|
|
|
var plr = GetPlayerByIndex(i, C4PT_User);
|
2015-03-01 13:01:26 +00:00
|
|
|
var j = 0, crew;
|
2014-08-14 15:15:12 +00:00
|
|
|
while (crew = GetCrew(plr, j++))
|
|
|
|
{
|
|
|
|
crew->SetCrewEnabled(true);
|
|
|
|
crew->ClearInvincible();
|
2015-09-15 02:36:02 +00:00
|
|
|
// just in case clonk was underwater
|
|
|
|
var breath_diff = crew.Sequence_stored_breath - crew->GetBreath();
|
|
|
|
crew.Sequence_stored_breath = nil;
|
|
|
|
if (breath_diff) crew->DoBreath(breath_diff + 100); // give some bonus breath for the distraction
|
2014-08-14 15:15:12 +00:00
|
|
|
//if (crew.Sequence_was_cursor) SetCursor(plr, crew);
|
|
|
|
}
|
2015-03-01 13:01:26 +00:00
|
|
|
// Ensure proper cursor.
|
|
|
|
if (!GetCursor(plr))
|
|
|
|
SetCursor(plr, GetCrew(plr));
|
|
|
|
if (crew = GetCursor(plr))
|
|
|
|
SetPlrView(plr, crew);
|
|
|
|
// Per-player sequence callback.
|
2014-08-14 15:15:12 +00:00
|
|
|
RemovePlayer(plr);
|
|
|
|
}
|
2015-12-13 21:14:55 +00:00
|
|
|
Sound("UI::Ding", true);
|
2014-08-14 15:15:12 +00:00
|
|
|
started = false;
|
2015-03-01 13:01:26 +00:00
|
|
|
// Call stop function of this scene.
|
2014-08-14 15:15:12 +00:00
|
|
|
var fn_init = Format("~%s_Stop", seq_name);
|
|
|
|
if (!Call(fn_init))
|
|
|
|
GameCall(fn_init, this);
|
|
|
|
}
|
2015-03-01 13:01:26 +00:00
|
|
|
if (!no_remove)
|
|
|
|
RemoveObject();
|
2014-08-14 15:15:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
protected func Destruction()
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
|
|
|
Stop(true);
|
2015-03-01 13:01:26 +00:00
|
|
|
return;
|
2014-08-14 15:15:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
/*-- Sequence callbacks --*/
|
2014-08-14 15:15:12 +00:00
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
public func ScheduleNext(int delay, next_idx)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
2014-09-11 23:01:12 +00:00
|
|
|
return ScheduleCall(this, this.CallNext, delay, 1, next_idx);
|
2014-08-14 15:15:12 +00:00
|
|
|
}
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
public func ScheduleSame(int delay) { return ScheduleNext(delay, seq_progress); }
|
2014-08-14 15:15:12 +00:00
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
public func CallNext(next_idx)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
|
|
|
// Start conversation context.
|
|
|
|
// Update dialogue progress first.
|
2015-03-01 13:01:26 +00:00
|
|
|
if (GetType(next_idx))
|
|
|
|
seq_progress = next_idx;
|
|
|
|
else
|
|
|
|
++seq_progress;
|
2014-08-14 15:15:12 +00:00
|
|
|
// Then call relevant functions.
|
|
|
|
var fn_progress = Format("%s_%d", seq_name, seq_progress);
|
|
|
|
if (!Call(fn_progress))
|
|
|
|
GameCall(fn_progress, this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
/*-- Force view on target --*/
|
2014-08-14 15:15:12 +00:00
|
|
|
|
|
|
|
// Force all player views on given target
|
|
|
|
public func SetViewTarget(object view_target)
|
|
|
|
{
|
|
|
|
ClearScheduleCall(this, this.UpdateViewTarget);
|
|
|
|
if (view_target)
|
|
|
|
{
|
|
|
|
UpdateViewTarget(view_target);
|
|
|
|
ScheduleCall(this, this.UpdateViewTarget, 30, 999999999, view_target);
|
|
|
|
}
|
2014-09-11 23:01:12 +00:00
|
|
|
else
|
|
|
|
{
|
2015-03-01 13:01:26 +00:00
|
|
|
for (var i = 0; i < GetPlayerCount(C4PT_User); ++i)
|
2014-09-11 23:01:12 +00:00
|
|
|
{
|
|
|
|
var plr = GetPlayerByIndex(i, C4PT_User);
|
|
|
|
SetPlrView(plr, GetCursor(plr));
|
|
|
|
}
|
|
|
|
}
|
2014-08-14 15:15:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private func UpdateViewTarget(object view_target)
|
|
|
|
{
|
2015-03-01 13:01:26 +00:00
|
|
|
// Force view of all players on target.
|
|
|
|
if (!view_target)
|
|
|
|
return;
|
|
|
|
for (var i=0; i < GetPlayerCount(C4PT_User); ++i)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
|
|
|
var plr = GetPlayerByIndex(i, C4PT_User);
|
|
|
|
SetPlrView(plr, view_target);
|
|
|
|
}
|
2015-03-01 13:01:26 +00:00
|
|
|
return;
|
2014-08-14 15:15:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
/*-- Message function forwards --*/
|
2014-08-14 15:15:12 +00:00
|
|
|
|
|
|
|
public func MessageBoxAll(string message, object talker, bool as_message, ...)
|
|
|
|
{
|
|
|
|
return Dialogue->MessageBoxAll(message, talker, as_message, ...);
|
|
|
|
}
|
|
|
|
|
|
|
|
private func MessageBox(string message, object clonk, object talker, int for_player, bool as_message, ...)
|
|
|
|
{
|
|
|
|
return Dialogue->MessageBox(message, clonk, talker, for_player, as_message, ...);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
/*-- Helper Functions --*/
|
|
|
|
|
|
|
|
// Helper function to get a speaker in sequences.
|
|
|
|
public func GetHero(object nearest_obj)
|
2014-09-11 23:01:12 +00:00
|
|
|
{
|
2015-03-01 13:01:26 +00:00
|
|
|
// Prefer object stored as hero - if not assigned, find someone close to specified object.
|
2014-09-11 23:01:12 +00:00
|
|
|
if (!this.hero)
|
2015-03-01 13:01:26 +00:00
|
|
|
{
|
2014-09-11 23:01:12 +00:00
|
|
|
if (nearest_obj)
|
2014-09-21 14:35:39 +00:00
|
|
|
this.hero = nearest_obj->FindObject(Find_ID(Clonk), Find_Not(Find_Owner(NO_OWNER)), nearest_obj->Sort_Distance());
|
2014-09-11 23:01:12 +00:00
|
|
|
else
|
|
|
|
this.hero = FindObject(Find_ID(Clonk), Find_Not(Find_Owner(NO_OWNER)));
|
2015-03-01 13:01:26 +00:00
|
|
|
}
|
|
|
|
// If there is still no hero, take any clonk. Let the NPCs do the sequence among themselves.
|
2014-09-11 23:01:12 +00:00
|
|
|
// (to prevent null pointer exceptions if all players left during the sequence)
|
2015-03-01 13:01:26 +00:00
|
|
|
if (!this.hero)
|
|
|
|
this.hero = FindObject(Find_ID(Clonk));
|
|
|
|
// Might return nil if all players are gone and there are no NPCs. Well, there was noone to listen anyway.
|
2014-09-11 23:01:12 +00:00
|
|
|
return this.hero;
|
|
|
|
}
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
// Scenario section overload: automatically transfers all player clonks.
|
|
|
|
public func LoadScenarioSection(name, ...)
|
2014-09-11 23:01:12 +00:00
|
|
|
{
|
|
|
|
// Store objects: All clonks and sequence object
|
|
|
|
this.save_objs = [];
|
|
|
|
AddSectSaveObj(this);
|
2015-03-01 13:01:26 +00:00
|
|
|
var iplr, plr;
|
|
|
|
for (iplr = 0; iplr < GetPlayerCount(C4PT_User); ++iplr)
|
2014-09-11 23:01:12 +00:00
|
|
|
{
|
|
|
|
plr = GetPlayerByIndex(iplr, C4PT_User);
|
2015-03-01 13:01:26 +00:00
|
|
|
for (var icrew = 0, crew; icrew < GetCrewCount(iplr); ++icrew)
|
2014-09-11 23:01:12 +00:00
|
|
|
if (crew = GetCrew(plr, icrew))
|
|
|
|
AddSectSaveObj(crew);
|
|
|
|
}
|
|
|
|
var save_objs = this.save_objs;
|
|
|
|
// Load new section
|
|
|
|
var result = inherited(name, ...);
|
|
|
|
// Restore objects
|
2015-03-01 13:01:26 +00:00
|
|
|
for (var obj in save_objs)
|
|
|
|
if (obj)
|
|
|
|
obj->SetObjectStatus(C4OS_NORMAL);
|
|
|
|
if (this)
|
|
|
|
this.save_objs = nil;
|
2014-09-11 23:01:12 +00:00
|
|
|
// Recover HUD
|
2015-03-01 13:01:26 +00:00
|
|
|
for (iplr = 0; iplr < GetPlayerCount(C4PT_User); ++iplr)
|
2014-09-11 23:01:12 +00:00
|
|
|
{
|
|
|
|
plr = GetPlayerByIndex(iplr, C4PT_User);
|
|
|
|
var HUDcontroller = FindObject(Find_ID(GUI_Controller), Find_Owner(plr));
|
2015-01-10 09:14:02 +00:00
|
|
|
if (!HUDcontroller) HUDcontroller = CreateObjectAbove(GUI_Controller, 10, 10, plr);
|
2014-09-11 23:01:12 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
// Flag obj and any contained stuff for scenario saving.
|
|
|
|
public func AddSectSaveObj(object obj)
|
2014-09-11 23:01:12 +00:00
|
|
|
{
|
2015-03-01 13:01:26 +00:00
|
|
|
if (!obj)
|
|
|
|
return false;
|
2014-09-11 23:01:12 +00:00
|
|
|
this.save_objs[GetLength(this.save_objs)] = obj;
|
2015-03-01 13:01:26 +00:00
|
|
|
var cont, i = 0;
|
|
|
|
while (cont = obj->Contents(i++))
|
|
|
|
AddSectSaveObj(cont);
|
2014-09-11 23:01:12 +00:00
|
|
|
return obj->SetObjectStatus(C4OS_INACTIVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
/*-- Global helper functions --*/
|
2014-08-14 15:15:12 +00:00
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
// Starts the specified sequence at indicated progress.
|
2014-08-26 14:06:53 +00:00
|
|
|
global func StartSequence(string name, int progress, ...)
|
2014-08-14 15:15:12 +00:00
|
|
|
{
|
2015-03-01 13:01:26 +00:00
|
|
|
var seq = CreateObject(Sequence, 0, 0, NO_OWNER);
|
2014-08-26 14:06:53 +00:00
|
|
|
seq->Start(name, progress, ...);
|
2014-08-14 15:15:12 +00:00
|
|
|
return seq;
|
|
|
|
}
|
|
|
|
|
2015-03-01 13:01:26 +00:00
|
|
|
// Stops the currently active sequence.
|
2014-08-14 15:15:12 +00:00
|
|
|
global func StopSequence()
|
|
|
|
{
|
|
|
|
var seq = FindObject(Find_ID(Sequence));
|
2015-03-01 13:01:26 +00:00
|
|
|
if (!seq)
|
|
|
|
return false;
|
2014-08-14 15:15:12 +00:00
|
|
|
return seq->Stop();
|
|
|
|
}
|
2015-03-01 13:01:26 +00:00
|
|
|
|
|
|
|
// Returns the currently active sequence.
|
|
|
|
global func GetActiveSequence()
|
|
|
|
{
|
|
|
|
var seq = FindObject(Find_ID(Sequence));
|
|
|
|
return seq;
|
|
|
|
}
|
2016-07-21 04:24:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* User-made sequences from the editor */
|
|
|
|
|
2016-07-22 05:51:29 +00:00
|
|
|
local Name="$Name$";
|
|
|
|
local Description="$Description$";
|
2016-07-21 04:24:13 +00:00
|
|
|
local trigger, condition, action, action_progress_mode, action_allow_parallel;
|
|
|
|
local active=true;
|
|
|
|
local check_interval=12;
|
|
|
|
local deactivate_after_action; // If true, finished is set to true after the first execution and the trigger deactivated
|
2016-07-23 05:43:22 +00:00
|
|
|
local Visibility=VIS_Editor;
|
2016-08-07 21:33:51 +00:00
|
|
|
local trigger_started;
|
2016-07-21 04:24:13 +00:00
|
|
|
|
|
|
|
// finished: Disables the trigger. true if trigger has run and deactivate_after_action is set to true.
|
|
|
|
// Note that this flag is not saved in scenarios, so saving as scenario and reloading will re-enable all triggers (for editor mode)
|
|
|
|
local finished;
|
|
|
|
|
|
|
|
public func Definition(def)
|
|
|
|
{
|
2016-07-22 05:51:29 +00:00
|
|
|
// EditorActions
|
2016-07-21 04:24:13 +00:00
|
|
|
if (!def.EditorActions) def.EditorActions = {};
|
|
|
|
def.EditorActions.Test = { Name="$Test$", Command="OnTrigger(nil, nil, true)" };
|
2016-07-22 05:51:29 +00:00
|
|
|
// EditorProps
|
2016-07-21 04:24:13 +00:00
|
|
|
if (!def.EditorProps) def.EditorProps = {};
|
|
|
|
def.EditorProps.active = { Name="$Active$", Type="bool", Set="SetActive" };
|
|
|
|
def.EditorProps.finished = { Name="$Finished$", Type="bool", Set="SetFinished" };
|
2016-08-02 05:07:20 +00:00
|
|
|
def.EditorProps.trigger = { Name="$Trigger$", Type="enum", OptionKey="Trigger", Set="SetTrigger", Priority=110, Options = [
|
2016-07-21 04:24:13 +00:00
|
|
|
{ Name="$None$" },
|
2016-07-30 23:42:55 +00:00
|
|
|
{ Name="$PlayerEnterRegionRect$", EditorHelp="$PlayerEnterRegionHelp$", Value={ Trigger="player_enter_region_rect", Rect=[-20, -20, 40, 40] }, ValueKey="Rect", Delegate={ Type="rect", Color=0xff8000, Relative=true, Set="SetTriggerRect", SetRoot=true } },
|
|
|
|
{ Name="$PlayerEnterRegionCircle$", EditorHelp="$PlayerEnterRegionHelp$", Value={ Trigger="player_enter_region_circle", Radius=25 }, ValueKey="Radius", Delegate={ Type="circle", Color=0xff8000, Relative=true, Set="SetTriggerRadius", SetRoot=true } },
|
|
|
|
{ Name="$ObjectEnterRegionRect$", EditorHelp="$ObjectEnterRegionHelp$", Value={ Trigger="object_enter_region_rect", Rect=[-20, -20, 40, 40] }, Delegate={ Name="$ObjectEnterRegionRect$", EditorHelp="$ObjectEnterRegionHelp$", Type="proplist", EditorProps = {
|
|
|
|
ID = { Name="$ID$", EditorHelp="$IDHelp$", Type="def", Set="SetTriggerID", SetRoot=true },
|
|
|
|
Rect = { Type="rect", Color=0xff8000, Relative=true, Set="SetTriggerRect", SetRoot=true }
|
|
|
|
} } },
|
|
|
|
{ Name="$ObjectEnterRegionCircle$", EditorHelp="$ObjectEnterRegionHelp$", Value={ Trigger="object_enter_region_circle", Radius=25 }, Delegate={ Name="$ObjectEnterRegionCircle$", EditorHelp="$ObjectEnterRegionHelp$", Type="proplist", EditorProps = {
|
|
|
|
ID = { Name="$ID$", EditorHelp="$IDHelp$", Type="def", Set="SetTriggerID", SetRoot=true },
|
|
|
|
Radius = { Type="circle", Color=0xff8000, Relative=true, Set="SetTriggerRadius", SetRoot=true }
|
|
|
|
} } },
|
2016-08-08 03:37:48 +00:00
|
|
|
{ Name="$ObjectCountInContainer$", EditorHelp="$ObjectCountInContainerHelp$", Value={ Trigger="contained_object_count", Count=1 }, Delegate={ Name="$ObjectCountInContainer$", EditorHelp="$ObjectCountInContainerHelp$", Type="proplist", EditorProps = {
|
|
|
|
Container = { Name="$Container$", EditorHelp="$CountContainerHelp$", Type="object" },
|
|
|
|
ID = { Name="$ID$", EditorHelp="$CountIDHelp$", Type="def", EmptyName="$AnyID$" },
|
|
|
|
Count = { Name="$Count$", Type="int", Min=1 }
|
|
|
|
} } },
|
|
|
|
{ Name="$Interval$", EditorHelp="$IntervalHelp$", Value={ Trigger="interval", Interval=60 }, ValueKey="Interval", Delegate={ Name="$IntervalTime$", Type="int", Min=1, Set="SetIntervalTimer", SetRoot=true } },
|
2016-07-22 05:51:29 +00:00
|
|
|
{ Name="$GameStart$", Value={ Trigger="game_start" } },
|
|
|
|
{ Name="$PlayerJoin$", Value={ Trigger="player_join" } },
|
|
|
|
{ Name="$PlayerRemove$", Value={ Trigger="player_remove" } },
|
|
|
|
{ Name="$GoalsFulfilled$", Value={ Trigger="goals_fulfilled" } },
|
|
|
|
{ Group="$ClonkDeath$", Name="$AnyClonkDeath$", Value={ Trigger="any_clonk_death" } },
|
|
|
|
{ Group="$ClonkDeath$", Name="$PlayerClonkDeath$", Value={ Trigger="player_clonk_death" } },
|
|
|
|
{ Group="$ClonkDeath$", Name="$NeutralClonkDeath$", Value={ Trigger="neutral_clonk_death" } },
|
|
|
|
{ Group="$ClonkDeath$", Name="$SpecificClonkDeath$", Value={ Trigger="specific_clonk_death" }, ValueKey="Object", Delegate={ Type="object", Filter="IsClonk" } },
|
|
|
|
{ Name="$Construction$", Value={ Trigger="construction" }, ValueKey="ID", Delegate={ Type="def", Filter="IsStructure", EmptyName="$Anything$" } },
|
|
|
|
{ Name="$Production$", Value={ Trigger="production" }, ValueKey="ID", Delegate={ Type="def", EmptyName="$Anything$" } },
|
2016-07-21 04:24:13 +00:00
|
|
|
] };
|
2016-08-02 05:07:20 +00:00
|
|
|
def.EditorProps.condition = new UserAction.Evaluator.Boolean { Name="$Condition$" };
|
|
|
|
def.EditorProps.action = new UserAction.Prop { Priority=105 };
|
2016-07-21 04:24:13 +00:00
|
|
|
def.EditorProps.action_progress_mode = UserAction.PropProgressMode;
|
|
|
|
def.EditorProps.action_allow_parallel = UserAction.PropParallel;
|
|
|
|
def.EditorProps.deactivate_after_action = { Name="$DeactivateAfterAction$", Type="bool" };
|
2016-08-07 21:33:51 +00:00
|
|
|
def.EditorProps.check_interval = { Name="$CheckInterval$", EditorHelp="$CheckIntervalHelp$", Type="int", Set="SetCheckInterval", SaveAsCall="SetCheckInterval" };
|
2016-07-21 04:24:13 +00:00
|
|
|
}
|
|
|
|
|
2016-07-22 05:51:29 +00:00
|
|
|
public func SetTrigger(proplist new_trigger)
|
2016-07-21 04:24:13 +00:00
|
|
|
{
|
|
|
|
trigger = new_trigger;
|
|
|
|
// Set trigger: Restart any specific trigger timers
|
2016-08-08 03:37:48 +00:00
|
|
|
if (active && !finished) StartTrigger();
|
2016-07-21 04:24:13 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-22 05:51:29 +00:00
|
|
|
public func SetTriggerRect(array new_trigger_rect)
|
2016-07-21 04:24:13 +00:00
|
|
|
{
|
|
|
|
if (trigger && trigger.Rect)
|
|
|
|
{
|
|
|
|
trigger.Rect = new_trigger_rect;
|
|
|
|
SetTrigger(trigger); // restart trigger
|
|
|
|
}
|
2016-07-22 05:51:29 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func SetTriggerRadius(int new_trigger_radius)
|
|
|
|
{
|
|
|
|
if (trigger)
|
|
|
|
{
|
|
|
|
trigger.Radius = new_trigger_radius;
|
|
|
|
SetTrigger(trigger); // restart trigger
|
|
|
|
}
|
|
|
|
return true;
|
2016-07-21 04:24:13 +00:00
|
|
|
}
|
|
|
|
|
2016-07-30 23:42:55 +00:00
|
|
|
public func SetTriggerID(id new_id)
|
|
|
|
{
|
|
|
|
if (trigger)
|
|
|
|
{
|
|
|
|
trigger.ID = new_id;
|
|
|
|
SetTrigger(trigger); // restart trigger
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-21 04:24:13 +00:00
|
|
|
public func SetAction(new_action, new_action_progress_mode, new_action_allow_parallel)
|
|
|
|
{
|
|
|
|
action = new_action;
|
|
|
|
action_progress_mode = new_action_progress_mode;
|
|
|
|
action_allow_parallel = new_action_allow_parallel;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func SetActive(bool new_active, bool force_triggers)
|
|
|
|
{
|
|
|
|
if (active == new_active && !force_triggers) return true;
|
|
|
|
active = new_active;
|
|
|
|
if (active && !finished)
|
|
|
|
{
|
|
|
|
// Activated: Start trigger
|
|
|
|
StartTrigger();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Inactive or inactive by editor run: Stop trigger
|
|
|
|
StopTrigger();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func SetFinished(bool new_finished)
|
|
|
|
{
|
|
|
|
finished = new_finished;
|
|
|
|
return SetActive(active, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public func SetDeactivateAfterAction(bool new_val)
|
|
|
|
{
|
|
|
|
deactivate_after_action = new_val;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func StartTrigger()
|
|
|
|
{
|
|
|
|
if (!trigger) return false;
|
2016-08-07 21:33:51 +00:00
|
|
|
if (trigger_started) StopTrigger();
|
|
|
|
trigger_started = true;
|
2016-07-23 05:43:22 +00:00
|
|
|
SetGraphics("Active");
|
2016-07-21 04:24:13 +00:00
|
|
|
var fn = trigger.Trigger;
|
2016-07-30 23:42:55 +00:00
|
|
|
var id_search;
|
|
|
|
if (trigger.ID) id_search = Find_ID(trigger.ID);
|
|
|
|
if (fn == "player_enter_region_rect")
|
2016-07-21 04:24:13 +00:00
|
|
|
{
|
|
|
|
this.search_mask = Find_And(Find_InRect(trigger.Rect[0], trigger.Rect[1], trigger.Rect[2], trigger.Rect[3]), Find_OCF(OCF_Alive), Find_Func("IsClonk"), Find_Not(Find_Owner(NO_OWNER)));
|
|
|
|
AddTimer(this.EnterRegionTimer, check_interval);
|
|
|
|
}
|
2016-07-30 23:42:55 +00:00
|
|
|
else if (fn == "player_enter_region_circle")
|
2016-07-22 05:51:29 +00:00
|
|
|
{
|
|
|
|
this.search_mask = Find_And(Find_Distance(trigger.Radius), Find_OCF(OCF_Alive), Find_Func("IsClonk"), Find_Not(Find_Owner(NO_OWNER)));
|
|
|
|
AddTimer(this.EnterRegionTimer, check_interval);
|
|
|
|
}
|
2016-07-30 23:42:55 +00:00
|
|
|
else if (fn == "object_enter_region_rect")
|
|
|
|
{
|
|
|
|
this.search_mask = Find_And(Find_InRect(trigger.Rect[0], trigger.Rect[1], trigger.Rect[2], trigger.Rect[3]), id_search);
|
|
|
|
AddTimer(this.EnterRegionTimer, check_interval);
|
|
|
|
}
|
|
|
|
else if (fn == "object_enter_region_circle")
|
|
|
|
{
|
|
|
|
this.search_mask = Find_And(Find_Distance(trigger.Radius), Find_OCF(OCF_Alive), Find_Func("IsClonk"), id_search);
|
|
|
|
AddTimer(this.EnterRegionTimer, check_interval);
|
|
|
|
}
|
2016-08-08 03:37:48 +00:00
|
|
|
else if (fn == "contained_object_count")
|
|
|
|
{
|
|
|
|
AddTimer(this.CountContainedObjectsTimer, check_interval);
|
|
|
|
}
|
|
|
|
else if (fn == "interval")
|
|
|
|
{
|
|
|
|
AddTimer(this.OnTrigger, trigger.Interval);
|
|
|
|
}
|
2016-07-21 04:24:13 +00:00
|
|
|
else return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func StopTrigger()
|
|
|
|
{
|
2016-07-23 05:43:22 +00:00
|
|
|
SetGraphics();
|
2016-07-21 04:24:13 +00:00
|
|
|
// Remove any timers that may have been added
|
2016-08-07 21:33:51 +00:00
|
|
|
RemoveTimer(this.EnterRegionTimer);
|
2016-08-08 03:37:48 +00:00
|
|
|
RemoveTimer(this.CountContainedObjectsTimer);
|
|
|
|
RemoveTimer(this.OnTrigger);
|
2016-08-07 21:33:51 +00:00
|
|
|
trigger_started = false;
|
2016-07-21 04:24:13 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-08-07 21:33:51 +00:00
|
|
|
public func SetCheckInterval(new_interval)
|
|
|
|
{
|
|
|
|
check_interval = Max(1, new_interval);
|
|
|
|
return SetTrigger(trigger); // restart trigger
|
|
|
|
}
|
|
|
|
|
2016-08-08 03:37:48 +00:00
|
|
|
public func SetIntervalTimer(int new_interval)
|
|
|
|
{
|
|
|
|
if (trigger) trigger.Interval = new_interval;
|
|
|
|
return SetTrigger(trigger); // restart trigger
|
|
|
|
}
|
|
|
|
|
2016-07-21 04:24:13 +00:00
|
|
|
private func EnterRegionTimer()
|
|
|
|
{
|
|
|
|
for (var clonk in FindObjects(this.search_mask))
|
|
|
|
{
|
|
|
|
if (!clonk) continue; // deleted by previous execution
|
|
|
|
OnTrigger(clonk, clonk->GetOwner());
|
|
|
|
if (active != true) break; // deactivated by trigger
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-08 03:37:48 +00:00
|
|
|
private func CountContainedObjectsTimer()
|
|
|
|
{
|
|
|
|
if (trigger.Container && trigger.Container->ContentsCount(trigger.ID) >= trigger.Count)
|
|
|
|
{
|
|
|
|
OnTrigger(nil, NO_OWNER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-21 04:24:13 +00:00
|
|
|
public func OnTrigger(object triggering_clonk, int triggering_player, bool is_editor_test)
|
|
|
|
{
|
|
|
|
// Editor test: Triggered by first player
|
|
|
|
if (is_editor_test)
|
|
|
|
{
|
|
|
|
if (GetPlayerCount(C4PT_User)) triggering_player = GetPlayerByIndex();
|
|
|
|
}
|
|
|
|
// Check condition
|
2016-08-08 04:49:17 +00:00
|
|
|
if (condition && !UserAction->EvaluateCondition(condition, this, triggering_clonk, triggering_player)) return false;
|
2016-07-21 04:24:13 +00:00
|
|
|
// Only one action at the time
|
|
|
|
if (!action_allow_parallel) StopTrigger();
|
|
|
|
// Execute action
|
|
|
|
return UserAction->EvaluateAction(action, this, triggering_clonk, triggering_player, action_progress_mode, action_allow_parallel, this.OnActionFinished);
|
|
|
|
}
|
|
|
|
|
|
|
|
private func OnActionFinished(context)
|
|
|
|
{
|
|
|
|
// Callback from EvaluateAction: Action finished. Deactivate action if desired.
|
|
|
|
if (deactivate_after_action)
|
|
|
|
SetFinished(true);
|
2016-08-08 04:49:17 +00:00
|
|
|
else if (active && !finished && !trigger_started)
|
2016-07-21 04:24:13 +00:00
|
|
|
StartTrigger();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-22 05:51:29 +00:00
|
|
|
public func OnClonkDeath(object clonk, int killer)
|
|
|
|
{
|
|
|
|
// Is this a clonk death trigger?
|
|
|
|
if (!trigger || !clonk) return false;
|
|
|
|
var t = trigger.Trigger;
|
|
|
|
if (!WildcardMatch(t, "*_clonk_death")) return false;
|
|
|
|
// Specific trigger check
|
|
|
|
if (t == "player_clonk_death")
|
|
|
|
{
|
|
|
|
if (clonk->GetOwner() == NO_OWNER) return false;
|
|
|
|
}
|
|
|
|
else if (t == "neutral_clonk_death")
|
|
|
|
{
|
|
|
|
if (clonk->GetOwner() != NO_OWNER) return false;
|
|
|
|
}
|
|
|
|
else if (t == "specific_clonk_death")
|
|
|
|
{
|
|
|
|
if (trigger.Object != clonk) return false;
|
|
|
|
}
|
|
|
|
// OK, trigger it!
|
|
|
|
return OnTrigger(clonk, killer);
|
|
|
|
}
|
|
|
|
|
|
|
|
public func OnConstructionFinished(object structure, int constructing_player)
|
|
|
|
{
|
|
|
|
// Is this a structure finished trigger?
|
|
|
|
if (!trigger || !structure) return false;
|
|
|
|
if (trigger.Trigger != "construction") return false;
|
|
|
|
if (trigger.ID) if (structure->GetID() != trigger.ID) return false;
|
|
|
|
// OK, trigger it!
|
|
|
|
return OnTrigger(structure, constructing_player);
|
|
|
|
}
|
|
|
|
|
|
|
|
public func OnProductionFinished(object product, int producing_player)
|
|
|
|
{
|
|
|
|
// Is this a structure finished trigger?
|
|
|
|
if (!trigger || !product) return false;
|
|
|
|
if (trigger.Trigger != "production") return false;
|
|
|
|
if (trigger.ID) if (product->GetID() != trigger.ID) return false;
|
|
|
|
// OK, trigger it!
|
|
|
|
return OnTrigger(product, producing_player);
|
|
|
|
}
|
|
|
|
|
|
|
|
public func OnGoalsFulfilled()
|
|
|
|
{
|
|
|
|
// All goals fulfilled: Return true if any action is executed (stops regular GameOver)
|
|
|
|
if (!trigger) return false;
|
|
|
|
if (trigger.Trigger != "goals_fulfilled") return false;
|
|
|
|
return OnTrigger();
|
|
|
|
}
|
|
|
|
|
2016-07-23 05:43:22 +00:00
|
|
|
public func SetName(string new_name, ...)
|
|
|
|
{
|
|
|
|
if (new_name == GetID()->GetName())
|
|
|
|
{
|
|
|
|
Message("");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (trigger)
|
|
|
|
Message(Format("@<c ff8000>%s</c>", new_name));
|
|
|
|
else
|
|
|
|
Message(Format("@<c 808080>%s</c>", new_name));
|
|
|
|
}
|
|
|
|
return inherited(new_name, ...);
|
|
|
|
}
|
|
|
|
|
2016-07-21 04:24:13 +00:00
|
|
|
/*-- Saving --*/
|
|
|
|
|
|
|
|
// No scenario saving.
|
|
|
|
public func SaveScenarioObject(props, ...)
|
|
|
|
{
|
|
|
|
if (!_inherited(props, ...)) return false;
|
|
|
|
// Do not save script-created sequences
|
|
|
|
if (this.seq_name) return false;
|
|
|
|
// Save editor-made sequences
|
|
|
|
if (save_scenario_dup_objects && finished) // finished flag only copied for object duplication; not saved in savegames
|
|
|
|
props->AddCall("Active", this, "SetFinished", finished);
|
|
|
|
if (!active) props->AddCall("Active", this, "SetActive", active);
|
|
|
|
if (trigger) props->AddCall("Trigger", this, "SetTrigger", trigger);
|
|
|
|
if (condition) props->AddCall("Condition", this, "SetCondition", condition);
|
|
|
|
if (action || action_progress_mode || action_allow_parallel) props->AddCall("Action", this, "SetAction", action, action_progress_mode, action_allow_parallel);
|
|
|
|
if (deactivate_after_action) props->AddCall("DeactivateAfterAction", this, "SetDeactivateAfterAction", deactivate_after_action);
|
2016-08-07 20:09:40 +00:00
|
|
|
return true;
|
2016-07-21 04:24:13 +00:00
|
|
|
}
|