When you pull the book out, it touches the area in front of it. So in your script you will need to add a collision callback to check for this event, one for each book & area.
I'm sure they can all call the same code block event.
To start off, you'll want to start the timer and ticking noise. Next up, you'll want to add a local variable which will be used to count the amount of books currently positioned. By the end you'll add a check for when that variable is equal to 3, and run the completion event.
The timer needs to reset these values and all when it expires.
This is just me thinking it in my head, but I believe it will work just fine. But if you need help with the script, here's an example. Hopefully you understand it.
void OnStart()
{
for(int i = 1; i <= 3; i++) //this will loop the following line 3 times.
AddEntityCollideCallback("Book_"+i, "Area_"+i, "PullBook", false, 1);
//Adds callbacks for Book_1 and Area_1, and for 2 and 3.
}
void PullBook(string &in asParent, string &in asChild, int alState)
{
AddTimer("expire", 20.0f, "TimerExpired"); //Change time to how long it should last
//Add a PlaySoundAtEntity here
AddLocalVarInt("Books", 1); //This adds +1 to this variable for every book that collides with the area.
//Add something to make asParent not movable so that they dont cheat by
//using the same book several times to enter the area, like SetPropStaticPhysics
//or something better. Alternatively you can disable the area and reset it later.
if(GetLocalVarInt("Books") == 3) {
//This will complete the event, put your code here
RemoveTimer("expire");
//Run StopSound
}
}
void TimerExpired(string &in asTimer)
{
//This will reset the event because you ran out of time.
for(int i = 1; i <= 3; i++) ResetProp("Book_"+i);
SetLocalVarInt("Books", 0);//Resets the counter
//Run StopSound here.
//Run any additional resets here as well if you have any.
}
Disclaimer: I might've forgotten something.