Monster Grapples: Difference between revisions
No edit summary |
|||
| (4 intermediate revisions by the same user not shown) | |||
| Line 43: | Line 43: | ||
<pre> | <pre> | ||
#define | #define grappleOnSeated() grappleOnSeated() | ||
grappleOnSeated(){ | |||
// Start your animations here and set a timer to damage/arouse player | |||
// Use list targs = gotMonsterGrapple$getTargs(); for a list of grappled player HUDs. | |||
} | } | ||
</pre> | #define grappleOnEnd() grappleOnEnd() | ||
grappleOnEnd(){ | |||
// Stop animations here and unset timers | |||
} | |||
</pre>Finally you can setup your camera params and sit targets in your ini() function. | |||
=== Starting your grapple === | |||
There are two methods you can use to start a grapple (usually from a spell): | |||
* '''gotMonsterGrapple$start(targ, huds, fxFlags)''' - Starts a grapple on specified HUDs, and sends specified fxFlags to the targets. Targ is usually LINK_THIS. | |||
* '''gotMonsterGrapple$grappleClosestConal(targ, arc, range, grappleFlags, minPlayers, maxPlayers, debug)''' - Attempts to grapple the closest player(s) in a cone in front of the monster (positive X). Debug should be false unless you are debugging players, otherwise NPCs in a hookup pose will bypass the default condition checks such as if they have aggro on the victim. Targ is usually LINK_THIS. | |||
== Setting up a hookup == | |||
A hookup is a pose where two NPCs double team one or more players. The hookup is split into 3 parts: Host, Client, Player. The hookup may start after the host has successfully grappled the player. Once the grapple starts, the host sends out a message to nearby NPCs and if they pass conditions and have aggro on the player, the first one to respond is allowed to join. | |||
=== Setting up the host === | |||
The host must be able to do a normal grapple (see above). Once that is setup, the only thing you must setup on the host is a host name for the clients to identify. Something like the following: | |||
<pre> | |||
#define cf$hup$HOST "GOBLIN_BRANDER" | |||
</pre> | |||
The client manages the animations and effects of a hookup. But the host has access to a few things if you need them for any reason: | |||
<pre> | |||
#define grappleOnClientAnim(npcAnim, playerAnims) grappleOnClientAnim(npcAnim, playerAnims) | |||
grappleOnClientAnim(string npcAnim, list playerAnims){ | |||
// Raised when the client triggers an animation on the host. npcAnim is the host animation. | |||
} | |||
#define grappleOnHookup(hostname) grappleOnHookup(hostname) | |||
grappleOnHookup(str hostname){ | |||
// Raised when hooking up as a client, or hooked up to as a host | |||
// hostname is empty if we are hosting. Otherwise it is the hostname of the host we are the client for. | |||
} | |||
</pre> | |||
=== Setting up the client === | |||
The client may have hookups for multiple different hosts, and also multiple hookups for the same host. You set them up in your got LocalConf.NPC.template script ini function by calling cf$hup$addClientPose:<pre> | |||
// Add one of these for each pose that you want to add. | |||
cf$hup$addClientPose( | |||
"GOBLIN_BRANDER", // Hostname that this pose is for | |||
"<1,0,0>", // Our position relative to host. 1M in front of them. | |||
"<0,0,1,0>", // Our rotation relative to host. Rotate 180 degrees on Z to face them. | |||
"clientAnimLoop", // Looping animation to play on the client | |||
"pcAnimLoop", // Looping animation to play on PC. Can be a list if you want multiple players. | |||
"hostAnimLoop", // Looping animation to play on the host. | |||
0, // Resync. Can be used to force restart animations. But most often is best left at 0. | |||
"<1,1,0>", // Camera pos relative to host | |||
"<0,0,0>", // Camera target relative to host | |||
1, // NR player victims this pose supports | |||
[] // Conditions. See got MonsterGrapple.lsl gotMonsterGrappleHupCond for more info. | |||
); | |||
</pre> | |||
Then you need a grappleOnHookup handler and a grappleOnEnd handler. If your monster should both be able to grapple and hookup, then grappleOnEnd is used for both types of grapples. | |||
<pre> | |||
#define grappleOnEnd() grappleOnEnd() | |||
grappleOnEnd(){ | |||
// Stop your grapple and hookup animations here | |||
} | |||
#define grappleOnHookup( hostname ) grappleOnHookup(hostname) | |||
grappleOnHookup(string hostname){ | |||
if( hostname == "" ){ | |||
// A grapple has started and we are hosting. We often do not need to do anything here. | |||
} | |||
else{ | |||
if( hostname == "GOBLIN_BRANDER" ){ | |||
// Set a timer here to do the damage, play sounds and animations etc. | |||
// Use gotMonsterGrapple$hostAnim(pcAnim, hostAnim) to trigger an animation on host/player(s) | |||
// Use list huds = gotMonsterGrapple$getHupTarg(); to get the grappled HUDs | |||
} | |||
} | |||
} | |||
</pre> | |||
==== Custom conditions: ==== | |||
If you are not content with the built in conditions, you can use the following in the client to test before allowing a grapple: | |||
<pre> | |||
#define grappleOnHupClientTest( poseIndexes ) grappleOnHupClientTest( poseIndexes) | |||
list grappleOnHupClientTest( list poseIndexes ){ | |||
list huds = gotMonsterGrapple$getTargs(); | |||
string hostname = gotMonsterGrapple$getHupName(); | |||
list out; integer i = count(poseIndexes); | |||
while( i-- ){ | |||
list grappleData = gotMonsterGrapple$getHupClientPosePose(l2i(poseIndexes, i)); | |||
// Use the gotMonsterGrappleConst$hup$* to get information about the grapple that you are testing. See got MonsterGrapple.lsl | |||
if( /*...something*/ ) | |||
out += l2i(poseIndexes, i); | |||
} | |||
return out; // return the viable poses | |||
} | |||
</pre> | |||
[[Category:Tutorials]] | |||
Latest revision as of 14:27, 10 April 2024
GoThongs 2.3 introduced a dedicated grapple handler to help offload the hard-coded monster grapples that already exist. Here is a primer on how to use the system together with got LocalConf.NPC.template
Terminology
- Grapple: A monster grabs one or more players, forcing them to sit on the object and is usually accompanied by a quick time event and some kind of status effect.
- Quick time event: The keypad that pops up on your screen. Can be either "tap specific keys" or "tap left-right alternating". The alternating mode can also be set to allow fail.
- Hookup: A quick time event where two monsters gang up on one player.
- Hookup Host: The monster that grapples a player (PC) in a hookup.
- Hookup Client: The monster that joins the host in a hookup.
- Hostname: A unique identifier for the host that is used by the client to tell if they can join or not.
Any NPC that should be able to grapple MUST have the optional got MonsterGrapple script in its inventory.
Setting up a grapple
If you want the monster to grapple one or more players. Note that this step is not needed if you only want your NPC to be a client for hookups.
Start by defining any of these that you want to use in your got LocalConf.NPC.template script. If not defined, the default values are used.
#define cf$GRAPPLE_FLAGS // int - Grapple settings flags. These are the flags used by got Evts quick time events to set things like left/right events. #define cf$GRAPPLE_DURATION // float - Grapple max duration = 300. Max duration before force releasing the players. #define cf$GRAPPLE_PREDELAY // float - Grapple predelay = 0. Time before starting the QTE after grapple starts. #define cf$GRAPPLE_BUTTON_DELAY // float - Grapple button delay = 0. Used in normal QTEs to set a delay before buttons. #define cf$GRAPPLE_STAGES // int - QTE stages, alternatively speed for quick grapples. = 30 #define cf$GRAPPLE_MONSTERFLAGS // int - Monster to set when grapple starts and unset when it ends = Monster$RF_IMMOBILE|Monster$RF_PACIFIED|Monster$RF_NOROT|Monster$RF_NO_SPELLS #define cf$GRAPPLE_STRIP // bool - Strip clothes during the grapple #define cf$GRAPPLE_NO_QTE // bool - No QTE. call grappleEnd manually #define cf$GRAPPLE_FAIL_TIMEOUT // float - On failable grapples, wait this long before unsitting the player.
Next you should define event handlers:
#define grappleOnStart() // A grapple has been started but players may not be seated. You may use this to turn off custom NPC logic. #define grappleOnSeated() // All players are seated. You can now trigger animations! Get the players with gotMonsterGrapple$getTargs() #define grappleOnEnd() #define grappleOnQteFinish(bool success) // #define grappleOnButton(key victim_hud, bool success) // Used a classic QTE and the player hit a button #define grappleOnClientAnim(str npcAnim, list playerAnims) // Raised when client triggers an animation on the host. Raised on both host and client.
A basic way of doing it is something like this
#define grappleOnSeated() grappleOnSeated()
grappleOnSeated(){
// Start your animations here and set a timer to damage/arouse player
// Use list targs = gotMonsterGrapple$getTargs(); for a list of grappled player HUDs.
}
#define grappleOnEnd() grappleOnEnd()
grappleOnEnd(){
// Stop animations here and unset timers
}
Finally you can setup your camera params and sit targets in your ini() function.
Starting your grapple
There are two methods you can use to start a grapple (usually from a spell):
- gotMonsterGrapple$start(targ, huds, fxFlags) - Starts a grapple on specified HUDs, and sends specified fxFlags to the targets. Targ is usually LINK_THIS.
- gotMonsterGrapple$grappleClosestConal(targ, arc, range, grappleFlags, minPlayers, maxPlayers, debug) - Attempts to grapple the closest player(s) in a cone in front of the monster (positive X). Debug should be false unless you are debugging players, otherwise NPCs in a hookup pose will bypass the default condition checks such as if they have aggro on the victim. Targ is usually LINK_THIS.
Setting up a hookup
A hookup is a pose where two NPCs double team one or more players. The hookup is split into 3 parts: Host, Client, Player. The hookup may start after the host has successfully grappled the player. Once the grapple starts, the host sends out a message to nearby NPCs and if they pass conditions and have aggro on the player, the first one to respond is allowed to join.
Setting up the host
The host must be able to do a normal grapple (see above). Once that is setup, the only thing you must setup on the host is a host name for the clients to identify. Something like the following:
#define cf$hup$HOST "GOBLIN_BRANDER"
The client manages the animations and effects of a hookup. But the host has access to a few things if you need them for any reason:
#define grappleOnClientAnim(npcAnim, playerAnims) grappleOnClientAnim(npcAnim, playerAnims)
grappleOnClientAnim(string npcAnim, list playerAnims){
// Raised when the client triggers an animation on the host. npcAnim is the host animation.
}
#define grappleOnHookup(hostname) grappleOnHookup(hostname)
grappleOnHookup(str hostname){
// Raised when hooking up as a client, or hooked up to as a host
// hostname is empty if we are hosting. Otherwise it is the hostname of the host we are the client for.
}
Setting up the client
The client may have hookups for multiple different hosts, and also multiple hookups for the same host. You set them up in your got LocalConf.NPC.template script ini function by calling cf$hup$addClientPose:
// Add one of these for each pose that you want to add. cf$hup$addClientPose( "GOBLIN_BRANDER", // Hostname that this pose is for "<1,0,0>", // Our position relative to host. 1M in front of them. "<0,0,1,0>", // Our rotation relative to host. Rotate 180 degrees on Z to face them. "clientAnimLoop", // Looping animation to play on the client "pcAnimLoop", // Looping animation to play on PC. Can be a list if you want multiple players. "hostAnimLoop", // Looping animation to play on the host. 0, // Resync. Can be used to force restart animations. But most often is best left at 0. "<1,1,0>", // Camera pos relative to host "<0,0,0>", // Camera target relative to host 1, // NR player victims this pose supports [] // Conditions. See got MonsterGrapple.lsl gotMonsterGrappleHupCond for more info. );
Then you need a grappleOnHookup handler and a grappleOnEnd handler. If your monster should both be able to grapple and hookup, then grappleOnEnd is used for both types of grapples.
#define grappleOnEnd() grappleOnEnd()
grappleOnEnd(){
// Stop your grapple and hookup animations here
}
#define grappleOnHookup( hostname ) grappleOnHookup(hostname)
grappleOnHookup(string hostname){
if( hostname == "" ){
// A grapple has started and we are hosting. We often do not need to do anything here.
}
else{
if( hostname == "GOBLIN_BRANDER" ){
// Set a timer here to do the damage, play sounds and animations etc.
// Use gotMonsterGrapple$hostAnim(pcAnim, hostAnim) to trigger an animation on host/player(s)
// Use list huds = gotMonsterGrapple$getHupTarg(); to get the grappled HUDs
}
}
}
Custom conditions:
If you are not content with the built in conditions, you can use the following in the client to test before allowing a grapple:
#define grappleOnHupClientTest( poseIndexes ) grappleOnHupClientTest( poseIndexes)
list grappleOnHupClientTest( list poseIndexes ){
list huds = gotMonsterGrapple$getTargs();
string hostname = gotMonsterGrapple$getHupName();
list out; integer i = count(poseIndexes);
while( i-- ){
list grappleData = gotMonsterGrapple$getHupClientPosePose(l2i(poseIndexes, i));
// Use the gotMonsterGrappleConst$hup$* to get information about the grapple that you are testing. See got MonsterGrapple.lsl
if( /*...something*/ )
out += l2i(poseIndexes, i);
}
return out; // return the viable poses
}