Flick Purpose
The purpose of a Flick script is to allow a flick developer to create a scripted sequence through an easy to read, high level language that hides all the tiresome work associated with object wiring, messaging, committing jobs, and last but not least streaming objects in/out of the world. In the end, the flick developer only needs to announce what he/she wishes to affect and how it will be affected.
You can think of a flick sequence as a director of a scene, telling this actor to talk to that actor, the camera man to move the camera to a particular location, the lights to fade out, etc. Flick scripts are meant to read like a script from a movie - move here, play this anim, move camera here, etc. A flick script is meant to contain very little logic. As such, it's intentionally limited in that area.
Flick Debugging
To debug .flick files you should first set 'flick_retry=true' in your .ini file. When this is set to True, the flick compiler will attempt to recompile the flick when an error is found.
If an error is found in the .flick and you have flick_retry=true, then you should see an error box describing the error. After you've read the error, press 'ignore once' then you should see a batter box dialog. When you see this dialog you can make your .flick changes then press Retry.
You can also force Re-compile All Flicks by typing 'reload flick' in the console. Keep in mind this will not reload the gos that the flick sequence has affected, so its recommended that you reload gos right after reloading flick.
When looking for documentation on a command or condition and you can't seem to find it... look no further than the flick_functions.gas file. That file is what the flick compiler reads in to build its list of commands, so if its not in that file then its not a command or condition. There are comments for every command/condition and their parameters. Also if you wish to add your own flick commands or conditions just add it to the flick_functions.gas file, have it point to the appropriate skrit functions and your good to go. For good examples look at existing commands and conditions.
To see a list of flick console commands type ‘help flick’ in the console.
Another helpful tool is using the 'flick launch' console command. With it you can manually run flicks whenever you want. Keep in mind that the hero will be considered the flick 'owner' and the scidnames within the current region will be bound to the flick names.
I also like to use the 'bool aijobs' console command. You’ll see state info next to your actors so you know what they are thinking/doing as well as debug lines showing you where they are headed.
There is also a flick probe (Alt-F3) that will show you all the flicks affecting a particular actor/object and what state they are in.
Flick Sequence
Flicks sequences have at least one or more threads. For each sequence the flick developer needs to bind the names of the different go's used in that sequence, as well as what thread will be the entry (or startup) thread. When binding names, scidnames are used, for example if you want to use an actor you've placed in SE you need to give that actor a scidname.
sequence town_battle // town_battle is the name of this flick sequence
{
role Monster; // a tacklack monster was placed in SE and its scidname is Monster
role Karnov; // a town NPC was placed in SE and its scidname is Karnov
entry main; // the starting thread. ( if there is only one thread then this is not needed )
}
Flick Thread
Threads have a list of flick commands that are executed in top to bottom order when the thread is launched. The general idea is to have a thread orchestrate something, such as the camera, or a set of objects.
Example Flick
[Zed_and_Friends]
{
// set the roles for this sequence ( you can specify role type to limit or extend role command access )
role(actor) Zed, Skeletor, Hero; // actors have mind and can do jobs like attack, walk, ect.
role(prop) Bench; // props are gos that generally don't have a mind
role CutMark1; // no role type means that this role cannot use 'actor' or 'prop' commands
// set the startup thread for this sequence
entry main;
thread main
{
// capture the camera so the user can't control it any more
CameraCapture;
// Have the camera easebetween to the CutMark1 position and orientation over 10 seconds.
CameraFlyTo CutMark1 10 EaseBetween, wait;
Zed:
Animate gtup, wait; // have Zed do the 'gtup' animation
Report "Hello!";
MoveTo Bench, wait;
Report "This is my bench!";
MoveTo Skeletor, wait;
Report "This is a skeleton!";
Sleep, duration 1;
Hero:
Attack Zed, duration 3.0;
Skeletor:
MoveTo Bench, wait;
Report "Go away!";
Hero:
MoveTo Skeletor, wait;
Report "Got you!";
Sleep, duration 1;
Attack Skeletor, duration 2;
Skeletor:
MoveTo Zed, wait;
Sleep, duration 3;
MoveTo Player, wait;
Hero:
MoveTo Zed, wait;
// release the camera back to the player's control
CameraRelease;
// have skeletor laugh if he is close enough
run skel_laughing, WhenWithinDistance Skeletor 10;
// have skeletor laugh no matter what ( define thread in a single line )
run haha { Skeletor: playsound "laughing"; };
}
thread skel_laughing
{
Skeletor: playsound [inSound]"laughing"; // [inSound] explicitly specifies the sound parameter
}
}
Talk Flicks
Talk flicks are special flicks that are called when an actor is talked to in game. Here is an example of an actor template that is setup to use a talk flick:
[t:morden_viir_thug_leader_npc_tutorial,n:0xff000059]
{
[body]
{
initial_chore = chore_none;
}
[common]
{
screen_name = "Morden Lieutenant Jerind";
}
[conversation]
{
talk_flick = a0_tutbeach_gate_guard_talk; // <--- Here is where the talk flick is specified.
[conversations]
{
* = mercenary_base;
* = mercenary_camera_lesson;
* = mercenary_open_gate_morden;
}
}
[mind]
{
b actor_may_attack = false;
b actor_may_be_attacked = false;
movement_orders = mo_hold;
}
[placement]
{
q orientation = 0,-0.946931,0,0.32144;
p position = 2.24951,0.884001,2.652,0xeb3f2886;
}
}
This actor is hooked up to be able to talk, and he has conversations specified, so when you click on him the 'a0_tutbeach_gate_guard_talk' flick will launch. Here is that flick;
[a0_tutbeach_gate_guard_talk]
{
// set the roles for this sequence
role (actor) speaker, listener;
external role (prop) gate_01;
// set the startup thread for this sequence ( because there is more than 1 thread )
entry main;
thread main
{
speaker:
Capture;
if !WhenQuestBool( a1_p_tutorial_main, tutorial_gate_done )
{
branch gate_open_first_time;
}
else
{
StartConversation mercenary_open_gate_morden, wait;
}
}
thread gate_open_first_time
{
speaker:
StartConversation mercenary_open_gate_morden, wait;
if !WhenConversationChoice( speaker, cancel_conversation )
{
BroadcastMessage Speaker we_user_indicate_emitter_off; //turn off "!"
SetQuestBool( a1_p_tutorial_main, tutorial_gate_done, true );
activate gate_01; // unlock
activate gate_01; // open
CompleteTask a1_p_tutorial_main speak_jerind_2;
ActivateTask a1_p_tutorial_main train_melee_2;
ActivateTask a1_p_tutorial_main report_after_training;
SetMayAttack true;
SetMayBeAttacked true;
}
}
}
If the 'tutorial_gate_done bool' is set, the actor will play his 'mercenary_open_gate_morden' conversation. If the bool is not set, he will play the 'mercenary_open_gate_morden' conversation, turn off the '!' icon above his head, set the bool, unlock the gate, update some quest tasks, and become attackable. Because of the bool, the entire 'gate_open_first_time' thread will only run once. Most NPCs in DS2 have talk flicks; some are much simpler than this and some are vastly more complex.
Additional Flick Resources
References
Tutorials
- Introductory
- Intermediate
- Advanced
208: Weapon Effects
Tut: 1 - Getting Started