Worldedit
  Worldedit
Le site sur l'éditeur de warcraft 3 !
 
  FAQFAQ   RechercherRechercher   Liste des MembresListe des Membres    Groupes d'utilisateursGroupes d'utilisateurs   medals.php?sid=6b4da9b0164caac339f7bb24df863904Médailles   S'enregistrerS'enregistrer 
 ProfilProfil   Se connecter pour vérifier ses messages privésSe connecter pour vérifier ses messages privés   ConnexionConnexion 
  FAQFAQ World Editor   UploadUploader une map ou une image    UploadAjouter sa map à l'annuaire   UploadConsulter l'annuaire

Les sons: exemple des projectiles

 
Poster un nouveau sujet   Répondre au sujet    Worldedit Index du Forum -> Tutoriels
Voir le sujet précédent :: Voir le sujet suivant  
Auteur Message
 jk2pach
Invité








MessagePosté le: 12/08/10 06:57    Sujet du message: Les sons: exemple des projectiles Citer

I/ Introduction

Situation : un membre du forum (Nuage) n’arrive pas à entendre le son qu’il aimerait pour une unité (tourelle d’un Tower Defence) lorsque celle-ci attaque une autre unité (un monstre).

Il veut que le son d’attaque de l’unité Marine (qui est le même que celui de l’attaque 1 de la Machine Volante) soit joué, pour produire ainsi le bruit d’une mitrailleuse.

Objectif de ce tutoriel : présenter les différentes méthodes pour faire écouter aux joueurs un son à partir de l’attaque d’une unité.

Je suivrai donc l’exemple du cas de Nuage pour présenter ces méthodes, avec chacune leurs avantages et inconvénients.

Il n’y a pas de pré-requis pour l’ensemble du tutorial, mais ils seront indiqués selon les différentes méthodes.

Note : encore une fois, l’exemple montre comment entendre un bruit de mitrailleuse au cours de l’attaque d’un monstre par une tour. Mais les méthodes présentées fonctionnent pour tout type de lecture de son 3D.

Note 2 : pour éviter que le tutoriel parte dans tous les sens et pour des raisons évidentes de simplification, on va partir sur un exemple encore plus précis : la tour utilisera le modèle de Cercle de Puissance et le projectile celui de l’attaque 1 de la Machine Volante.

La tour
Unités/Neutre passif/Campagne/Bâtiments/Cercle de puissance. ID entier associé : ‘ncop’
Chemin dans l’archive MPQ de Warcraft 3 :
Buildings\Other\CircleOfPower\CircleOfPower.mdx

Le projectile:
Unités-Projectiles /Machine Volante (le premier des deux proposés)
Chemin dans l’archive MPQ de Warcraft 3 :
Abilities\Weapons\GyroCopter\GyroCopterImpact.mdx

Vous pouvez faire le test, si vous utilisez une tour avec ce modèle en lui donnant comme attaque le projectile ci-dessus, vous aurez bien les effets visuels, mais aucun son.

Voyons maintenant comment entendre notre son, qui, manifestement, n’existe pas ni sur la tour, ni sur le projectile.

II/ Modifier le modèle de l’unité

Pré-requis

Le programme Warcraft 3 Model Editor

Conseil

Lire le tutoriel de Rhadamante sur Warcraft 3 Model Editor
On va commencer par ouvrir Warcraft 3 Model Editor. J’espère que vous savez faire.



On ne perd pas de temps : le but est de retrouver le modèle du cercle de puissance.
Allez donc cliquer sur Window/MPQ Browser.
Ce menu permet de consulter l’ensemble des fichiers présents dans l’archive MPQ de Warcraft 3.



Il faut sélectionner le bon MPQ. Je vous aide, on va trouver le cercle de puissance dans War3 (le MPQ de Reign Of Chaos).

Ensuite, il faut chercher notre modèle.
Le notre se situe ici : Buildings\Other\CircleOfPower\CircleOfPower.mdx



Une fois que vous avez ouvert votre fichier, vous pouvez déjà l’enregistrer quelque part en faisant File/Save As comme sur n’importe quel programme.



Histoire de passer aux choses sérieuses:

Dans un Modèle de Warcraft 3, on trouve un certain nombre de propriétés appellées « Nodes ». Un Notes, ça peut être un ensemble de points du modèles (Bones), un émetteur de Particules, un Son etc…
On va donc ouvrir le module de Warcrat 3 Model Editor appelé Node Manager



Vous devriez voir la meme fenêtre que sur ma capture d’écran.
Il y a une série d’objets appelés Plane avec un symbole d’os. Ce sont des Bones (os en Français), en général on s’en sert pour grouper des séries de points (ceux-là formant le design du modèle).
Omni01 est une lumière. Ca va créer un effet d’éclairage.
Dummy01 avec un point d’exclamation, c’est un Helper. On va le considérer comme un Bone : on s’en fiche toujours.
Sprite Rallye Point Ref, avec un trombone, c’est un attachement. Si vous lancez un sort avec comme infographie-cible l’attachement Sprite Rallye Point Ref, l’effet du sort sera créé sur ce point du modèle.
Bref, toujours pas pour nous.
La boîte appelée Collision, c’est pareil, elle ne nous concerne pas.

Mais alors, où est le son de l’unité ?
La réponse : elle n’en n’a pas. On va donc le créer.


Avantage
Le son est intégré dans le modèle, donc il se joue sans problème pour toutes les unités de la carte, en temps réel.

Inconvénient

Si vous ne savez pas éditer les modèles, vous allez devoir apprendre à créer des Event Objects via Warcraft 3 Model Editor par exemple. En plus cela oblige à importer les modèles, donc à alourdir la carte.

III/ Modifier le modèle du projectile


2-Vous devez rajouter un Event Object au modèle du projectile que vous voulez utiliser (essayer avec ce modèle édité: http://communots.free.fr/upload/GyroCopterImpact12650250.mdx )

Avantage

Le son est intégré dans le modèle, donc il se joue sans problème pour toutes les unités de la carte, en temps réel.
Contrairement à l'importation d'unités complètes, les modèles de projectiles sont bien plus légers.

Inconvénient

Si vous ne savez pas éditer les modèles, vous allez devoir apprendre à créer des Event Objects via Warcraft 3 Model Editor par exemple, pour chaque projectile que vous voulez utiliser.

IV/ Remplacer le son d’un modèle avec son intégré


3-Vous utilisez un projectile qui utilise déjà un son, et vous remplacez le son utilisé par ce projectile par celui de votre choix.
Vous devez donc importer un son et lui donner le chemin complet de celui utilisé par le projectile en chemin personnalisé via l'éditeur d'importation.

Avantage

N'oblige pas à modifier des modèles et à les importer.

Inconvénient

Les poids des sons importés sont élevés en général, pour pas grand chose de plus au final.
Cela oblige à trouver un projectile (n'importe quel modèle du jeu) pour lequel vous vous moquez du son, donc que vous n'allez pas utiliser plus tard: par exemple si vous choisissez d'utiliser le modèle de Feu Sacré, à chaque fois que ce modèle servira, il aura votre son de sulfateuse.
En plus vous allez devoir trouver le chemin complet du son à remplacer quand vous importerez le votre.

V/ Jouer le son par déclencheur en Gui


4-Vous jouez un son par déclencheur en détectant l'attaque de la tour en question. A priori vous pouver coder en Gui donc la seule solution offerte par le Gui c'est :
Secret:

Gui:
Trigger:
Untitled Trigger 001
Events
Unit - A unit Is attacked
Collapse Conditions
(Unit-type of (Attacking unit)) Equal to Goblin Sulfate-Machine
Collapse Actions
Sound - Play Sulfateuse <gen> at 100.00% volume, attached to (Attacking unit)



Avantage

Méthode la plus "légère": aucune modification à faire sur les modèles et les projectiles, juste un petit déclencheur.

Inconvénient

Warcraft 3 gère mal les sons. Du coup, en utilisant ces fonctions, il ne peut y avoir qu'un seul son 3D joué à la fois en Gui. Si vous avez 30 tourelles qui tirent toutes les 0.05 secondes, une grosse partie des sons ne sera pas jouée.
En plus, le son ne sera joué que lorsque la cible de ta tour sera attaquée. Si vous utilisez des projectiles lents sur des distances importantes, ça va faire bizarre d'entendre le son du canon en train de tirer alors que le projectile sera seulement en train d'arriver sur la cible.

VI/ Jouer le son par déclencheur en Jass


5-Il faut apprendre le Jass et le Vjass et vous servir de la library SoundUtils. Nécessite le JassNewGenPack, la library Stack et la library TimerUtils en support.
Pour l'installation d'une library: vous créez un déclencheur. Vous le sélectionnez, faites Edition/Convertir en texte personnalisé. Vous supprimez tout le contenu du déclencheur convertit. Vous copiez le code de la library.
Vous copiez SoundUtils
Secret:

Jass:

library SoundUtils requires Stack, TimerUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* Sounds are a very picky datatype in WC3. They have many quirks that one must
//* account for in order to use them, and simply using the internal WE Sound
//* Editor isn't enough because the sounds it makes can't be played multiple
//* times at once. 3-D sounds are also very tricky because there are different
//* WC3 sound options that a user can have activated where certain sounds will
//* or will not work. This library attempts to streamline the handling of sounds
//* so that it is less likely to confuse you or cause problems.
//*
//* The .mp3 format can be used for 3-D sounds, but there is one problem that
//* must be noted. If your computer supports the "Dolby Surround" sound option
//* in WC3 and you have it selected, then .mp3 files will work for 3-D sounds.
//* If you don't, however, they may not work depending on what you do have
//* selected and what is available for your computer. The .wav format works on
//* all possible settings, making them excellent for general use. This library
//* can interface with sounds of either type.
//*
//* Known issues with sounds that this library resolves:
//*     - A given sound variable can only be played once at a time. In order to
//*       play a sound type multiple times at once, you need multiple variables.
//*     - A sound cannot be played at the same instant that it is created.
//*
//* The DefineSound function defines a sound type based on some basic parameters
//* the user provides. DefineSoundEx is available if the user wants control over
//* all possible parameters, though they won't have an impact most of the time.
//* The duration parameter for DefineSound and DefineSoundEx is in milliseconds,
//* which is consistent with Blizzard's natives. To get the duration of a given
//* sound, open up the WE's Sound Editor, navigate to your sound, and select
//* "Add as Sound." In doing so, it will show its duration in seconds. Multiply
//* that number by 1000 and use it as the duration argument.
//*
//* This library returns a sound variable with RunSound that you can change the
//* settings of using the standard JASS sound API. The library assigns default
//* values to the parameters for 2-D and 3-D sounds, that way they will run
//* without any further help.
//*
//* The library automatically allocates, runs, and recycles a sound when you
//* call RunSound. This library will not automatically recycle looping sounds,
//* so you will need to call ReleaseSound on the looping sound when you want it
//* to end.
//*
//******************************************************************************
//*
//*    > function DefineSound takes string fileName, integer duration, ...
//*        boolean looping, boolean is3D returns integer
//*
//* This function defines a sound type with a short list of parameters. The
//* returned integer serves as a SOUND_TYPE for running this type of sound at
//* any other point in a map.
//*
//*    > function DefineSoundEx takes string fileName, integer duration, ...
//*        boolean looping, boolean is3D, boolean stopwhenoutofrange, ...
//*        integer fadeInRate, integer fadeOutRate, string eaxSetting ...
//*        returns integer
//*
//* This function serves an identical purpose to DefineSound, but gives the user
//* full control over the entire list of parameters. Similar to DefineSound, the
//* returned integer serves as a SOUND_TYPE for running this type of sound.
//*
//*    > function RunSound takes integer soundRef returns sound
//*
//* This function runs a sound with the parameters held within the soundRef
//* integer argument. The soundRef argument is the returned value of DefineSound
//* or DefineSoundEx.
//*
//*    > function RunSoundOnUnit takes integer soundRef, unit whichUnit returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type on a
//* specified unit.
//*
//*    > function RunSoundAtPoint takes integer soundRef, real x, real y, real z returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type at a
//* specified point in 3D space.
//*
//*    > function RunSoundForPlayer takes integer soundRef, player p returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type only
//* for the specified player.
//*
//*    > function ReleaseSound takes sound s returns boolean
//*
//* This function need only be called on looping sounds. If a sound is not
//* looping, it will be released and recycled on its own. This function should
//* be used on looping sounds when you want them to end.
//*
//* Example usage:
//*     set SOUND_TYPE = DefineSound("Sound\\Path.wav", 300, false, true)
//*     call RunSound(SOUND_TYPE)
//*     call RunSoundOnUnit(SOUND_TYPE, SomeUnit)
//*     call RunSoundAtPoint(SOUND_TYPE, x, y, z)
//*     call RunSoundForPlayer(SOUND_TYPE, Player(5))
//*     call ReleaseSound(SomeLoopingSound)
//*
globals
    private hashtable ht = InitHashtable() //Attach sound types to sounds
    private hashtable st = InitHashtable() //Sound hashtable
    private hashtable rt = InitHashtable() //Attach soundrecyclers to sounds
    private hashtable kt = InitHashtable() //Attach StopSound data
endglobals

//Struct for each sound type
private struct soundhelper
    //Stack associated to each struct
    Stack   sta
   
    //Sound Settings for this sound type
    string  fileName           = ""
    integer duration           = 0
    boolean looping            = false
    boolean is3D               = false
    boolean stopwhenoutofrange = false
    integer fadeInRate         = 0
    integer fadeOutRate        = 0
    string  eaxSetting         = ""
   
    static method create takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopwhenoutofrange, integer fadeInRate, integer fadeOutRate, string eaxSetting returns soundhelper
        local soundhelper sh      = soundhelper.allocate()
        //Load the parameters so the sound can be created later as necessary
        set sh.fileName           = fileName
        set sh.duration           = duration
        set sh.looping            = looping
        set sh.is3D               = is3D
        set sh.stopwhenoutofrange = stopwhenoutofrange
        set sh.fadeInRate         = fadeInRate
        set sh.fadeOutRate        = fadeOutRate
        set sh.eaxSetting         = eaxSetting
        //Create the stack for the struct
        set sh.sta                = Stack.create()
        return sh
    endmethod
endstruct

//Struct for holding data for the sound recycling
private struct soundrecycler
    timer   t       = null
    sound   s       = null
    integer sh      = 0
    boolean stopped = false //Only gets used if StopSound is called on a new sound
   
    static method create takes sound whichSound, integer soundRef returns soundrecycler
        local soundrecycler sr = soundrecycler.allocate()
        set sr.t       = NewTimer()
        set sr.s       = whichSound
        set sr.sh      = soundRef
        call SetTimerData(sr.t, integer(sr))
       
        //Hook the value to the soundRef and whichSound
        call SaveInteger(rt, soundRef, GetHandleId(whichSound), integer(sr))
        return sr
    endmethod
    private method onDestroy takes nothing returns nothing
        call RemoveSavedInteger(rt, .sh, GetHandleId(.s))
        call ReleaseTimer(.t)
    endmethod
endstruct

//******************************************************************************

private function HookStopSound takes sound soundHandle, boolean killWhenDone, boolean fadeOut returns nothing
    local integer       id       = GetHandleId(soundHandle)
    local integer       soundRef = 0
    local soundrecycler sr       = 0
    if HaveSavedInteger(ht, 0, id) then //Sound is from stacks
        set soundRef = LoadInteger(ht, 0, id)
        if HaveSavedInteger(rt, soundRef, id) then //Sound has a recycler
            set sr         = soundrecycler(LoadInteger(rt, soundRef, id))
            set sr.stopped = true
        endif
        if killWhenDone then
            debug call BJDebugMsg(SCOPE_PREFIX+"Warning: (StopSound) Destroying a sound in the stack")
        endif
    endif
endfunction

hook StopSound HookStopSound

private function HookKillSoundWhenDone takes sound soundHandle returns nothing
    if HaveSavedInteger(ht, 0, GetHandleId(soundHandle)) then
        call BJDebugMsg(SCOPE_PREFIX+"Warning: (KillSoundWhenDone) Destroying a sound in the stack")
    endif
endfunction

debug hook KillSoundWhenDone HookKillSoundWhenDone

//******************************************************************************

function DefineSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopwhenoutofrange, integer fadeInRate, integer fadeOutRate, string eaxSetting returns integer
    return integer(soundhelper.create(fileName, duration, looping, is3D, stopwhenoutofrange, fadeInRate, fadeOutRate, eaxSetting))
endfunction
function DefineSound takes string fileName, integer duration, boolean looping, boolean is3D returns integer
    return DefineSoundEx(fileName, duration, looping, is3D, true, 10, 10, "CombatSoundsEAX")
endfunction

function ReleaseSound takes sound s returns boolean
    local integer       id       = GetHandleId(s)
    local integer       soundRef = 0
    local soundhelper   sh       = 0
    local soundrecycler sr       = 0
   
    if s == null then
        debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot recycle a null sound")
        return false
    elseif not HaveSavedInteger(ht, 0, id) then
        debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot recycle a sound not allocated by RunSound")
        return false
    endif
   
    set soundRef = LoadInteger(ht, 0, id)
    set sh       = soundhelper(soundRef)
   
    call StopSound(s, false, true)             //Stop the sound
    call sh.sta.push(id)                       //Return it to the stack
    call SaveSoundHandle(st, soundRef, ids) //Save it to hashtable
    if not sh.looping then
        //soundrecycler only exists for non-looping sounds
        set sr = soundrecycler(LoadInteger(rt, soundRef, id))
        call sr.destroy()                      //Destroy recycler helper
    endif
    return true
endfunction

private function Recycle takes nothing returns nothing
    local soundrecycler sr = soundrecycler(GetTimerData(GetExpiredTimer()))
    local soundhelper   sh = soundhelper(sr.sh)
    local integer       id = GetHandleId(sr.s)
   
    call StopSound(sr.s, false, true)               //Stop the sound
    call sh.sta.push(id)                            //Return it to the stack
    call SaveSoundHandle(st, integer(sh), id, sr.s) //Save it to hashtable
    call sr.destroy()                               //Destroy recycler helper
endfunction

private function Run takes nothing returns nothing
    local soundrecycler sr = soundrecycler(GetTimerData(GetExpiredTimer()))
    local soundhelper   sh = soundhelper(sr.sh)
   
    if not sr.stopped then
        call StartSound(sr.s) //Play sound here
    endif
    if not sh.looping and not sr.stopped then
        call TimerStart(sr.t, sh.duration*0.001, false, function Recycle)
    else
        call sr.destroy()
    endif
endfunction

function RunSound takes integer soundRef returns sound
    local sound         s  = null
    local integer       i  = 0
    local soundhelper   sh = soundhelper(soundRef)
    local soundrecycler sr = 0
   
    if soundRef <= 0 then
        debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot run sound of undefined type")
        return null
    endif
    //Check if the stack is empty
    if sh.sta.peek() == Stack.EMPTY then
        //Create a new sound for the stack
        set s = CreateSound(sh.fileName, sh.looping, sh.is3D, sh.stopwhenoutofrange, sh.fadeInRate, sh.fadeOutRate, sh.eaxSetting)
        //Attach the type to the sound for future reference
        call SaveInteger(ht, 0, GetHandleId(s), integer(sh))
        call SetSoundDuration(s, sh.duration)
       
        //Stuff that must be performed immediately upon creation of sounds
        call SetSoundChannel(s, 5)
        call SetSoundVolume(s, 127)
        call SetSoundPitch(s, 1.)
        if sh.is3D then
            //These are settings necessary for 3-D sounds to function properly
            //You can change them at will outside of this function
            call SetSoundDistances(s, 600., 10000.)
            call SetSoundDistanceCutoff(s, 3000.)
            call SetSoundConeAngles(s, 0., 0., 127)
            call SetSoundConeOrientation(s, 0., 0., 0.)
        endif
       
        //Start sound after a delay because it was created here
        set sr = soundrecycler.create(s, soundRef)
        call TimerStart(sr.t, 0.001, false, function Run)
    else
        //Allocate a sound from the stack
        set i = sh.sta.pop()
        if not HaveSavedHandle(st, soundRef, i) then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: No sound in given stack member")
            return null
        endif
        set s = LoadSoundHandle(st, soundRef, i)
        call RemoveSavedInteger(st, soundRef, i)
        call SetSoundVolume(s, 127) //Start volume at max
       
        //Start it here since it wasn't created here
        call StartSound(s)
        //Recycle the sound in a timer callback after it's finished if nonlooping
        if not sh.looping then
            set sr = soundrecycler.create(s, soundRef)
            call TimerStart(sr.t, sh.duration*0.001, false, function Recycle)
        endif
    endif
    return s
endfunction

function RunSoundOnUnit takes integer soundRef, unit whichUnit returns sound
    local sound s = RunSound(soundRef)
    call AttachSoundToUnit(s, whichUnit)
    return s
endfunction

function RunSoundAtPoint takes integer soundRef, real x, real y, real z returns sound
    local sound s = RunSound(soundRef)
    call SetSoundPosition(s, x, y, z)
    return s
endfunction

function RunSoundForPlayer takes integer soundRef, player p returns sound
    local sound s = RunSound(soundRef)
    if GetLocalPlayer() != p then
        call SetSoundVolume(s, 0)
    else
        call SetSoundVolume(s, 127)
    endif
    return s
endfunction
endlibrary
 



SoundsUtils nécessite également Stack (idem)

Secret:

Jass:
library Stack

//*****************************************************************
//*  STACK
//*
//*  written by: Anitarf
//*
//*  This is an efficient implementation of a stack in vJass. Since
//*  it is based on a linked list, I decided to add common list
//*  methods to the data structure so it can function both as
//*  a stack and a simple linked list.
//*
//*  As a linked list, it has less functionality than Ammorth's
//*  LinkedList, but is considerably faster. Note only that most of
//*  the list methods have O(n) time complexity so they may not be
//*  suitable for operations on very large lists, however due to
//*  their simplicity the list would need to be really large for
//*  this to become a problem.
//*
//*  All stack methods are of course O(1) and as fast as possible.
//*  If you just need a stack, this is definitely the best choice.
//*
//*   set s=Stack.create()  - Instanceates a new Stack object.
//*   call s.destroy()      - Destroys the Stack.
//*
//*  Stack syntax:
//*   call s.push(123)      - Pushes the value 123 on the stack.
//*                           A stack may contain multiple
//*                           instances of the same value.
//*   set i=s.peek()        - Reads the top value of the stack
//*                           and stores it to the variable i.
//*   set i=s.pop()         - Removes the top value from the stack
//*                           and stores it to the variable i.
//*   s.peek()==Stack.EMPTY - Checks if the stack is empty.
//*
//*  List syntax:
//*   call s.add(123)       - Adds the value 123 to the list.
//*                           A list may contain multiple
//*                           instances of the same value.
//*   s.size                - The total number of values on the list.
//*   s.contains(123)       - Checks if the value 123 is on the list.
//*   set n=s.count(123)    - Stores the number of times the value
//*                           123 is on the list to the variable n.
//*   call s.remove(123)    - Removes one instance of the value 123
//*                           from the list. Returns false if
//*                           the value was not found on the list.
//*   call s.purge(123)     - Removes all instances of the value 123
//*                           from the list. Returns the number of
//*                           values that were removed.
//*   set i=s.first         - Reads the first value from the list
//*                           and stores it to the variable i.
//*   set i=s.random        - Reads a random value from the list
//*                           and stores it to the variable i.
//*   set s2=s.copy()       - Makes a copy of the list and stores
//*                           it to the variable s2.
//*   call s.enum(Func,b)   - Calls function Func for all values
//*                           on the list. The function must follow
//*                           the Enum function interface.
//*                           b is a boolean value, if it is true
//*                           then the values will be enumerated
//*                           top to bottom, if false then bottom
//*                           to top.
//*****************************************************************

    public function interface Enum takes integer value returns nothing

    struct Stack
        // Use a totally random number here, the more improbable someone uses it, the better.
        // This is the value that is returned by .pop and .peek methods and .first and .random operators when called on an empty stack.
        public static constant integer EMPTY=0x28829022

        // End of calibration.

        readonly integer size = 0
        private integer top = 0
        private static integer free = 1
        private static integer array next
        private static integer array value
       
        method push takes integer i returns nothing
            // Get an index from the list of free indexes.
            local integer n=Stack.free
            set Stack.free=Stack.next[n]
            // Extend the list of free indexes if needed.
            if Stack.free==0 then
                set Stack.free=n+1
            endif
            // Store the value to the index.
            set Stack.value[n]=i
            // Add index to the top of the stack.
            set Stack.next[n]=.top
            set .top=n
            set .size=.size+1
        endmethod
        method pop takes nothing returns integer
            // Get the top index of stack.
            local integer n=.top
            // Safety check in case a user pops an empty stack.
            if n==0 then
                debug call BJDebugMsg("stack warning: .pop called on an empty stack!")
                return Stack.EMPTY
            endif
            // Remove the top index from stack.
            set .top=Stack.next[n]
            set .size=.size-1
            // Add the index to the list of free indexes.
            set Stack.next[n]=Stack.free
            set Stack.free=n
            // Return the value.
            return Stack.value[n]
        endmethod
        method peek takes nothing returns integer
            // Read the value of the top index.
            return Stack.value[.top]
        endmethod


        method add takes integer value returns nothing
            call .push(value)
        endmethod
        method contains takes integer value returns boolean
            // Get the first index of the list.
            local integer i=.top
            // Search through the list.
            loop
                // Stop the search when the end of the list is reached.
                exitwhen i==0
                // Stop the search if the value is found.
                if Stack.value[i]==value then
                    return true
                endif
                // Get the next index of the list.
                set i=Stack.next[i]
            endloop
            return false
        endmethod
        method count takes integer value returns integer
            local integer count=0
            // Get the first index of the list.
            local integer i=.top
            // Search through the list.
            loop
                // Stop the search when the end of the list is reached.
                exitwhen i==0
                // Increase the count if the value is found.
                if Stack.value[i]==value then
                    set count=count+1
                endif
                // Get the next index of the list.
                set i=Stack.next[i]
            endloop
            return count
        endmethod
        method operator first takes nothing returns integer
            return .peek()
        endmethod
        method operator random takes nothing returns integer
            local integer r=GetRandomInt(1,.size)
            // Get the first index of the list.
            local integer i=.top
            // Loop through the list.
            loop
                // Stop the loop after a random amount of repeats.
                set r=r-1
                exitwhen r==0 or i==0
                // Get the next index of the list.
                set i=Stack.next[i]
            endloop
            return Stack.value[i]
        endmethod
        method remove takes integer value returns boolean
            // Get the first index of the list.
            local integer i1=.top
            local integer i2
            // Check if the first index holds the value.
            if Stack.value[i1]==value then
                call .pop()
                return true
            endif
            // Search through the rest of the list.
            loop
                set i2=Stack.next[i1]
                // Stop the search when the end of the list is reached.
                exitwhen i2==0
                // Check if the next index holds the value.
                if Stack.value[i2]==value then
                    // Remove the index from the stack.
                    set Stack.next[i1]=Stack.next[i2]
                    // Add the removed index to the list of free indexes.
                    set Stack.next[i2]=Stack.free
                    set Stack.free=i2
                    set .size=.size-1
                    return true
                endif
                set i1=i2
            endloop
            return false
        endmethod
        method purge takes integer value returns integer
            local integer count=0
            local integer i1
            local integer i2
            // If the first index holds the value, pop it.
            loop
                // If the list is empty, return.
                if .top==0 then
                    return count
                endif
                // Repeat until the first index doesn't hold the value.
                exitwhen Stack.value[.top]!=value
                call .pop()
                set count=count+1
            endloop
            // Get the first index of the list.
            set i1=.top
            // Search through the rest of the list.
            loop
                set i2=Stack.next[i1]
                // Stop the search when the end of the list is reached.
                exitwhen i2==0
                // Check if the next index holds the value.
                if Stack.value[i2]==value then
                    // Remove the index from the stack.
                    set Stack.next[i1]=Stack.next[i2]
                    // Add the removed index to the list of free indexes.
                    set Stack.next[i2]=Stack.free
                    set Stack.free=i2
                    set .size=.size-1
                    set count=count+1
                else
                    set i1=i2
                endif
            endloop
            return count
        endmethod
        method enum takes Enum f, boolean top2bottom returns nothing
            local integer array value
            // Get the first index of the list.
            local integer i1=.top
            local integer i2=0
            // Populate the array.
            loop
                exitwhen i1==0
                set value&#91;i2]=Stack.value&#91;i1]
                set i2=i2+1
                set i1=Stack.next&#91;i1]
            endloop
            // Call the Enum function for each value in the array.
            set i1=i2-1
            loop
                exitwhen i2==0
                set i2=i2-1
                // Enumerate in which direction?
                if top2bottom then
                    call f.evaluate(value&#91;i1-i2])
                else
                    call f.evaluate(value&#91;i2])
                endif
            endloop
        endmethod
        method copy takes nothing returns Stack
            local Stack that=Stack.create()
            // Get the first index of the list.
            local integer i1=.top
            local integer i2
            // Add a dummy index to the top of the new list.
            call that.push(0)
            set i2=that.top
            loop
                exitwhen i1==0
                // Get an index from the list of free indexes and add it at the end of the list.
                set Stack.next&#91;i2]=Stack.free
                set i2=Stack.next&#91;i2]
                set Stack.free=Stack.next&#91;i2]
                // Extend the list of free indexes if needed.
                if Stack.free==0 then
                    set Stack.free=i2+1
                endif
                // Copy the value to the new index.
                set Stack.value&#91;i2]=Stack.value&#91;i1]
                set i1=Stack.next&#91;i1]
            endloop
            // End the new list correctly.
            set Stack.next&#91;i2]=0
            // Remove the dummy index.
            call that.pop()
            // Copy the size value to the new list.
            set that.size=this.size
            return that
        endmethod


        method onDestroy takes nothing returns nothing
            local integer n
            // Remove all remaining indexes from the stack.
            loop
                // Get the top index.
                set n=.top
                exitwhen n==0
                // Remove it from the stack.
                set .top=Stack.next&#91;n]
                // Add it to the list of free indexes.
                set Stack.next&#91;n]=Stack.free
                set Stack.free=n
            endloop
        endmethod
        static method onInit takes nothing returns nothing
            // Store the EMPTY value to index 0 to make .peek inline friendly.
            set Stack.value&#91;0]=Stack.EMPTY
        endmethod
    endstruct
endlibrary



Et enfin, de même, il faut ajouter TimerUtils.

Secret:

Jass:
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//*  To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//*  To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass)   More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//*  * Attaching
//*  * Recycling (with double-free protection)
//*
//* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t)       : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2)     : Attach value 2 to timer
//* GetTimerData(t)       : Get the timer's value.
//*                         You can assume a timer's value is 0
//*                         after NewTimer.
//*
//* Multi-flavor:
//*    Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************

//================================================================
    globals
        //How to tweak timer utils:
        // USE_HASH_TABLE = true  (new blue)
        //  * SAFEST
        //  * SLOWEST (though hash tables are kind of fast)
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true  (orange)
        //  * kinda safe (except there is a limit in the number of timers)
        //  * ALMOST FAST
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
        //  * THE FASTEST (though is only  faster than the previous method
        //                  after using the optimizer on the map)
        //  * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
        //                     work)
        //
        private constant boolean USE_HASH_TABLE      = false
        private constant boolean USE_FLEXIBLE_OFFSET = false

        private constant integer OFFSET     = 0x100000
        private          integer VOFFSET    = OFFSET
             
        //Timers to preload at map init:
        private constant integer QUANTITY   = 256
       
        //Changing this  to something big will allow you to keep recycling
        // timers even when there are already AN INCREDIBLE AMOUNT of timers in
        // the stack. But it will make things far slower so that's probably a bad idea...
        private constant integer ARRAY_SIZE = 8190

    endglobals

    //==================================================================================================
    globals
        private integer array data[ARRAY_SIZE]
        private hashtable     ht
    endglobals

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        static if(USE_HASH_TABLE) then
            // new blue
            call SaveInteger(ht,0,GetHandleId(t), value)
           
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-VOFFSET]=value
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-OFFSET]=value
        endif       
    endfunction

    function GetTimerData takes timer t returns integer
        static if(USE_HASH_TABLE) then
            // new blue
            return LoadInteger(ht,0,GetHandleId(t) )
           
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-VOFFSET]
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-OFFSET]
        endif       
    endfunction

    //==========================================================================================
    globals
        private timer array tT[ARRAY_SIZE]
        private integer tN = 0
        private constant integer HELD=0x28829022
        //use a totally random number here, the more improbable someone uses it, the better.
    endglobals

    //==========================================================================================
    function NewTimer takes nothing returns timer
        if (tN==0) then
            //If this happens then the QUANTITY rule has already been broken, try to fix the
            // issue, else fail.
            debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
            static if( not USE_HASH_TABLE) then
                debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
                set tT[0]=CreateTimer()
                static if( USE_FLEXIBLE_OFFSET) then
                    if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
                        //all right, couldn't fix it
                        call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                        return null
                    endif
                else
                    if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
                        //all right, couldn't fix it
                        call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                        return null
                    endif
                endif
            endif
        else
            set tN=tN-1
        endif
        call SetTimerData(tT[tN],0)
     return tT[tN]
    endfunction

    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        if(t==null) then
            debug call BJDebugMsg("Warning: attempt to release a null timer")
            return
        endif
        if (tN==ARRAY_SIZE) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            call PauseTimer(t)
            if(GetTimerData(t)==HELD) then
                debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
                return
            endif
            call SetTimerData(t,HELD)
            set tT[tN]=t
            set tN=tN+1
        endif   
    endfunction

    private function init takes nothing returns nothing
     local integer i=0
     local integer o=-1
     local boolean oops = false
     
        static if( USE_HASH_TABLE ) then
            set ht = InitHashtable()
            loop
                exitwhen(i==QUANTITY)
                set tT[i]=CreateTimer()
                call SetTimerData(tT[i], HELD)
                set i=i+1
            endloop
            set tN = QUANTITY
        else
            loop
                set i=0
                loop
                    exitwhen (i==QUANTITY)
                    set tT[i] = CreateTimer()
                    if(i==0) then
                        set VOFFSET = GetHandleId(tT[i])
                        static if(USE_FLEXIBLE_OFFSET) then
                            set o=VOFFSET
                        else
                            set o=OFFSET
                        endif
                    endif
                    if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
                        exitwhen true
                    endif
                    if (GetHandleId(tT[i])-o>=0)  then
                        set i=i+1
                    endif
                endloop
                set tN = i
                exitwhen(tN == QUANTITY)
                set oops = true
                exitwhen not USE_FLEXIBLE_OFFSET
                debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")               
            endloop
           
            if(oops) then
                static if ( USE_FLEXIBLE_OFFSET) then
                    debug call BJDebugMsg("The problem has been fixed.")
                    //If this message doesn't appear then there is so much
                    //handle id fragmentation that it was impossible to preload
                    //so many timers and the thread crashed! Therefore this
                    //debug message is useful.
                elseif(DEBUG_MODE) then
                    call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
                    call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
                    call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
                    call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
                endif
            endif
        endif

    endfunction

endlibrary



Cela ne doit pas faire peur, vous n'avez pas besoin de modifier le code de ces trois libraries.
Vous, ce dont vous avez besoin, ensuite, c'est de pouvoir utiliser un son.
L'idée c'est comme pour la méthode "Gui", sauf que vous allez vous servir de SoundUtils comme suit:
Vous avez besoin d'un déclencheur vide que vous aller remplir de cette manière:

Secret:

Jass:
scope MesVariablesSons initializer init

globals
    integer SON_SULFATEUSE //l'ID associée au son. On peut en créer autant que l'on voudra jouer de sons différents.
endglobals


public function init takes nothing returns nothing
    set SON_SULFATEUSE = DefineSound("Units\\Human\\GyroCopter\\GyrocopterImpactHit1.wav",750, false, true)
    //Pendant le chargement de la carte, le son va être initialisé.
    //N'oubliez pas de mettre deux barres obliques pour que le chemin fichier soit valide au lieu d'une seule en Gui.
    //En gros, ici, vous ordonnez la fonction suivante:
    //Régler SON_SULFATEUSE pour être Define Sound(blablabla)
    //DefineSound demande les arguments:
    //Chemin Personalisé du son
    //Durée du son en milièmes de secondes
    //Son à jouer en boucle oui/non =>vous, a priori, jamais: le son sera joué à chaque attaque
    //Son en 3D: toi, a priori, oui: le son ne sera entendu que lorsque la caméra pendant le jeu est à portée auditive de la tourelle
    //Si je traduis set SON_SULFATEUSE = DefineSound("Units\Human\GyroCopter\GyrocopterImpactHit1.wav",750, false, true)
    //Ca donne: régler SON_SULFATEUSE comme un son défini pour jouer le chemin fichier "Units\Human\GyroCopter\GyrocopterImpactHit1.wav" pendant 750 milisecondes, pas en boucle, en son 3D
    //Vous pouvez créer autant de sons différents que souhaité, mais il faut les régler comme celui-ci
endfunction
endscope



Note: un scope, c'est un genre de déclencheur VJass.
Note: les globals, ce sont comme des variables que tu utilise en Gui. Ici, ce sont des variables globales, mais en VJass.
Et enfin, exactement comme pour la méthode "Gui", quand l'unité attaque, vous jouez le son (ici j'ai mis en condition Tour Canon, mais vous pouvez la remplacer par ce que vous voulez):

Secret:

Gui:
Trigger:
Untitled Trigger 001
Events
Unit - A unit Is attacked
Collapse Conditions
(Unit-type of (Attacking unit)) Equal to Tour canon
Collapse Actions
Custom script: call RunSoundOnUnit( SON_SULFATEUSE, GetAttacker() )



Avantage

Contrairement à la méthode Gui, vous pourrez jouer autant de sons en même temps que tu vous le voulez. En effet, SoundUtils permet de dépasser les limites des fonctions de base Blizzard sur le son en créant automatiquement des variables pour chaque son joué. Alors qu'en Gui, le même son ne peut pas être joué plusieurs fois en même temps.
Et mêmes avantages que la method Gui: pas besoin d'importer des modèles et de les modifier.

Inconvénient

Bah c'est du VJass, donc ça peut faire peur.
Ensuite on utilise TimerUtils, donc cela demande un peu plus de puissance que les méthodes jouant le son par l'intermédiaire du modèle.
Même chose que la méthode Gui: le son n'est joué que lorsque le projectile atteint la cible.
Même chose que la méthode Gui: il faut trouver les chemins des sons que vous voulez utiliser, et ici, connaître leur durée (on peut le voir via l'éditeur de son quand on clique sur un son en faisant: utiliser comme son).


VII/ Conclusion

A noter que dans le cas d'armes à tir rapide, par exemple, il n'y a pas de recettes miracles. Sur Restricted Complex 601 j'ai adopté la méthode de modification des modèles de projectiles pour que chaque arme ait son propre son de tir. L'inconvénient, c'est qu'avec ne serait-ce que 10 unités qui tirent toutes les 0.4 secondes, la moitié des sons n'est pas entendue, car le moteur de Warcraft III ne gère pas un grand nombre de sons en même temps. Passer par des déclencheurs ou SoundsUtils n'y change absolument rien. Ce qui fait, dans mon cas, que j'ai supprimé les sons des projectiles pour la plupart des armes, puisqu'on entendait absolument pas le quart des sons des unités etc.

J'espère que ce didacticiel pourra vous aider. L'exemple des projectiles peut bien sûr s'appliquer à tout son du jeu: effet spécial, unité, etc...
_________________


Dernière édition par jk2pach le 12/08/10 13:11; édité 1 fois
Revenir en haut
 Tirlititi



Inscrit le: 21 Fév 2010
Messages: 1785
Sujets: 22
Spécialité en worldedit: La modestie
Médailles: 1 (En savoir plus...)
Grand mage créateur de sort (Quantité : 1)

MessagePosté le: 12/08/10 12:17    Sujet du message: Citer

Dans ton dernier déclo, tu mets "(Unit-type of (Triggering unit))" au lieu de "Attacking unit". Sinon, je pense que les sons, avec les méthodes déclencheur, sont joués au début de l'attaque, et pas quand le projectile atteint sa cible.

A part ça, très bon tuto, j'ai appris plein de trucs Wink .
_________________
Warcraft III, c'était mieux avant. Y'avait des purs chefs-d'oeuvres du mapping !
Road Of Glory (par moi)
Casse-briques (par moi)
Temple de Glace (par moi)
Revenir en haut
Voir le profil de l'utilisateur Envoyer un message privé
 jk2pach
Invité








MessagePosté le: 12/08/10 13:11    Sujet du message: Citer

Attacking Unit corrigé.

Pour les sons, non. Par déclencheur, c'est le moment d'atteinte du projectile qui compte.

Pour ça que la méthode d'incorporer le son dans un modèle est un peu plus précise, suffit de mettre l'event object au moment de l'animation "Birth".
_________________
Revenir en haut
 Gevans
Back


Inscrit le: 21 Aoû 2007
Messages: 1930
Sujets: 85

Médailles: 2 (En savoir plus...)
Gevans (Quantité : 1) Rédacteur de tuto #1 (Quantité : 1)

MessagePosté le: 17/08/10 08:21    Sujet du message: Citer

Génial, enfin quelque chose sur les sons !
_________________
Si vous recherchez quelque chose, il y a de très grandes chances que vous trouviez la réponse ici alors jetez y un oeil.
Topic indispensable
Revenir en haut
Voir le profil de l'utilisateur Envoyer un message privé Envoyer l'e-mail Visiter le site web du posteur MSN Messenger
 jk2pach
Invité








MessagePosté le: 06/09/10 10:33    Sujet du message: Citer

Passé en officiel.
_________________
Revenir en haut
 Hisoka
Membre actif


Inscrit le: 10 Sep 2014
Messages: 116
Sujets: 3
Spécialité en worldedit: Scénario/cinématiques/effets spéciaux


MessagePosté le: 30/01/15 12:28    Sujet du message: Citer

Salut,

Je me permets de rebondir pour poser une petite question:
Je pense que ce n'est pas possible mais dans le cas d'un effet spécial ou d'un buff, y a-t-il moyen de dissocier le son de l'effet que l'on joue?

Merci.
_________________
Revenir en haut
Voir le profil de l'utilisateur Envoyer un message privé
 Tirlititi



Inscrit le: 21 Fév 2010
Messages: 1785
Sujets: 22
Spécialité en worldedit: La modestie
Médailles: 1 (En savoir plus...)
Grand mage créateur de sort (Quantité : 1)

MessagePosté le: 30/01/15 17:45    Sujet du message: Citer

La plupart du temps, non : le son est dans le modèle de l'effet spécial. Il y en a quelques-uns qui sont personnalisables dans l'éditeur d'objet ("Infographie - Son", si jeune mabuse) mais sinon, il faut passer par un logiciel 3D.

Avec War3Model Editor, par exemple, tu peux trouver les sons dans le panneau des "nodes".
_________________
Warcraft III, c'était mieux avant. Y'avait des purs chefs-d'oeuvres du mapping !
Road Of Glory (par moi)
Casse-briques (par moi)
Temple de Glace (par moi)
Revenir en haut
Voir le profil de l'utilisateur Envoyer un message privé
 Ayane
Bot administrateur


Inscrit le: 17 Sep 2007
Messages: 2009
Sujets: 49

Médailles: 2 (En savoir plus...)
Ayane (Quantité : 1) Rédacteur de tuto #1 (Quantité : 1)

MessagePosté le: 31/01/15 23:02    Sujet du message: Citer

Ce n'est pas le son en lui-même qui est dans le modèle, seulement un chemin. Le fichier de son est accessible et donc jouable séparément. Pour désactiver un son d'un effet spécial sans lui changer le modèle tu peux toujours remplacer dans la carte via importateur le fichier son par un fichier vide.
_________________
Revenir en haut
Voir le profil de l'utilisateur Envoyer un message privé MSN Messenger
 Hisoka
Membre actif


Inscrit le: 10 Sep 2014
Messages: 116
Sujets: 3
Spécialité en worldedit: Scénario/cinématiques/effets spéciaux


MessagePosté le: 02/02/15 13:50    Sujet du message: Citer

Cependant, si on applique ta méthode, cela signifie que si l'effet est joué dans son véritable contexte en cours de partie, il n'y aura plus de son associé, ce qui peut être problématique.

Le cas que je voulais soulevé est celui de l'aura. Pour une aura j'utilise un effet, que je fais se répéter sur mon unité. Mais au final il est assez désagréable d'entendre en permanence le son du "birth" se répéter.
_________________
Revenir en haut
Voir le profil de l'utilisateur Envoyer un message privé
Montrer les messages depuis:   
Poster un nouveau sujet   Répondre au sujet    Worldedit Index du Forum -> Tutoriels Toutes les heures sont au format GMT + 1 Heure
Page 1 sur 1
La question posée dans ce topic a été résolue !

 
Sauter vers:  
Vous ne pouvez pas poster de nouveaux sujets dans ce forum
Vous ne pouvez pas répondre aux sujets dans ce forum
Vous ne pouvez pas éditer vos messages dans ce forum
Vous ne pouvez pas supprimer vos messages dans ce forum
Vous ne pouvez pas voter dans les sondages de ce forum


Powered by phpBB © 2001, 2005 phpBB Group
Traduction par : phpBB-fr.com