Sequence: Add more triggers.

* Player enters circular region
* Game starts (after player joins)
* Player joins
* Player removed
* All goals fulfilled
* Clonk death
* Construction of structure
* Production of item
qteditor
Sven Eberhardt 2016-07-22 01:51:29 -04:00
parent 14d01cd5cd
commit 58faf41068
7 changed files with 174 additions and 28 deletions

View File

@ -46,16 +46,44 @@ public func Start(string name, int progress, ...)
protected func InitializePlayer(int plr)
{
JoinPlayer(plr);
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();
}
return true;
}
public func RemovePlayer(int plr)
{
// 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);
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);
}
return true;
}
@ -300,6 +328,8 @@ global func GetActiveSequence()
/* User-made sequences from the editor */
local Name="$Name$";
local Description="$Description$";
local trigger, condition, action, action_progress_mode, action_allow_parallel;
local active=true;
local check_interval=12;
@ -311,14 +341,27 @@ local finished;
public func Definition(def)
{
// EditorActions
if (!def.EditorActions) def.EditorActions = {};
def.EditorActions.Test = { Name="$Test$", Command="OnTrigger(nil, nil, true)" };
// EditorProps
if (!def.EditorProps) def.EditorProps = {};
def.EditorProps.active = { Name="$Active$", Type="bool", Set="SetActive" };
def.EditorProps.finished = { Name="$Finished$", Type="bool", Set="SetFinished" };
def.EditorProps.trigger = { Name="$Trigger$", Type="enum", OptionKey="Trigger", Options = [
{ Name="$None$" },
{ Name="$EnterRegionRect$", Value={ Trigger="enter_region_rect", Rect=[-20, -20, 40, 40] }, ValueKey="Rect", Delegate={ Type="rect", Relative=true, Set="SetTriggerRect", SetRoot=true } } // TODO: Allow runtime update of search fn
{ Name="$EnterRegionRect$", Value={ Trigger="enter_region_rect", Rect=[-20, -20, 40, 40] }, ValueKey="Rect", Delegate={ Type="rect", Color=0xff8000, Relative=true, Set="SetTriggerRect", SetRoot=true } },
{ Name="$EnterRegionCircle$", Value={ Trigger="enter_region_circle", Radius=25 }, ValueKey="Radius", Delegate={ Type="circle", Color=0xff8000, Relative=true, Set="SetTriggerRadius", SetRoot=true } },
{ 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$" } },
] };
def.EditorProps.condition = UserAction.Evaluator.Condition;
def.EditorProps.action = UserAction.Prop;
@ -327,7 +370,7 @@ public func Definition(def)
def.EditorProps.deactivate_after_action = { Name="$DeactivateAfterAction$", Type="bool" };
}
public func SetTrigger(new_trigger)
public func SetTrigger(proplist new_trigger)
{
trigger = new_trigger;
// Set trigger: Restart any specific trigger timers
@ -339,13 +382,24 @@ public func SetTrigger(new_trigger)
return true;
}
public func SetTriggerRect(new_trigger_rect)
public func SetTriggerRect(array new_trigger_rect)
{
if (trigger && trigger.Rect)
{
trigger.Rect = new_trigger_rect;
SetTrigger(trigger); // restart trigger
}
return true;
}
public func SetTriggerRadius(int new_trigger_radius)
{
if (trigger)
{
trigger.Radius = new_trigger_radius;
SetTrigger(trigger); // restart trigger
}
return true;
}
public func SetAction(new_action, new_action_progress_mode, new_action_allow_parallel)
@ -394,6 +448,11 @@ public func StartTrigger()
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);
}
else if (fn == "enter_region_circle")
{
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);
}
else return false;
return true;
}
@ -440,6 +499,57 @@ private func OnActionFinished(context)
return true;
}
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();
}
/*-- Saving --*/
// No scenario saving.

View File

@ -0,0 +1,22 @@
Name=Sequenz
Description=Erlaubt das Erstellen von Intros, Zwischensequenzen oder sonstigen Ereignissen.
Test=Testen
Active=Aktiv
Finished=Sequenz beendet
Trigger=Ausloeser
None=Nichts
EnterRegionRect=Spieler betritt Bereich (Rechteck)
EnterRegionCircle=Spieler betritt Bereich (Radius)
DeactivateAfterAction=Nur einmal ausfuehren
GameStart=Spielstart
PlayerJoin=Spielerbeitritt
PlayerRemove=Spieler geloescht
GoalsFulfilled=Alle Spielziele erfuellt
ClonkDeath=Ein Clonk stirbt
Construction=Konstruktion Gebaeude
Anything=Beliebig
Production=Produktion Objekt
AnyClonkDeath=Beliebiger Clonk stirbt
PlayerClonkDeath=Spielerclonk stirbt
NeutralClonkDeath=Neutraler Clonk stirbt
SpecificClonkDeath=Bestimmer Clonk stirbt...

View File

@ -1,7 +1,22 @@
Name=Sequence
Description=Allows creation of intros, cutscenes or other events bound to triggers such as player joins, players entering regions or finished productions.
Test=Test
Active=Active
Finished=Finished
Trigger=Trigger
None=None
EnterRegionRect=Player enters region (rect)
EnterRegionRect=Player enters region (rectangle)
EnterRegionCircle=Player enters region (radius)
DeactivateAfterAction=Run once only
GameStart=Game start
PlayerJoin=Player join
PlayerRemove=Player deleted
GoalsFulfilled=All goals fulfilled
ClonkDeath=A clonk dies
Construction=Construction of structure
Anything=Anything
Production=Production of item
AnyClonkDeath=Any clonk dies
PlayerClonkDeath=Player clonk dies
NeutralClonkDeath=Neutral clonk dies
SpecificClonkDeath=Specific clonk dies...

View File

@ -1,7 +0,0 @@
Test=Testen
Active=Aktiv
Finished=Sequenz beendet
Trigger=Ausloeser
None=Nichts
EnterRegionRect=Spieler betritt Bereich
DeactivateAfterAction=Nur einmal ausfuehren

View File

@ -157,12 +157,13 @@ public func EvaluateAction(proplist props, object action_object, object triggeri
context.temp = true;
}
// Prevent duplicate parallel execution
if (!allow_parallel && (context.hold && !context.suspended)) return nil;
if (!allow_parallel && (context.hold && !context.suspended)) return false;
// Init context settings
context->InitContext(action_object, triggering_player, triggering_object, props);
// Execute action
EvaluateValue("Action", props, context);
FinishAction(context);
return true;
}
public func EvaluateCondition(proplist props, object action_object, object triggering_object, int triggering_player)

View File

@ -25,7 +25,7 @@
#include Library_PowerConsumer
// Production queue, a list of items to be produced.
// Contains proplists of format {Product = <objid>, Amount = <int>, Infinite = (optional)<bool>}. /Infinite/ == true -> infinite production.
// Contains proplists of format {Product = <objid>, Amount = <int>, Infinite = (optional)<bool>, ProducingPlayer = (optional)<int>}. /Infinite/ == true -> infinite production.
local queue;
@ -364,7 +364,7 @@ public func ModifyQueueIndex(int position, int amount, bool infinite_production)
@param amount the amount of items of \c item_id which should be produced. Amount must not be negative.
@paramt infinite whether to enable infinite production.
*/
public func AddToQueue(id product_id, int amount, bool infinite)
public func AddToQueue(id product_id, int amount, bool infinite, int producing_player)
{
// Check if this producer can produce the requested item.
if (!IsProduct(product_id))
@ -384,7 +384,7 @@ public func AddToQueue(id product_id, int amount, bool infinite)
// Otherwise create a new entry in the queue.
if (!found)
PushBack(queue, { Product = product_id, Amount = amount, Infinite = infinite});
PushBack(queue, { Product = product_id, Amount = amount, Infinite = infinite, ProducingPlayer=producing_player });
// Notify all production menus open for this producer.
UpdateInteractionMenus(this.GetProductionMenuEntries);
}
@ -429,7 +429,7 @@ private func ModifyProduction(proplist info, int player)
if (index == nil && (amount > 0 || infinite))
{
AddToQueue(product, amount, infinite);
AddToQueue(product, amount, infinite, player);
}
else if (index != nil)
{
@ -471,8 +471,9 @@ private func ProcessQueue()
// Produce first item in the queue.
var product_id = queue[0].Product;
var producing_player = queue[0].ProducingPlayer;
// Check raw material need.
if (!Produce(product_id))
if (!Produce(product_id, producing_player))
{
// No material available? request from cable network.
RequestAllMissingComponents(product_id);
@ -504,7 +505,7 @@ public func PowerNeed() { return 80; }
public func GetConsumerPriority() { return 50; }
private func Produce(id product)
private func Produce(id product, producing_player)
{
// Already producing? Wait a little.
if (IsProducing())
@ -527,7 +528,7 @@ private func Produce(id product)
CheckFuel(product, true);
// Add production effect.
AddEffect("ProcessProduction", this, 100, 2, this, nil, product);
AddEffect("ProcessProduction", this, 100, 2, this, nil, product, producing_player);
return true;
}
@ -636,13 +637,14 @@ private func IsProducing()
}
protected func FxProcessProductionStart(object target, proplist effect, int temporary, id product)
protected func FxProcessProductionStart(object target, proplist effect, int temporary, id product, int producing_player)
{
if (temporary)
return FX_OK;
// Set product.
// Set product information
effect.Product = product;
effect.producing_player = producing_player;
// Set production duration to zero.
effect.Duration = 0;
@ -729,6 +731,8 @@ protected func FxProcessProductionStop(object target, proplist effect, int reaso
// Create product.
var product = CreateObject(effect.Product);
OnProductEjection(product);
// Global callback
if (product) GameCallEx("OnProductionFinished", product, effect.producing_player);
return FX_OK;
}

View File

@ -208,7 +208,7 @@ public func Collection2(object obj)
// check if we're done?
if(full_material)
StartConstructing();
StartConstructing(obj->GetController());
}
// component removed (e.g.: Contained wood burned down or some externel scripts went havoc)
@ -267,7 +267,7 @@ private func GetMissingComponents()
return missing_material;
}
private func StartConstructing()
private func StartConstructing(int by_player)
{
if(!definition)
return;
@ -308,6 +308,7 @@ private func StartConstructing()
// If not: Autoconstruct 2.0!
Schedule(site, "DoCon(2)",1,50);
Schedule(this,"RemoveObject()",1);
Global->ScheduleCall(nil, Global.GameCallEx, 51, 1, "OnConstructionFinished", site, by_player);
site->Sound("Structures::FinishBuilding");
}