Call of Duty 5: Zombie Map Tutorial: Verruckt

From COD Modding & Mapping Wiki
Revision as of 17:48, 7 February 2012 by CoDEmanX (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Overview

If you have not gotten Mod Tools 1.2 patch yet, be sure to make backups if you've modified zombie mode specific files for your mods so you don't lose your work as there are updates to scripts, zone source, sounds, etc... Once you've downloaded the 1.2 update and installed it, you'll be ready to make a Nazi Zombie Verrückt-style map.

A lot of the script examples below are taken from the Verrückt nazi_zombie_asylum.GSC that can be found in raw\maps. It is the best source to look at if you have questions regarding implementation of any of the script examples below as they are all taken from Verrückt.

Implementation

Map Name

Custom zombie maps will need to have a prefix of nazi_zombie_ in order to work correctly with the game. Your scripts/ zone source/ soundalias/ etc should follow the same naming convention too.


Introscreen

An introscreen isn't required in terms of displaying a title, time, and date, but there are important zombie function calls needed at the beginning of the map that are called during the introscreen for Verruckt.


Strings

In \raw\english\localizedstrings you'll want to make a new string file for your map if you haven't done so already. Inside add a title, date, and place string. Save the file as Mapname.STR (changing Mapname to your level's name):

VERSION             "1"
CONFIG              "G:\CoD5\cod\cod5\bin\StringEd.cfg"
FILENOTES           ""

REFERENCE           TITLE
LANG_ENGLISH        "'My Zombie Map'"

REFERENCE           DATE
LANG_ENGLISH        "April 10, 1945"

REFERENCE           PLACE
LANG_ENGLISH        "The Reichstag, Berlin"

ENDMARKER


Precache

In your map script file, before you call maps/_zombiemode::main();, you'll want to precache the strings by adding (changing Mapname to your level's name):

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	precachestring(&"MAPNAME_TITLE");
	precachestring(&"MAPNAME_DATE");
	precachestring(&"MAPNAME_PLACE");
	// Snippet
	maps\_zombiemode::main();
}

The notation is actually &"STRINGFILENAME_REFERENCE", so if your strings aren't in Mapname.STR be sure to make appropriate changes to the script.

Script

In your level script, you'll want to create a function for your introscreen and call it after maps/_zombiemode::main();. An example function take from Verruckt is (changing Mapname to your level's name):

intro_screen()
{

	flag_wait( "all_players_connected" );
	wait(2);
	level.intro_hud = [];
	for(i = 0;  i < 3; i++)
	{
		level.intro_hud[i] = newHudElem();
		level.intro_hud[i].x = 0;
		level.intro_hud[i].y = 0;
		level.intro_hud[i].alignX = "left";
		level.intro_hud[i].alignY = "bottom";
		level.intro_hud[i].horzAlign = "left";
		level.intro_hud[i].vertAlign = "bottom";
		level.intro_hud[i].foreground = true;
		
		if ( level.splitscreen && !level.hidef )
		{
			level.intro_hud[i].fontScale = 2.75;
		}
		else
		{
			level.intro_hud[i].fontScale = 1.75;
		}
		level.intro_hud[i].alpha = 0.0;
		level.intro_hud[i].color = (1, 1, 1);
		level.intro_hud[i].inuse = false;
	}
	level.intro_hud[0].y = -110;
	level.intro_hud[1].y = -90;
	level.intro_hud[2].y = -70;
	
	
	level.intro_hud[0] settext(&"MAPNAME_TITLE");
	level.intro_hud[1] settext(&"MAPNAME_DATE");
	level.intro_hud[2] settext(&"MAPNAME_PLACE");
	
	for(i = 0 ; i < 3; i++)
	{
		level.intro_hud[i] FadeOverTime( 1.5 ); 
		level.intro_hud[i].alpha = 1;
		wait(1.5);

	
	}
	wait(1.5);
	for(i = 0 ; i < 3; i++)
	{
		level.intro_hud[i] FadeOverTime( 1.5 ); 
		level.intro_hud[i].alpha = 0;
		wait(1.5);
	
	
	}	
	//wait(1.5);
	for(i = 0 ; i < 3; i++)
	{
		level.intro_hud[i] destroy();
	
	}
	
	
	level thread magic_box_limit_location_init();

}

The notation is actually &"STRINGFILENAME_REFERENCE", so if your strings aren't in Mapname.STR be sure to make appropriate changes to the script.

Zone Source

In your Mapname.CSV in \zone_source, you'll have to add (changing Mapname to your level's name):

localize,mapname,,

The above example is assuming you've made your strings in your Mapname.STR, so if your strings are elsewhere be sure to make appropriate changes to the zone source.

Players

In Verruckt players are split into two pairs. You can do this following the extra steps below, or can choose to spawn your players together like in Prototype by following the first instruction outline.


Spawn Point Set Up

Add in an info_player_start found in the right click menu under the info sub menu. You'll need just one, but you'll have to add in four (4) script_structs to support four players. Place these anywhere you wish in your map and give them the KVP:

"targetname" "initial_spawn_points"

Normally you would then have to script a function to move the players around to each but the zombie mode utility already takes care of that for you if you use the above targetname.

Paired Players

If you want to split players into pairs, you'll need to set a flag in your level script shortly after maps/_zomebiemode::main(); is called.


#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode::main();
	// Snippet
	flag_set( "spawn_point_override" );
}

This will tell _zombiemode.gsc to not automatically move your players around. So you will need to create a function in your level script to do this. You can use the example below taken from Verruckt.


Spawn Points

You'll need to add an extra KVP to your player spawn script_structs in Radiant. For two (2) of them you'll need to add:

"script_noteworthy" "north_spawn"

And for the other two (2):

"script_noteworthy" "south_spawn"


Script

You'll want to make create an override function in your level script and make a call to the function shortly after maps/_zomebiemode::main(); is called.

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode::main();
	// Snippet
	level thread spawn_point_override();
}


The actual override function would look like:

spawn_point_override()
{
	flag_wait( "all_players_connected" );
	
	players = get_players(); 

	//spawn points are split, so grab them both seperately
	north_structs = getstructarray("north_spawn","script_noteworthy");
	south_structs = getstructarray("south_spawn","script_noteworthy");

	side1 = north_structs;
	side2 = south_structs;
	if(randomint(100)>50)
	{
		side1 = south_structs;
		side2 = north_structs;
	}
		
	//spawn players on a specific side, but randomize it up a bit
	for( i = 0; i < players.size; i++ )
	{
		
		//track zombies for sounds
		players[i] thread player_zombie_awareness();
		players[i] thread player_killstreak_timer();

			
		if(i<2)
		{
			players[i] setorigin( side1[i].origin ); 
			players[i] setplayerangles( side1[i].angles );
			players[i].respawn_point = side1[i];
			players[i].spawn_side = side1[i].script_noteworthy;
		}
		else
		{
			players[i] setorigin( side2[i].origin);
			players[i] setplayerangles( side2[i].angles);
			players[i].respawn_point = side2[i];
			players[i].spawn_side = side2[i].script_noteworthy;
		}	
	}	
}

Voice Overs

To get player dialogue working in your map such as lines like "Great, power's out.", "Low on ammo.", or "Need another clip!", you'll need to just add the player sound alias entries to your map sound alias. Please see the SoundAlias section under the Compiling & Distribution section for more information.

We have provided some of the new sounds from Verruckt. We encourage you to record your own sounds to enhance your mod.

Cameras

This isn't required but if you want a camera view pan at the end of the match add in two script_structs to your map. Make the first struct target the second one, the start and end points for the camera view respectively.

For the start point struct, you will need to give it two KVPs

"targetname" "intermission"
"speed" "20"

20 is a good number and the one used for the stock zombie map, feel free to tweak it.

You can repeat the process to have multiple camera path so the game will cycle randomly through them during the end of the game.


Zombies

For placing zombies, right cick in the ortographic view and use Actors> Axis> Zombie_ger_ber_sshonor. While you have the spawner selected, press N (by default) to open the entity window to give the zombie some KVPs

"script_noteworthy" "zombie_spawner"
"count" "9999"
"script_forcespawn" "1"
"targetname" "zombie_spawner_init"
"spawnflags" "3"


Nodes

Your map will need nodes placed based on where you want the Zombies to walk.

For Zombie Mode you will more than likely only need path nodes and traversals since Zombies won't be using action/cover nodes.

Traversals

Ideally, if you have Zombies breaking through windows, they will need to make their way through the window. This requires placing in negotiation nodes with a traverse animation, but these are already done for you in the form of prefabs. The one you will need is: _prefabs/traverse/wall_hop_zombie.map

For better positioning of the prefab, use the traverse brush in the prefab as a guide. The brush represents the lower end of the wall so you can match it up to the geometry in your map so the AI plays the wall hop animation nicely onto your geo.

As with the window barriers, the direction of the angle on the prefab is the direction the Zombies will wall hop through.

Blockers

Blockers are buyable furniture in the map. These aren't prefabbed because they aren't always the same and can differ depending on how a mapper wants to set them up.


Model

Start off by making a script_model, and assigning any model you want to block the path. Then you'll need to give it some KVPs.
Required:

"script_linkTo" ""
"spawnflags" "1"
"targetname" ""

Optional:

"script_firefx" "poltergeist"
"script_fxid" "large_ceiling_dust"
"script_noteworthy" "jiggle"

script_linkTo and targetname are left blank up above because it will be different for each furniture piece (hence a reason why there are no prefabs). They will be referenced in the Move Location section right below on how to properly set them up.


Move Location

You'll need to make a script_struct on where you want the blocker to move when triggered. You'll have to give a KVP:

"script_linkName" ""

The script_linkName should match up to the number as the script_linkTo on the blocker. You'll see a red line connected from the blocker to the struct when both are entered.


Buy Trigger

You'll then need to make triggers for players to buy the blocker, depending on how your map is laid out, you typically need to make two trigger_uses on both sides (in case the players unlock a different part of the map and come from the other side, depends on your map). Any triggers you want to use for that particular blocker will need a few KVPs:

"targetname" "zombie_debris"
"zombie_cost" ""
"target" ""

The target should match up to the targetname on the blocker. Once the triggers are set, you should see red lines connecting to the blocker.

zombie_cost you define on your own based on how much money you want required. These are the defined values (if you want a different price, you'll have to edit a string into the the zombie.STR in raw\english\localizedstrings):

100 200 250 500 750 1000 1250 1500 1750 2000


Weapons

Buyable weapons are like blockers in that they are unique and are left up to the user to determine price cost. Although, there are a few prefabs of buyable weapons that can be included in your map or used to learn from in \map_source\_prefabs\zombiemode\ -- they are the weapon_*.MAP prefabs.


Decal

You'll want to add the weapon chalk decals to your map, these can be found in Radiant under Locale>Decals.


Model

Add in a script_model.

Assign it a model of the weapon you want players to buy and place it by the decal.


Buy Trigger

Make a trigger_use.

Select the script_model you just made while the trigger is still selected and press W (by default) to make the trigger target the script_model weapon.

The trigger will need some KVPs:

"targetname" "weapon_upgrade"
"zombie_weapon_upgrade" ""
"zombie_cost" ""

The zombie_weapon_upgrade value should be the weapon name.

zombie_cost you define on your own based on how much money you want required. These are the stock defined values (if you want a different price, you'll have to edit a string into the the zombie.STR in raw\english\localizedstrings):

100 200 250 500 750 1000 1250 1500 1750 2000


Boards

The boards Zombies rip through can easily be placed in your map using the prefabs: \map_source\_prefabs\zombiemode\window_med.map or \map_source\_prefabs\zombiemode\factory_large_window.map

The direction of the angle on the prefab is the direction the Zombie will approach it and break through.

Toilet Easter Egg

Radiant

Place a misc_model using the xmodel/static_berlin_toilet model.

Create a trigger_multiple near the chair. Give it a KVP of:

"targetname" "toilet"


Script

You'll need to call a toilet usage function after maps\_zombiemode::main();

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode::main();
	// Snippet
	level thread toilet_useage();
}

The actual toilet function will look like:

toilet_useage()
{

	toilet_counter = 0;
	toilet_trig = getent("toilet", "targetname");
	toilet_trig SetCursorHint( "HINT_NOICON" );
	toilet_trig UseTriggerRequireLookAt();

	players = getplayers();
	if (!IsDefined (level.eggs))
	{
		level.eggs = 0;
	}
	while (1)
	{
		wait(0.5);
		for(i=0;i<players.size;i++)
		{			
			toilet_trig waittill( "trigger", players);
			toilet_trig playsound ("toilet_flush", "sound_done");				
			toilet_trig waittill ("sound_done");				
			toilet_counter ++;
			if(toilet_counter == 3)
			{
				playsoundatposition ("cha_ching", toilet_trig.origin);
				level.eggs = 1;
				setmusicstate("eggs");
				wait(245);	
				setmusicstate("WAVE_1");
				level.eggs = 0;
				
			}
				
		}
	}
	
}


Sound

Dentist Chair Easter Egg

Radiant

Place a misc_model using the xmodel/zombie_asylum_chair (or xmodel/zombie_asylum_chair_on) model.

Create a trigger_multiple near the chair. Give it a KVP of:

"targetname" "dentist_chair"


Script

You'll need to call a chair usage function after maps\_zombiemode::main();

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode::main();
	// Snippet
	level thread chair_useage();
}

The actual chair function will look like:

chair_useage()
{

	chair_counter = 0;
	chair_trig = getent("dentist_chair", "targetname");
	chair_trig SetCursorHint( "HINT_NOICON" );
	chair_trig UseTriggerRequireLookAt();

	players = getplayers();
	while (1)
	{
		wait(0.05);
		for(i=0;i<players.size;i++)
		{			
			chair_trig waittill( "trigger", players);
			chair_counter ++;
			if(chair_counter == 3)
			{
				playsoundatposition ("chair", chair_trig.origin);
				chair_counter = 0;
			}
				
		}
	}
	
}


Sound

Water Sheeting

Water sheeting isn't required, but you can feel free to add it by following the steps below.

Triggers

Make trigger_multiples in your map where you have water FX dripping down and give each trigger the KVP:

"targetname" "waterfall"


Script

In your level script, after you call maps/_zombiemode::main();, you'll want to activate your water triggers by getting them and threading them to a water sheet function:

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode::main();
	// Snippet
	water_trigs = getentarray( "waterfall","targetname" );
	array_thread( water_trigs,::watersheet_on_trigger );
}

The actual watersheet function is:

watersheet_on_trigger( )
{

	while( 1 )
	{
		self waittill( "trigger", who );
		
		if( isDefined(who) && isplayer(who) && isAlive(who)  )
		{
			if( !who maps\_laststand::player_is_in_laststand() ) 
			{
				who setwatersheeting(true, 3);
				wait( 0.1 );
			}
		}
	}
}

Bouncing Betties

Decal

You'll want to add the betty chalk decals to your map, these can be found in Radiant under Locale>Decals.


Model

Add in a script_model.

Assign it a bouncing betty model (xmodel/weapon_bbetty_mine) and place it by the decal.


Buy Trigger

Make a trigger_use.

Select the script_model you just made while the trigger is still selected and press W (by default) to make the trigger target the script_model weapon.

The trigger will need a KVP:

"targetname" "betty_purchase"

Script

In your level script, after you call maps/_zombiemode::main();, you'll want to activate your betty triggers by getting them and threading them to a series of bouncing betties functions:

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode::main();
	// Snippet
	level thread purchase_bouncing_betties();
	level thread give_betties_after_rounds();
}


The actual functions would look like:

purchase_bouncing_betties()
{
	trigs = getentarray("betty_purchase","targetname");
	array_thread(trigs,::buy_bouncing_betties);
}

buy_bouncing_betties()
{
	self.zombie_cost = 1000;
	
	betty_model = getent(self.target, "targetname");
	betty_model hide();
	self sethintstring( &"ZOMBIE_BETTY_PURCHASE" );

	level thread set_betty_visible();
	while(1)
	{
		self waittill("trigger",who);
		if( who in_revive_trigger() )
		{
			continue;
		}
						
		if( is_player_valid( who ) )
		{
			
			if( who.score >= self.zombie_cost )
			{				
				if(!isDefined(who.has_betties))
				{
					who.has_betties = 1;
					play_sound_at_pos( "purchase", self.origin );
					betty_model show();
					//set the score
					who maps\_zombiemode_score::minus_to_player_score( self.zombie_cost ); 
					who thread bouncing_betty_setup();
					//who thread show_betty_hint("betty_purchased");

					trigs = getentarray("betty_purchase","targetname");
					for(i = 0; i < trigs.size; i++)
					{
						trigs[i] SetInvisibleToPlayer(who);
					}
				}
				
			}
		}
	}
}

set_betty_visible()
{
	players = getplayers();	
	trigs = getentarray("betty_purchase","targetname");

	while(1)
	{
	for(j = 0; j < players.size; j++)
	{
		if( !isdefined(players[j].has_betties))
		{						
			for(i = 0; i < trigs.size; i++)
			{
				trigs[i] SetInvisibleToPlayer(players[j], false);
			}
		}
	}
	
	wait(1);
	}
}

bouncing_betty_watch()
{

	while(1)
	{
		self waittill("grenade_fire",betty,weapname);
		if(weapname == "mine_bouncing_betty")
		{
			betty.owner = self;
			betty thread betty_think();
			self thread betty_death_think();
		}
	}
}

betty_death_think()
{
	self waittill("death");
	
	if(isDefined(self.trigger))
	{
		self.trigger delete();
	}
	
	self delete();
	
}

bouncing_betty_setup()
{	
	self thread bouncing_betty_watch();
	
	self giveweapon("mine_bouncing_betty");
	self setactionslot(4,"weapon","mine_bouncing_betty");
	self setweaponammostock("mine_bouncing_betty",5);
}

betty_loadout()
{
	flag_wait("all_players_connected");
	//players = get_players();
	//array_thread(players,::bouncing_betty_setup);
}

betty_think()
{
	wait(2);
	trigger = spawn("trigger_radius",self.origin,9,80,64);
	trigger waittill( "trigger" );
	trigger = trigger;
	self playsound("betty_activated");
	wait(.1);	
	fake_model = spawn("script_model",self.origin);
	fake_model setmodel(self.model);
	self hide();
	tag_origin = spawn("script_model",self.origin);
	tag_origin setmodel("tag_origin");
	tag_origin linkto(fake_model);
	temp_origin = self.origin;
	playfxontag(level._effect["betty_trail"],tag_origin,"tag_origin");
	fake_model moveto (self.origin + (0,0,32),.2);
	fake_model waittill("movedone");
	playfx(level._effect["betty_explode"],fake_model.origin);
	earthquake(1,.4, temp_origin, 512);
	
	//CHris_P - betties do no damage to the players
	zombs = getaiarray("axis");
	for(i=0;i<zombs.size;i++)
	{
		if(DistanceSquared(zombs[i].origin, temp_origin) < 200 * 200)
		{
			zombs[i] thread maps\_zombiemode_spawner::zombie_damage( "MOD_EXPLOSIVE", "none", zombs[i].origin, self.owner );
		}
	}
	//radiusdamage(self.origin,128,1000,75,self.owner);
	
	trigger delete();
	fake_model delete();
	tag_origin delete();

	if(isdefined(self))
	{
		self delete();
	}
}

betty_smoke_trail()
{
	self.tag_origin = spawn("script_model",self.origin);
	self.tag_origin setmodel("tag_origin");
	playfxontag(level._effect["betty_trail"],self.tag_origin,"tag_origin");
	self.tag_origin moveto(self.tag_origin.origin + (0,0,100),.15);
}

give_betties_after_rounds()
{
	while(1)
	{
		level waittill( "between_round_over" );
		{
			players = get_players();
			for(i=0;i<players.size;i++)
			{
				if(isDefined(players[i].has_betties))
				{
					players[i] giveweapon("mine_bouncing_betty");
					players[i]  setactionslot(4,"weapon","mine_bouncing_betty");
					players[i]  setweaponammoclip("mine_bouncing_betty",2);
				}
			}
		}
	}
}

Perks-a-Cola

Radiant

You can find four (4) easy to drop prefabs for the Perks-a-Cola machines in \map_source\_prefabs\zombiemode\.

You can only have one (1) of each, but you do not need to have all four (4) in your map.

vending_doubletap.MAP Double Tap Faster Fire Rate.
vending_juggernaut.MAP Juggernaut More Health.
vending_revive.MAP Revive Faster Reviving.
vending_sleight.MAP Sleight of Hand Faster Reloading.

Script

If you have Perks-a-Cola machines in your map you will need to initialize them in script. You can do this by making a call to the perks GSC before _zombiemode::main();

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode_perks::init();
	maps\_zombiemode::main();
	// Snippet
}


Electric System

Power Switch

Radiant

Make a trigger_multiple and give it a KVP of:

"use_master_switch" "targetname"

Script

You'll want to call a master electric switch function shortly after maps/_zombiemode::main();:

#include common_scripts\utility; 
#include maps\_utility;
#include maps\_zombiemode_utility;

#using_animtree("generic_human");

main()
{
	// Snippet
	maps\_zombiemode::main();
	// Snippet
	level thread master_electric_switch();

}

The actual master electric switch function will look like:

master_electric_switch()
{
	
	trig = getent("use_master_switch","targetname");
	master_switch = getent("master_switch","targetname");	
	master_switch notsolid();
	//master_switch rotatepitch(90,1);
	trig sethintstring(&"ZOMBIE_ELECTRIC_SWITCH");
	
	//turn off the buyable door triggers downstairs
	door_trigs = getentarray("electric_door","script_noteworthy");
	//door_trigs[0] sethintstring(&"ZOMBIE_FLAMES_UNAVAILABLE");
	//door_trigs[0] UseTriggerRequireLookAt();
	array_thread(door_trigs,::set_door_unusable);
	array_thread(door_trigs,::play_door_dialog);
	fx_org = spawn("script_model", (-674.922, -300.473, 284.125));
	fx_org setmodel("tag_origin");
	fx_org.angles = (0, 90, 0);
	playfxontag(level._effect["electric_power_gen_idle"], fx_org, "tag_origin");  
	
	
		
	cheat = false;
	
/# 
	if( GetDvarInt( "zombie_cheat" ) >= 3 )
	{
		wait( 5 );
		cheat = true;
	}
#/

	if ( cheat != true )
	{
		trig waittill("trigger",user);
	}
	
	array_thread(door_trigs,::trigger_off);
	master_switch rotateroll(-90,.3);

	//TO DO (TUEY) - kick off a 'switch' on client script here that operates similiarly to Berlin2 subway.
	master_switch playsound("switch_flip");

	//level thread electric_current_open_middle_door();
	//level thread electric_current_revive_machine();
	//level thread electric_current_reload_machine();
	//level thread electric_current_doubletap_machine();
	//level thread electric_current_juggernog_machine();

	clientnotify("revive_on");
	clientnotify("middle_door_open");
	clientnotify("fast_reload_on");
	clientnotify("doubletap_on");
	clientnotify("jugger_on");
	level notify("switch_flipped");
	maps\_audio::disable_bump_trigger("switch_door_trig");
	level thread play_the_numbers();
	left_org = getent("audio_swtch_left", "targetname");
	right_org = getent("audio_swtch_right", "targetname");
	left_org_b = getent("audio_swtch_b_left", "targetname");
	right_org_b = getent("audio_swtch_b_right", "targetname");

	if( isdefined (left_org)) 
	{
		left_org playsound("amb_sparks_l");
	}
	if( isdefined (left_org_b)) 
	{
		left_org playsound("amb_sparks_l_b");
	}
	if( isdefined (right_org)) 
	{
		right_org playsound("amb_sparks_r");
	}
	if( isdefined (right_org_b)) 
	{
		right_org playsound("amb_sparks_r_b");
	}
	// TUEY - Sets the "ON" state for all electrical systems via client scripts
	SetClientSysState("levelNotify","start_lights");
	level thread play_pa_system();	

	//cut for now :(
	//enable the MG fountain 
	//level thread fountain_mg42_activate();
	
	//set the trigger hint on the fountain
	//getent("trig_courtyard_mg","targetname") sethintstring(&"ZOMBIE_FLAMES_UNAVAILABLE");
	
	flag_set("electric_switch_used");
	trig delete();	
	
	//enable the electric traps
	traps = getentarray("gas_access","targetname");
	for(i=0;i<traps.size;i++)
	{
		traps[i] sethintstring(&"ZOMBIE_BUTTON_NORTH_FLAMES");
		traps[i].is_available = true;
	}
	
	master_switch waittill("rotatedone");
	playfx(level._effect["switch_sparks"] ,getstruct("switch_fx","targetname").origin);
	
	//activate perks-a-cola
	level notify( "master_switch_activated" );
	fx_org delete();
	
	fx_org = spawn("script_model", (-675.021, -300.906, 283.724));
	fx_org setmodel("tag_origin");
	fx_org.angles = (0, 90, 0);
	playfxontag(level._effect["electric_power_gen_on"], fx_org, "tag_origin");  
	fx_org playloopsound("elec_current_loop");


	//elec room fx on
	//playfx(level._effect["elec_room_on"], (-440, -208, 8));
	
	//turn on green lights above the zapper trap doors
	north_zapper_light_green();
	south_zapper_light_green();

	wait(6);
	fx_org stoploopsound();
	level notify ("sleight_on");
	level notify("revive_on");
	level notify ("electric_on_middle_door");
	level notify ("doubletap_on");
	level notify ("juggernog_on");



	//level waittill("electric_on_middle_door");
	doors = getentarray(door_trigs[0].target,"targetname");
	open_bottom_doors(doors);
	
	exploder(101);
	//exploder(201);
	
	//This wait is to time out the SFX properly
	wait(8);
	playsoundatposition ("amb_sparks_l_end", left_org.origin);
	playsoundatposition ("amb_sparks_r_end", right_org.origin);
	
}


Doors

Traps

Compiling & Distribution

Script

Sound Alias

You'll want to copy the nazi_zombie_asylum.CSV from \raw\soundaiases\ and rename it your Nazi Zombie map. This will allow all the Zombie Verruckt sounds to be usable in your map. You can of course go into the CSV and remove any sounds you aren't using or do not wish to use.


Load Screen

Mission Screen

String

In \raw\english\localizedstrings you'll want to make a new string file for your map mod if you haven't done so already. Inside add a title for your map that will appear on the Coop map selection list. Save the file as MyMod.STR (Feel free to change the STR filename to whatever you like, just be sure to make the appropiate change in your ARENA file and Zone Source):

VERSION             "1"
CONFIG              "G:\CoD5\cod\cod5\bin\StringEd.cfg"
FILENOTES           ""

REFERENCE           YOURMAPNAME
LANG_ENGLISH        "My First Verruckt Map"

ENDMARKER


Arena File

The arena file does not go into an IWD or FastFile, so you'll want to make one in your map's mod folder called mod.arena. Below is an example of what it should contain, making the appropriate change for your map:

{
	map			"mak"
	longname	"MENU_LEVEL_MAK"
	gametype	"cmp arc"
}
{
	map			"nazi_zombie_prototype"
	longname	"MOD_LEVEL_ZOMBIE_PROTOTYPE"
	gametype	"zom"
}
{
	map			"nazi_zombie_asylum"
	longname	"MENU_LEVEL_ZOMBIE_ASYLUM"
	gametype	"zom"
}
{
	map			"nazi_zombie_YOURMAPNAME"
	longname	"MYMOD_YOURMAPNAME"
	gametype	"zom"
}


Zone Source

Map

You can use the nazi_zombie_asylum.CSV from \zone_source\ as a base by coping it and renaming it your Nazi Zombie map. You'll have to go in and make changes as noted below (you do not need all of them unless you have made the files for your map). Please note again, the example below is just a fragment from the Asylum CSV on where you would need to make changes specific to your map -- the example below alone would not work properly.

rawfile,clientscripts/nazi_zombie_YOURMAPNAME.csc
rawfile,clientscripts/nazi_zombie_YOURMAPNAME_amb.csc
rawfile,clientscripts/nazi_zombie_YOURMAPNAME_fx.csc
rawfile,clientscripts/createfx/nazi_zombie_YOURMAPNAME_fx.csc
rawfile,maps/nazi_zombie_YOURMAPNAME.gsc
rawfile,maps/nazi_zombie_YOURMAPNAME_fx.gsc
rawfile,maps/createfx/nazi_zombie_YOURMAPNAME_fx.gsc

// Snippet

col_map_sp,maps/nazi_zombie_YOURMAPNAME.d3dbsp

sound,common,nazi_zombie_YOURMAPNAME,all_sp
sound,generic,nazi_zombie_YOURMAPNAME,all_sp
sound,voiceovers,nazi_zombie_YOURMAPNAME,all_sp
sound,requests,nazi_zombie_YOURMAPNAME,all_sp
sound,character,nazi_zombie_YOURMAPNAME,all_sp
sound,projectiles,nazi_zombie_YOURMAPNAME,all_sp
sound,nazi_zombie_asylum,nazi_zombie_YOURMAPNAME,all_sp
sound,physics,nazi_zombie_YOURMAPNAME,all_sp
sound,destructibles,nazi_zombie_YOURMAPNAME,all_sp

// Snippet

// Fog and vision,
rawfile,maps/createart/nazi_zombie_YOURMAPNAME_art.gsc

You'll also want to remove the FX raw asset inclusion section at the bottom of the Asylum CSV unless you plan on using them in your map, they are not Zombie Mode specific.

Load

In \zone_source\ you'll want to make a nazi_zombie_YOURMAPNAME_load.CSV. This will store information for your Coop loadscreen for Fast File creation. It should look along the lines of (with necessary changes for your map):

ignore,code_post_gfx
ignore,common
ui_map,maps/nazi_zombie_YOURMAPNAME.csv

maps/nazi_zombie_YOURMAPNAME.csv refers to a CSV that you should have made for your loadscreen. If it does not exist please see the Loadscreen creation segment above.

Compile

Once you have everything done, go into Launcher and select your map in the map list. Check off:

Compile BSP
Compile Lights
Connect Paths
Compile Reflections (If you added reflection nodes)
Build Fast Files
Run Map After Compile (If you want to play it right away)

You may want to make your map a Mod Specific Map. Make a folder in your [root]\mods folder with the same name as your map for organization, then in Launcher check the Mod Specific Map box and select your mod on the drop down list.


Distribution

Once you've completed the above steps, you'll want to grab a few files to ZIP them up to share with your friends and the PC Community. The list of things are:

  • Map FastFile
  • Map _Load FastFile
  • Map IWD
  • Map Arena

You'll probably want to create a ReadMe text file with your information, how to install your map, how to launch the map, and any other relevant information.

Once you have all these things, ZIP them together and give that ZIP to others. You'll need to let them know (in your ReadMe) that they must go to the Mods menu when they launch the game and launch your map's mod before attempting to play the map in Solo or Coop.