Call of Duty 4: Vending Machine

From COD Modding & Mapping Wiki
Revision as of 23:00, 16 October 2008 by Zeroy (talk | contribs) (New page: ''By Kill3r'' Image:Nutshell.png This tutorial will show you how to add a vending machine that when used heals you and a bottle pops out of it like in (or will be in) the rat_house_v2...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

By Kill3r

This tutorial will show you how to add a vending machine that when used heals you and a bottle pops out of it like in (or will be in) the rat_house_v2 map.

 This tutorial presumes that you already know how to create, compile, create the usual .gscs and .csvs and create the .ff for your maps.
  REMEMBER: Anywhere that says "yourmapname" change to what your map is called, like mp_funny_bunny



Adding the vending machine model

First off, open the map you want to add the vending machine to. Add a vending machine prefab by right clicking on the 2D area of Radiant > misc > prefab



Go the the misc_models folder, and look for "com_vending_xxx.map" were xxx is which type you want, but its best to use one that has an opening at the bottom.



Creating the trigger

Next we need the trigger, create a brush surrounding the front of the vending machine and texture it with tools > trigger



Also create a small 8x8x8 brush, the center of this brush is where the bottles will spawn (the origin of the bottles is at the bottom) to help position the smaller brush add a misc_model by right clicking on the 2D area of Radiant > misc > model and navigate to your raw/xmodel folder then insert com_bottle1. Also set the angles of the bottle so they lay flat as they would when coming out of the vending machine.



We will be needing the com_bottle1 model again later so dont delete it just yet :P

Unselect everything then select the trigger brush and the small brush, right click in the 2D area of radiant > trigger > (type you want here)

Types of triggers:

  • damage - Shoot the trigger to trigger it
  • disk - a round trigger, cant change the height
  • friendlychain - Used for SP only
  • hurt - Hurts the player... dont use this one lol xD
  • lookat - Look at the trigger to trigger it
  • multiple - Triggered when touched by a player
  • once - Triggered only once
  • radius - like disk but can change the height
  • use - Press the USE button to trigger
  • use_touch - Player must be touching the trigger and press the USE button to trigger it


I recommended to use trigger_use_touch or trigger_use



Unselect everything then texture the small brush with tools > origin. Press N to open the entity box, and type the following in:

Key: hintstring
Value: Press [^3USE^7] to have a drink
Key: targetname
Value: heal


Adding where the impact will come from

Next we need to set where the impact is going to come from to force the bottle out of the vending machine (and also set the angle of the bottle when it spawns). To do this we need to pace a script_origin by right clicking on the 2D area of Radiant > script > origin then a red box should appear. Place this corresponding to where you what the impact to come from.

Now use the com_bottle1 model that you used before when adding the trigger, make sure that the angles are as if it was coming out of the vending machine. Now press N to open the entitys box for the bottle and look at the angles, then select the script_origin and set the same angles for that. Now you can delete the com_bottle1 model.



Make sure nothing is selected by pressing Esc few times, now select the trigger THEN the script_origin and press W. A red line should appear from the center of the large or smaller trigger brushes connecting to the script_origin.



The .csv files

(ff .csv and sound .csv)

OK, thats the Radiant part done, now for the .csvs

  • Go to CoD4\zone_source and look for yourmapname.csv

Open that with notepad and add these lines somewhere (xmodel, lines only requierd if the model is not already used in your map)

xmodel,com_bottle1
xmodel,com_bottle2
xmodel,com_bottle3
xmodel,com_bottle4
sound,yourmapname,,,  
  • Save and close


  • Now go to CoD4\raw\soundaliases, make a new .csv called yourmapname.csv (a .txt renamed to .csv)

Open it with notepad and add the following:

name,sequence,file,vol_min,vol_max,vol_mod,pitch_min,pitch_max,dist_min,dist_max,channel,type,probability,loop,masterslave,loadspec,subtitle,compression,secondaryaliasname,volumefalloffcurve,startdelay,speakermap,reverb,lfe percentage,center percentage,platform,envelop_min,envelop_max,envelop percentage,conversion

vender,,level/ac_unit_window_loop.wav,0.7,0.7,max,1,1,150,300,auto,streamed,,looping
vender_drop,1,yourmapname/vender_drop1.wav,0.7,0.8,max,0.9,1,150,300,auto,,,
vender_drop,2,yourmapname/vender_drop2.wav,0.7,0.8,max,0.9,1,150,300,auto,,,  

Save and close again


Go to CoD4\raw\sound and make a new folder called yourmapname, in there place the 2 .wav sounds that can be downloaded (link at the bottom)


The Scripts

OK then, now the scripty part

New commands that are added with these scripts are:

set yourmapname_allow_vender "1"
set yourmapname_health_wait "10"

yourmapname depends what your map is called, like if your map is mp_funny_bunny then the new commands would be:

set mp_funny_bunny_allow_vender "1"
set mp_funny_bunny_health_wait "10"


Add this part somewhere in the main() function of your map .gsc

precache();                    // Call the precache function

    venders = getentarray("heal","targetname");    // Get all the entitys that have "heal" for their "targetname"
    for(i=0;i<venders.size;i++)
        venders[i] thread vender();

So it should look something like this:

main()
{
    level.tweakfile = true;
    setdvar("scr_fog_disable", "0");
    VisionSetNaked("mp_vending", 0);

    mapsmp_load::main();
    mapsmp_compass::setupMiniMap("compass_map_mp_vending");

    ambientPlay("ambient_pipeline");

    game["allies"] = "marines";
    game["axis"] = "opfor";
    game["attackers"] = "axis";
    game["defenders"] = "allies";
    game["allies_soldiertype"] = "desert";
    game["axis_soldiertype"] = "desert";

    setdvar("r_specularcolorscale", "1" );
    setdvar("r_glowbloomintensity0",".1");
    setdvar("r_glowbloomintensity1",".1");
    setdvar("r_glowskybleedintensity0",".1");

    setdvar("compassmaxrange","2048");

    precache();// Call the precache function

    venders = getentarray("heal","targetname");// Get all the entitys that have "heal" for their "targetname"
    for(i=0;i<venders.size;i++)
        venders[i] thread vender();
}  

Add this code UNDER the main() function

        if(!level.allow_vender)// If vending machines are not allowed then say a message
        {
            player iPrintlnBold("^2Sorry, fat rats are not allowed on this server");
            continue;
        }

        if(isDefined(dir)) // If the script_origin is placed correctly in the map, then make a bottle pop out
        {
            self playSound("vender_drop");    // Play the bottle drop sound

            if(isDefined(level.venderbottles[level.venderbottlescurrent]))
                level.venderbottles[level.venderbottlescurrent] delete();    // If we have gone over the limit for max bottle in the map, delete the oldest one

            level.venderbottles[level.venderbottlescurrent] = spawn("script_model",self.origin);        // Spawn the bottle
            level.venderbottles[level.venderbottlescurrent].angles = dir.angles;// Set the angles of the bottle
            level.venderbottles[level.venderbottlescurrent] setModel("com_bottle" + (randomInt(4) + 1));    // Set random bottle model
            point = dir.origin;
            origin = level.venderbottles[level.venderbottlescurrent].origin + mapsmp_utility::vector_Scale(anglestoup(level.venderbottles[level.venderbottlescurrent].angles),4 + randomFloat(1));
            velocity = VectorNormalize(origin - point);// Calculate velocity and direction of the impact
            velocity = mapsmp_utility::vector_Scale(velocity,10000 + randomInt(5000));
            level.venderbottles[level.venderbottlescurrent] physicsLaunch(point, velocity); // Make the bottle fly!

            level.venderbottlescurrent++;

            if(level.venderbottlescurrent >= level.venderbottlesmax)
                level.venderbottlescurrent = 0;
        }

        if(isDefined(player.healthwait))// If the player has already used the vending machine then...
        {
            num = randomInt(100);
            if(num < 20)
                player iPrintlnBold("^2When was the last time you saw a fat rat?");
            else if(num >= 20 && num < 40)
                player iPrintlnBold("^1Careful! You'll break it!");// Say one of these random messages
            else if(num >= 40 && num < 60)
                player iPrintlnBold("^3Oh no! There's none left!");
            else if(num >= 60 && num < 80)
                player iPrintlnBold("^4You're hungry ain't ya?");
            else
                player iPrintlnBold("^5Woof");
            continue;
        }

        player.healthwait = true;
        if(player.health < player.maxhealth)// If the players health is not at the max
        {
            player.health = player.maxhealth;// Set the players health to the max
            player iPrintlnBold("^2mmm... ^1y^2u^3m^4m^5y^6!");// Say these messages if the player was healed
            player iPrintlnBold("^1Health Restored!");
        }
        else
            player iPrintlnBold("^2You already have full health, but ^1y^2u^3m^4m^5y^6! ^2anyway!");// If the player already has full health then say this message
        player thread healthwait();
    }
}

healthwait()
{
    self endon("disconnect");    // Kill the tread if the player disconnects
    wait level.vender_wait;        // Wait how long level.vender_wait is
    self.healthwait = undefined;    // Make self.healthwait undefined
}  

So, with all the code it should look like this:

main()
{
    level.tweakfile = true;
    setdvar("scr_fog_disable", "0");
    VisionSetNaked("mp_vending", 0);

    mapsmp_load::main();
    mapsmp_compass::setupMiniMap("compass_map_mp_vending");

    ambientPlay("ambient_pipeline");

    game["allies"] = "marines";
    game["axis"] = "opfor";
    game["attackers"] = "axis";
    game["defenders"] = "allies";
    game["allies_soldiertype"] = "desert";
    game["axis_soldiertype"] = "desert";

    setdvar("r_specularcolorscale", "1" );
    setdvar("r_glowbloomintensity0",".1");
    setdvar("r_glowbloomintensity1",".1");
    setdvar("r_glowskybleedintensity0",".1");

    setdvar("compassmaxrange","2048");

    precache();// Call the precache function

    venders = getentarray("heal","targetname");// Get all the entitys that have "heal" for their "targetname"
    for(i=0;i<venders.size;i++)
        venders[i] thread vender();
}

precache()
{
    if(getDvar(getDvar("mapname") + "_allow_vender") == "")
        setDvar(getDvar("mapname") + "_allow_vender", 1);// This creates the dvar to enable the vending machines

    if(getDvar(getDvar("mapname") + "_health_wait") == "")
        setDvar(getDvar("mapname") + "_health_wait", 10);// This creates the dvar to set the time between being able to get health again

    level.allow_vender = int(getDvar(getDvar("mapname") + "_allow_vender"));
    level.vender_wait = int(getDvar(getDvar("mapname") + "_health_wait"));

    for(i=1;i<5;i++)
        precacheModel("com_bottle" + i);// Precache (load) the com_bottle1, com_bottle2, com_bottle3 and com_bottle4 models

    level.venderbottles = [];
    level.venderbottlescurrent = 0;
    level.venderbottlesmax = 24;// Max bottles in the map at one time, try not to have this too high as it can cause lag and somtimes crash the server
}

vender()
{
    self.targetname = undefined;
    dir = getent(self.target,"targetname");
    if(isDefined(dir))
    {
        temp = spawnStruct();
        temp.origin = dir.origin;
        temp.angles = dir.angles;// Spawn a struct which doesnt count as an entity, record the script_origin's origin and angles then delete the script_origin
        dir delete();
        dir = temp;
    }
    self.target = undefined;

    self playLoopSound("vender");// Make the trigger play a loop sound

    while(1)
    {
        self waittill("trigger", player);// Wait until its triggered
        if(player.sessionstate != "playing")// Make sure the player is playing
            continue;

        if(!level.allow_vender)// If vending machines are not allowed then say a message
        {
            player iPrintlnBold("^2Sorry, fat rats are not allowed on this server");
            continue;
        }

        if(isDefined(dir))// If the script_origin is placed correctly in the map, then make a bottle pop out
        {
            self playSound("vender_drop");// Play the bottle drop sound

            if(isDefined(level.venderbottles[level.venderbottlescurrent]))
                level.venderbottles[level.venderbottlescurrent] delete();// If we have gone over the limit for max bottle in the map, delete the oldest one

            level.venderbottles[level.venderbottlescurrent] = spawn("script_model",self.origin);// Spawn the bottle
            level.venderbottles[level.venderbottlescurrent].angles = dir.angles; // Set the angles of the bottle
            level.venderbottles[level.venderbottlescurrent] setModel("com_bottle" + (randomInt(4) + 1));    // Set random bottle model
            point = dir.origin;
            origin = level.venderbottles[level.venderbottlescurrent].origin + mapsmp_utility::vector_Scale(anglestoup(level.venderbottles[level.venderbottlescurrent].angles),4 + randomFloat(1));
            velocity = VectorNormalize(origin - point);// Calculate velocity and direction of the impact
            velocity = mapsmp_utility::vector_Scale(velocity,10000 + randomInt(5000));
            level.venderbottles[level.venderbottlescurrent] physicsLaunch(point, velocity);            // Make the bottle fly!

            level.venderbottlescurrent++;

            if(level.venderbottlescurrent >= level.venderbottlesmax)
                level.venderbottlescurrent = 0;
        }

        if(isDefined(player.healthwait))// If the player has already used the vending machine then...
        {
            num = randomInt(100);
            if(num < 20)
                player iPrintlnBold("^2When was the last time you saw a fat rat?");
            else if(num >= 20 && num < 40)
                player iPrintlnBold("^1Careful! You'll break it!");// Say one of these random messages
            else if(num >= 40 && num < 60)
                player iPrintlnBold("^3Oh no! There's none left!");
            else if(num >= 60 && num < 80)
                player iPrintlnBold("^4You're hungry ain't ya?");
            else
                player iPrintlnBold("^5Woof");
            continue;
        }

        player.healthwait = true;
        if(player.health < player.maxhealth)// If the players health is not at the max
        {
            player.health = player.maxhealth;// Set the players health to the max
            player iPrintlnBold("^2mmm... ^1y^2u^3m^4m^5y^6!");// Say these messages if the player was healed
            player iPrintlnBold("^1Health Restored!");
        }
        else
            player iPrintlnBold("^2You already have full health, but ^1y^2u^3m^4m^5y^6! ^2anyway!");// If the player already has full health then say this message
        player thread healthwait();
    }
}

healthwait()
{
    self endon("disconnect");    // Kill the thread if the player disconnects
    wait level.vender_wait;        // Wait how long level.vender_wait is
    self.healthwait = undefined;    // Make self.healthwait undefined
}  

Prefabs, scripts, sounds and example map with .map can be downloaded here