Le pisseur
(animation de COD décodée pour CODUO et COD)

Retour au sommaire.

Tutorial by Tropheus. Je remercie ici aussi Bushman avec qui j'ai fait les recherches.

Au cours de ce tutorial vous allez apprendre à mettre sur votre map COD ou CODUO l'animation COD (le gars qui pisse) au début de la map Pathfinder.

 

1/ MAPPING

Créez votre map normalement.

Introduisez un soldat axe armé d'un Kar car l'animation est faite pour un allemand armé d'un kar.
classname / actor_axis_wehrmacht_soldier_kar98k
model / xmodel/character_german_WehrmactB
targetname / ducon1

Implantez un node_scripted
classname / node_scripted
targetname / piss => Ici piss semble indispensable comme targetname sinon il faut changer le nom dans le script alors autant le garder.
target / ducon1

ils vous faut maintenant 3 Triggers : 2 trigger_mutliple qui serviront de déclencheur d'activation et de fin de l'animation, et un trigger_damage qui lui vous servira à simuler le fait que le soldat axe entende tirer.

Trigger 1 // activation de l'animation par passage dans le trigger.
classname/ trigger_multiple
target / auto1
targetname / ducon1 => vous remarquerez qu'il a le même targetname que l'entité.

Trigger 2 // Fin de l'animation en passant à un endroit
classname / trigger_multiple
targetname / auto1 => voici une cilble du trigger précédent.

Trigger 3 // Fin de l'animation en tirant à un endroit (comme s'il y avait un bruit de tir proche)
classname / trigger_damage
targetname
/ auto1 => voici une autre cilble du trigger 1.

Pour l'après pipi il faut à votre soldat reprendre sa route. Pour cela disposez sur le terrain deux (ou plus) node_pathnode.

node_pathnode 1 // Destination 1 du gars
classname / node_pathnode
targetname / urinazi goes
target / Tropheus2

node_pathnode 2 // destination 2 du gars
classname / node_pathnode
targetname / Tropheus2

Donc en résumé nous avons :

 



ATTENTION

- Il existe tout de même quelques contraintes qu'il ne faut pas négliger.

- Lorsque vous passez le trigger le type vole jusqu'à son arbre pour pisser. => A vous de trouver le bon truc pour éviter que le joueur voit ça.

- Les angles du node_scripted définissent le sens vers lequel se tournera le personnage.=> Mais comme c'est une animation il revient à vous de trouver le bon angle afin qu'il appuie son fusil contre quelque chose. Sinon vous aurez ceci.

- D'autre part à vous de placer correctement les triggers de fin d'action (passage et attaque) afin de rendre votre carte plus réaliste.

2/ SCRIPTING.

Je vous propose ici les parties du script que j'ai isolées et qui permettent à l'animation de fonctionner. Pour cela il vous faut 2 fichiers .gsc celui qui gère toute la map ici test_piss.gsc et un fichier qui ne gèrera que les animations test_piss_anim.gsc, mais il vous faut aussi les fichiers sons .csv.

a/ Fichier test_pissgsc

main()
{
    maps\_load::main();
    maps\test_piss_anim::main();

    //GIVE PLAYER WEAPONS
    level.player takeallweapons();
    level.player giveWeapon("panzerfaust");
    level.player giveWeapon("springfield");
    level.player giveWeapon("colt");
    level.player giveWeapon("fraggrenade");
    level.player switchToWeapon("springfield");

    precacheModel("xmodel/piss_sequence_gunpose");

    level._effect["special line"]=loadfx("fx/impacts/pathfinder_special.efx");
    level._effect["special smoke"]=loadfx("fx/smoke/pathfinder_special_smoke.efx");

    thread piss (getnode ("piss","targetname")); //Envoi vers la partie piss avec comme variable le node qui a "piss" comme targetname.

}

///////////////////////
piss ( node )
{
    targets = getentarray (node.target,"targetname"); //mise en tableau de toutes les cibles du noeud piss
    for (i=0;i<targets.size;i++)
        {
        if (targets[i].classname == "trigger_multiple") //si la cible est un trigger_multiple.
                {
               start_trigger = targets[i]; //Ce trigger sera un start_trigger
               stop_triggers = getentarray (start_trigger.target,"targetname"); // le stop_trigger ser ala cible du start_trigger
               }
        else
               ai = targets[i]; //sinon elle s'appellera ai.
          }
    targets = undefined;

    level thread dropprint(ai);
    ai endon ("death"); //anim finira avec la mort
    ai endon ("done"); //anim finira lorsqu'il aura terminé
    ai endon ("pain"); //anim finira s'il est touché
    ai.allowdeath = true; //le type peut mourir
    ai.animname = "pisser"; //l'animation choisie "pisser"
    ai.health = 1; //la vie de ai sera de 1
    wait (7); // on attend 7 secondes avant que le trigger soit activé. (c'est surement pour cela que l'on ne peut activer l'anim qu'au bout d'un certain.)
    start_trigger waittill ("trigger"); //Le trigger attend d'être activé.

    rig = spawn ("script_model",(0,0,0)); //on fait spawner un script_model
    rig setmodel ("xmodel/piss_sequence_gunpose"); // le model est le "piss_sequence_gunpose".

    rig.origin = node.origin; //l'origine sera celle du node (piss bien sûr)
    rig.angles = node.angles; //les angles seront ceux du node (le même)

    rig maps\_anim::gun_leave_behind (ai, level.scr_notetrack[ai.animname][0]); //on commence l'animation

/////Activation des différents threads qui gèrent l'animation.   

   level thread alert_other_guy ( ai );//?=> peut-être s'il est averti par un autre de votre présence
    level thread piss_stop ( ai, "fight" );//L'animation se terminera en cas de combat
    level thread piss_stop ( ai, "all done" );//l'animation se terminera lorsque le pisseur aura fini
    level thread piss_stop ( ai, "pain" );//l'animation finira s'il est blessé
    level thread piss_stop ( ai, "death" );//l'animation finira s'il meurt
    level thread piss_pain( ai );
    level thread piss_think( ai, node );
    if (getcvar ("special") == "indeed")
    level thread piss_allover (ai);

    // ai.looper = spawn ("script_origin",(0,0,0));
    // ai.looper.origin = ai.origin;
    // ai.looper linkto (ai);
    // ai.looper playloopsound ("Pissing_Guy");
    ai playloopsound ("Pissing_Guy");//activation du son lié à l'animation
    // ai.looper thread ghetto_loop ("Pissing_Guy");

    array_thread (stop_triggers, ::trigger_stop, start_trigger);

    start_trigger waittill ("stop trigger");
    ai notify ("fight");
    guy[0] = ai;
    ai anim_single (guy, "flinch turn", undefined, node);
    ai.health = 100;
    node = getnode ("urinazi goes","targetname");//node vers lequel se dirigera l'Ai après avoir uriné
}

piss_allover (ai)
{
    ai endon ("death");
    ai endon ("fight");
    ai endon ("all done");

    timer = 0;
    while (1)
        {
            origin = ai gettagorigin ("Bip01 Pelvis");
            angles = ai gettagangles ("Bip01 Pelvis");
            angles = anglesToForward (angles);
            dest = maps\_utility::vectorScale (angles, -100);
            dif = 25;
            trace = bulletTrace(origin, origin+dest, false, undefined);
            smokeorg = trace["position"];
            randnum = randomint (4) + 1;
            if (gettime() > timer)
        {
            playfxOnTag ( level._effect["special line"], ai, "Bip01 Pelvis" );
            timer = gettime() + 500;
        }

            for (i=0;i<randnum;i++)
            {
            brightness = randomfloat (100) * 0.01;
            // playfx ( level._effect["special line"], origin, origin);//, origin + dest + (randomfloat (dif) - dif*0.5,randomfloat (dif) - dif*0.5,randomfloat (dif) - dif*0.5));
            playfx ( level._effect["special smoke"], smokeorg );
            /*
            line (origin, origin + dest + (randomfloat (dif) - dif*0.5,randomfloat (dif) - dif*0.5,randomfloat (dif) - dif*0.5),
            (1*brightness,1*brightness,0), 1, true);
            */
            }

    wait (0.05);
    }
}

///////////////////////
piss_stop (ai, notifier)
{
     // ai endon ("death");
    ai waittill (notifier);
    // if (isdefined (ai.looper))
    // ai.looper delete();
    ai stoploopsound ("Pissing_Guy");//fin du son du pipi

    // ai.looper stoploopsound ("pissing_guy");
}

//////////////
piss_pain ( ai )
{
    ai waittill ("pain");
    if (isalive (ai))
    ai notify ("end_sequence");
}

piss_think ( ai, node )
{
    ai endon ("death");
    ai endon ("fight");
    ai endon ("pain");

    guy[0] = ai;
    ai anim_single (guy, "idle", undefined, node);
    ai notify ("all done");
    ai anim_single (guy, "shakeout", undefined, node);
    ai anim_single (guy, "casual turn", undefined, node);
    ai notify ("done");
    node = getnode ("urinazi goes","targetname");
    ai setgoalnode (node);

    while (isdefined (node.target))
        {
        node = getnode (node.target,"targetname");
        ai setgoalnode (node);
        wait (10);
        }

}
///////////////////////////////////

dropprint (ai)
{
    ai waittill ("death");
    println (ai.dropweapon);
}
////////////////////////
alert_other_guy ( ai )
{
    ai waittill ("death");
    level notify ("early guy alert!");
}

////////////////////

anim_single (guy, anime, tag, node, tag_entity)
{
    maps\_anim::anim_single (guy, anime, tag, node, tag_entity);
}

////////////////////////////
array_thread (ents, process, var, excluders)
{
    maps\_utility::array_thread (ents, process, var, excluders);
}
///////////////////////////////////
trigger_stop ( trigger )
{
        if (self.classname == "trigger_damage")
        self enableGrenadeTouchDamage();
        self waittill ("trigger");
        trigger notify ("stop trigger");
}

b/ Le fichier test_piss_anim.gsc

Ce fichier contient toutes les étapes de l'animation.

#using_animtree("generic_human");//choix du type d'animation
main()
{
/*
explications en anglais du fichier anim de pathfinder.

piss animations; characters
play (idle). it is long so you won't need to play it more then once after the player sees it.
play (shakeout) for him to finish.
if the player gets in his line of sight or fires at him play (flinch turn) - he will quickly go for his gun
if he isn't desturbed before finishing play (casual turn) - he turn around to get his gun and start to walk off
if he is shot he can play a normal standing pain but his gun may just appear in his hand... if this is an issue we can deal with it.
piss animations; rig
you use this simply to get the basepose for the gun...
*/

// piss_sequence_gunpose (xmodel) - to place gun in correct spot leaning against the tree
level.scr_animtree["stand"] = #animtree;
level.scr_anim["pisser"]["casual turn"] = (%pisser_casualturn);
level.scr_anim["pisser"]["flinch turn"] = (%pisser_flinchturn);
level.scr_anim["pisser"]["idle"] = (%pisser_pissidle);
level.scr_anim["pisser"]["shakeout"] = (%pisser_shakeout);

level.scr_notetrack["pisser"][0]["notetrack"] = "detach gun";
level.scr_notetrack["pisser"][0]["detach gun"] = "xmodel/weapon_kAr98";
level.scr_notetrack["pisser"][0]["tag"] = "TAG_GUN";

level.scr_notetrack["pisser"][1]["notetrack"] = "attach gun left";
level.scr_notetrack["pisser"][1]["attach gun left"] = "xmodel/weapon_kAr98";
}

c/ Les fichiers sons.

Si on laisse ceci comme cela vous aurez un problème avec l'animation car il vous manquera le son du pipi.
Il va donc falloir le déclarer. pour cela il vous faut 2 fichiers .csv

* Le premier.

Dans le dossier map de votre pk3 créer un dossier soundaliases dans lequel vous allez mettre ceci sous le nom de test_piss_sound.csv

"# If the text in the first column of a row starts with a # character, the row is ignored",,
,,"If the first column for a row is blank, then the row is ignored"
# The first non-comment line of a sound alias file specifies the key name for all values appearing in this column.,,
" # This means it is safe to swap entire columns around, though you should never swap partial columns.",,
" # You can invent new keys, but the game will ignore them if it doesn't know about them.",,
" # You can leave out keys, but the ""name"" and ""file"" keys must always be present.",,

,name,name of the alias that is used to play this sound (required)
,sequence,"used to uniquely identify alias entries when more than one sound goes to an alias, used only to catch unwanted duplicates (default = 0)"
,file,the name of the file that contains the sound data (required)
,vol_min,"0 is silent, 1 is full volume (default = 1)"
,vol_max,"0 is silent, 1 is full volume (default = same as vol_min)"
,pitch_min,"1 is normal playback, 2 is twice as fast, 0.5 is half as fast (default = 1)"
,pitch_max,"1 is normal playback, 2 is twice as fast, 0.5 is half as fast (default = same as pitch_min)"
,dist_min,"within this distance in inches, the sound is always full volume (default = 120)"
,dist_max,"outside this distance in inches, the sound is not started. If left blank or set to 0, the sound will play from any distance. This does not affect sound volume falloff."
,channel,"auto, menu, weapon, voice, item, body, local, music, announcer (default = auto)",,,,,,,,,,,,,
,type,streamed / loaded (default = loaded),,,,,,,,,,,,,
,probability,weight to use for the weighted probability of playing this sound instead of another sound (default = 1),,,,,,,,,,,,,
,loop,"whether this sound is ""looping"" or ""nonlooping"" (default = ""nonlooping"")",,,,,,,,,,,,,
,masterslave,"if ""master"", this is a master sound. If a number, then this sound won't exceed this volume whenever any master sound is playing. If blank, then neither master nor slave.",,,,,,,,,,,,,
,loadspec,"space-separated list of which maps should use this alias; eg, ""burnville dawnville"". If blank, the alias is used on all maps.",,,,,,,,,,,,,

name,sequence,file,vol_min,vol_max,pitch_min,pitch_max,dist_min,dist_max,channel,type,probability,loop,masterslave,loadspec,subtitle

null,,null.wav,,,,,,,,,,,,,

# Piss,,,,,,,,,,,,,,

Pissing_Guy,,misc/german_pissing.wav,0.67,0.67,,,400,2000,,,,looping,,test_piss // Ici vous mettez le nom de votre map.

ATTENTION veuillez respecter les virgules dans ce fichiers, elles sont cruciales.

 

* Le second.

Toujours dans le dossier map du pk3 créer cette fois si un dossier soundloadspecs dans lequel vous mettrez un dossier nommé sp créez un fichier test_piss.csv.

# This file defines the default soundalias files to load if no map specific loadspec file is found
# If you want to include sounds into your map file which are not included here you should make a seperate
" # load spec file int the form of ""<map name>.csv"" using this file as a base. You should only load the"
# soundalias files which are absolutely necessary to keep the sound memory footprint as small as possible
#*********************************************************************************************************
#DEFAULT

scs1.csv
dialog_generic.csv
iw_music.csv
iw_sound.csv
iw_sound2.csv
iw_voiceovers.csv
gmi_dialog_generic.csv
gmi_music.csv
gmi_sound.csv
gmi_voiceovers.csv
pi_sound.csv
surface_gmi_sound.csv
wep_gmi_sound.csv
gmi_mp_weap_us.csv
gmi_mp_weap_all_allied.csv

#MAP
test_piss_sound.csv

Pour récapituler vous devez avoir dans votre pk3.

un dossier maps avec les fichiers test_piss.bsp, test_piss.gsc, test_piss_anim.gsc.
un dossier soundaliases avec le fichier test_piss_sound.csv
un dossier soundloadspecs avec dedans un autres dossier sp avec dedans le fichier test_piss.csv

Voila! vous mettez le tout dans votre pk3 et il ne vous reste plus qu'à tester votre map.

Pour tester la map test_piss uniquement pour CODUO cliquez ici.
Le PK3 comporte le fichier .map pour que vous ayez un aperçu de l'animation.

Pour CaskamiProd
by Tropheus©
mars 2006