Call of Duty 4: Vending Machine

From COD Modding & Mapping Wiki
Revision as of 13:55, 23 February 2012 by Ingram (talk | contribs) (meh.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
This tutorial assumes that you already know how to create, compile & create GSCs,CSVs & FastFiles for your maps

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.

  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^3 [{+activate}] ^7to 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:

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);

	maps\mp\_load::main();
	maps\mp\_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

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;

	randomMessages = []; // List of random messages when player has had a bottle recently, you can have as many of them as you want.
	randomMessages[0] = "^2When was the last time you saw a fat rat?";
	randomMessages[1] = "^1Careful! You'll break it!";
	randomMessages[2] = "^3Oh no! There's none left!";
	randomMessages[3] = "^4You're hungry ain't ya?";
	randomMessages[4] = "^5Woof";

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

	for(;;)
	{
		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 + maps\mp\_utility::vector_Scale(anglestoup(level.venderbottles[level.venderbottlescurrent].angles),4 + randomFloat(1));
			velocity = VectorNormalize(origin - point);// Calculate velocity and direction of the impact
			velocity = maps\mp\_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...
		{
			player iPrintlnBold(randomMessages[randomInt(randomMessages.size)]); // display one of the rather insulting messages to the player.
			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
}

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

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

	maps\mp\_load::main();
	maps\mp\_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;

	randomMessages = []; // List of random messages when player has had a bottle recently, you can have as many of them as you want.
	randomMessages[0] = "^2When was the last time you saw a fat rat?";
	randomMessages[1] = "^1Careful! You'll break it!";
	randomMessages[2] = "^3Oh no! There's none left!";
	randomMessages[3] = "^4You're hungry ain't ya?";
	randomMessages[4] = "^5Woof";

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

	for(;;)
	{
		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 + maps\mp\_utility::vector_Scale(anglestoup(level.venderbottles[level.venderbottlescurrent].angles),4 + randomFloat(1));
			velocity = VectorNormalize(origin - point);// Calculate velocity and direction of the impact
			velocity = maps\mp\_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...
		{
			player iPrintlnBold(randomMessages[randomInt(randomMessages.size)]); // display one of the rather insulting messages to the player.
			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