[NWScript] SpellHook, comment ça marche ?

Répondre
Partager Rechercher
Le "SpellHook" est un système offert par les concepteurs de NWN pour nous permettre de modifier le système de sorts.

Il commence par une action simple : Définir un script principal (pour mon exemple, le script s'appellera "spells_custom")

Pour définir ce script, il y aura deux méthodes possibles. La première consiste à mettre une variable sur le module, dont la valeur (de type string) fera référence à un script :
Code:
Name         : X2_S_UD_SPELLSCRIPT  (respecter les majuscules)
ValueString  : spells_custom
VariableType : String
La seconde consiste à rajouter quelques lignes dans le script de l'événement du module "OnModuleLoad" :
Code PHP:

// Avant la partie "void main()", j'inclus la
// bibliothèque de fonctions "x2_inc_switches" :
#include "x2_inc_switches"

// Dans la partie "void main()", je définis mon script
// grâce à la fonction "SetModuleOverrideSpellscript(...)" :
SetModuleOverrideSpellscript("spells_custom"); 
Je pourrai ainsi créer mon script "spells_custom" dans lequel je pourrai faire toutes les modifications que je souhaite apporter au système de sorts.

Une fonction primordiale est "SetModuleOverrideSpellScriptFinished()" qui empêchera les scripts des sorts de se lancer. Pratique lorsque l'on veut interdire la magie à certains endroits, définir une composante matérielle, ou modifier le fonctionnement d'un ou plusieurs sorts. Cette fonction se placera en général à la fin de notre script de SpellHook. Pour utiliser cette fonction, il faudra faire appel à la bibliothèque de fonctions "x2_inc_switches".

Les fonctions suivantes fonctionneront comme pour le système de sorts standard :
- GetSpellCastItem() : Définit l'objet qui a servi à lancer le sort (s'il y a lieu)
- GetSpellFeatId() : Définit le don utilisé (s'il y a lieu)
- GetSpellId() : Définit quel sort est en train d'être lancé
- GetSpellLevel() : Définit le niveau du sort
- GetSpellResistance(...) : Définit la résistance magique de l'objet placé en paramètre
- GetSpellSaveDC() : Définit le seuil de difficulté pour un éventuel jet de sauvegarde
- GetSpellTargetLocation() : Définit la localisation ciblée (dans le cas d'un sort de zone, par exemple)
- GetSpellTargetObject() : Définit l'objet cible du sort

Sous réserve de tests, la fonction "GetLastSpellCastClass()" pourrait permettre de déterminer quel classe a été utilisée pour lancer le sort. Cette fonction s'utilise normalement lors d'un événement "OnSpellCastAt", mais il est possible qu'elle ait une utilité pendant l'incantation d'un sort.

Dans un script SpellHook, comme dans n'importe quel script de sort, la commande "OBJECT_SELF" définit le lanceur du sort.


Enfin, il ne reste plus qu'à déterminer quel sera le contenu de mon script "spells_custom" :
Code PHP:

#include "x2_inc_switches"

void main()
{
    
// imaginons que le joueur doive absolument porter une
    // bague (tag : "BAGUE_SORTS") pour lancer ses sorts :
    
object oBague1 GetItemInSlot(INVENTORY_SLOT_LEFTRINGOBJECT_SELF);
    
object oBague2 GetItemInSlot(INVENTORY_SLOT_RIGHTRINGOBJECT_SELF);

    
// Si le joueur n'a pas cette bague, on termine
    // le script et on signale au module que le script
    // du sort ne doit pas s'exécuter :
    
if(GetTag(oBague1)!="BAGUE_SORTS" && GetTag(oBague2)!="BAGUE_SORTS")
    {
        
SetModuleOverrideSpellScriptFinished()
        return;
    }


    
// Imaginons que le joueur se situe dans une zone
    // (tag : "ZONE_ANTIMAGIE") où la magie est interdite :
    
if(GetTag(GetArea(OBJECT_SELF)) == "ZONE_ANTIMAGIE")
    {
        
SetModuleOverrideSpellScriptFinished();
        return;
    }


    
// Imaginons que le sort "Boule de feu" doit être modifié :
    
if(GetSpellId() == SPELL_FIREBALL)
    {
        
// Modification du sort...
        // bla bla... etc. etc.

        
SetModuleOverrideSpellScriptFinished();

        
// Si le sort subit seulement quelques modifications, et que
        // le script original peut quand même s'exécuter, il suffit de
        // ne pas utiliser la fonction "SetModuleOverrideSpellScriptFinished()"

        
return;
    }


    
// Imaginons que le sort "Boule de feu" est lancé...
    
if(GetSpellId() == SPELL_FIREBALL)
    {
        
// ... et qu'il ne peut pas affeter un PNJ bien précis...
        
if(GetTag(GetSpellTargetObject()) == "PNJ_BIEN_PRECIS")
        {
            
SetModuleOverrideSpellScriptFinished();
            return;
        }

        
// ... ou qu'il nécessiste une composante matérielle (tag : "COMP_FIREBALL")
        
if(GetItemPossessedBy(OBJECT_SELF"COMP_FIREBALL") == OBJECT_INVALID)
        {
            
SetModuleOverrideSpellScriptFinished();
            return;
        }
    }

Voila voila.
__________________
Je déterre un topic mais vu que je bosse sur les sorts actuellement... je me trompe peut être mais :

A mon sens, vu que le script override se lance avant que le sort soit lancé.

Quand on récupère le SpellID, on ne sait pas encore de quel sort il s'agit.

Code PHP:

// Imaginons que le sort "Boule de feu" doit être modifié :
    
if(GetSpellId() == SPELL_FIREBALL)
    {
        
// Modification du sort...
        // bla bla... etc. etc.

        
SetModuleOverrideSpellScriptFinished();

        
// Si le sort subit seulement quelques modifications, et que
        // le script original peut quand même s'exécuter, il suffit de
        // ne pas utiliser la fonction "SetModuleOverrideSpellScriptFinished()"

        
return;
    } 
Du coup ce genre de chose ne fonctionne pas, ça va détecter le sort qui a été lancé précédemment.

(Je viens de me prendre la tête avec ça pour mieux comprendre...)

Exemple :

Je lance le sort Résistance, il va me détecter ID = -1 car c'est le premier sort que je lance.

Je lance le sort Boule de feu, il va me détecter ID = 151 (qui est l'ID du sort lancé précédemment => Résistance)

Donc c'est pas aussi simple que ça, je sais pas si d'autres ont essayé d'utiliser ce système d'override de sorts mais perso ça marche que comme je viens de l'expliquer. (cf. screen)

Voici le script que j'utilise :

Code PHP:

#include "x2_inc_switches"
#include "mod_inc_cym_system"
void main()
{
 
object oPC OBJECT_SELF;
 
 
int iSpellID GetSpellId();
 
 
SendMessageToPC(oPC"iSpellID : " IntToString(iSpellID));
 
 
int iSpellLevel GetSpellLevel(iSpellID); 
 
 
SendMessageToPC(oPC"iSpellLevel : " IntToString(iSpellLevel)); 

 
int iCasterLevel GetCasterLevel(oPC);  
 
 
SendMessageToPC(oPC"iCasterLevel : " IntToString(iCasterLevel));
 
 
int iSpellCymCost iSpellLevel 100
 
 
SendMessageToPC(oPC"iSpellCymCost : " IntToString(iSpellCymCost));
 
 if (!
GetIsPC(oPC) || GetIsDM(oPC) || GetIsDMPossessed(oPC)) 
  {
   return;    
  }
 
 if (
CS_CheckCym(oPCiSpellCymCost) != 1)
  {
   
SendMessageToPC(oPC"Vous ne possédez pas assez de Cym pour lancer ce sort.");
   
SetNoticeText(oPC"SORT INTERROMPU (Pas assez de Cym)");
   
SetModuleOverrideSpellScriptFinished();     
   return;
  }
 else
  {
   
CS_TakeCym(oPCiSpellCymCost);      
  } 

Je serais curieux d'avoir vos avis la dessus
Miniatures attachées
Cliquez sur l'image pour la voir en taille réelle

Nom : NWN2_SS_092215_180207.jpg
Taille : 1920x976
Poids : 225,1 Ko
ID : 253643  
Ça paraît bizarre. Je n'ai utilisé qu'une fois ce système et je n'avais pas remarqué ça.
Ton script est bien exécuté par le hook et pas par un évènement ? Voir ici: [use GetLastSpell() on OnSpellCast event]

En regardant les scripts par défaut de never, on dirait bien que l'intention de GetSpellId() est bien celle décrite dans le premier post.

nx2_s0_nightshield.nss:
Code PHP:

void main()
{
  if (!
X2PreSpellCastCode())
    return;
  
  
// ...
  
if (GetIsObjectValid(oTarget))
  {
    
// remove previous usages of this spell
    
RemoveEffectsFromSpell(oTargetGetSpellId()); 

    
// ...
  
}

Appelle x2_inc_spellhook.nss
Code PHP:

int X2PreSpellCastCode()
{
  
// ...
  
if (nContinue)
  {
    
//-----------------------------------------------------------------------
    // run any user defined spellscript here
    //-----------------------------------------------------------------------
    
nContinue X2RunUserDefinedSpellScript();
  }

  
// ...
  
return nContinue;
}

int X2RunUserDefinedSpellScript()
{
  
// See x2_inc_switches for details on this code
  
string sScript GetModuleOverrideSpellscript();
  if (
sScript != "")
  {
    
ExecuteScript(sScript,OBJECT_SELF);
    if (
GetModuleOverrideSpellScriptFinished() == TRUE)
      return 
FALSE;
  }
  return 
TRUE;

Ça a l'air bon non ? Ça serait bizarre que spell_id soit setté après le moment où on fait X2PreSpellCastCode().

Dernière modification par Lv4 ; 23/09/2015 à 00h34.
Oho il reste donc des gens par ici merci pour ton aide!

Alors en faite je charge le script d'override dans le load du module :

#lps_mod_load
Code PHP:

 // Spell Override
 
SetModuleOverrideSpellscript("lps_mod_spelloverride"); 
Ensuite le script lps_mod_spelloverride, c'est celui que j'ai mis au dessus.

Je viens de tester en mettant :

int iSpellID = GetLastSpell(); à la place de int iSpellID = GetSpellID();

Mais le problème reste le même, c'est vrai que c'est bizarre par rapport à ce que j'ai lu et ce que tu m'as donné comme informations
Pour le moment je ne peux pas tester chez moi. Voilà ce que je ferais si j'avais le même problème :
- reproduire sur un module sans scripts (hormis le hook) ;
- réécrire un script de sort en affichant le spell_id directement lors de l'appel ;
- réécrire un script de sort en exécutant le hook par fonction (sans ExecuteScript) ;
- regarder les librairies de script pour never (pour never1 il y avait HCR) qui utilisent/reprennent ce système.

Vu le module Lyncya, j'imagine que les règles sont fortement modifiés. Ça n'est pas bête de tenter de reproduire sur un module sans override, scripts, 2DA ou autre choses modifiées, peut-être même sur une nouvelle install (si tu as modifié les ressources de base du jeu).

Edit: il me semble que Skywing avait réécrit une partie du netcode pour never, ça pourrait peut-être influencer.
Après la réinstallation propre de nwn2, la recréation d'un module en cas de corruption, de contrôle du module.ifo, du campaign.cam

Finalement la solution était déjà énoncée, au lieu de charger l'override via script au load du module :

#lps_mod_load
Code PHP:

 // Spell Override
 
SetModuleOverrideSpellscript("lps_mod_spelloverride"); 
Il faut créer une variable sur le module, avec comme nom : X2_S_UD_SPELLSCRIPT

Et comme valeur de variable en string, le nom de votre script de hook (lps_mod_spelloverride pour moi)

Un grand merci à Lv4 et désolé d'avoir mis en doute le post de Deyo, comme dit j'étais pas sûr

Dernière modification par Mythyzyn ; 24/09/2015 à 11h07.
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés