Wolfenstein 3D (Wolf 3D) — компьютерная игра, родоначальник жанра «шутер от первого лица». Wolfenstein 3D разработана компанией id Software и издана компанией Apogee Software 5 мая 1992 под DOS.
Решил я перепройти эту игру в очередной раз, но не просто так, а открыв все
секреты... Но, естественно, бегать и прозванивать все стены мне не очень
хочется .
Решение простое - пойти в гугл и найти карты (хардкорный про гейминг во всей
красе ). Но не тут то было. Нормальных карт я не нашел, а ломать глаза над
картинкой в 300x300 пикселей... Спасибо - не надо.
Вторая итерация простого решения - сделать карты самому... Для этого в гугле ищем как и где хранятся данные об уровнях...
Нам понадобятся 2 файла: GAMEMAPS.WL6 и MAPHEAD.WL6. WL6 - значит в файле хранятся данные о 6-ти актах (говорят есть еще WL1 и WL3).
Алгоритмы декодирования и декомпрессии расписывать, думаю, не стоит.
Лучше сразу перейти к их реализации (DecodeMapDataFile и DecompressMapDataFile)
на C# (люблю и почитаю этот язык, но кодировать на нем не умею ):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
namespace Wolfenstein3DMapGrabber
{
class Wolfenstein3DMapGrabber
{
/*
* Start
*/
static void Main(string[] args)
{
string ProjectPath = System.IO.Directory.GetCurrentDirectory();
Int32[] Offsets;
if ((Offsets = Wolfenstein3DMapGrabber.ParseMapHeadFile(ProjectPath + "/Wolfenstein3DData/MAPHEAD.WL6")).Length > 0)
{
Console.WriteLine("MAPHEAD.WL6: Data received (Blocks count = " + Offsets.Length + ")");
Wolfenstein3DMapGrabber.ParseMapDataFile(ProjectPath + "/Wolfenstein3DData/GAMEMAPS.WL6", Offsets);
}
Console.WriteLine("Something done! Press any key...");
Console.ReadKey();
}
/*
* Прочитать блок заданной длинны из стрима
*/
private static byte[] ReadBytes(FileStream Stream, int Offset, int Length)
{
byte[] BuffBytes = new byte[Length];
int BytesRead = 0;
Stream.Seek(Offset, SeekOrigin.Begin);
if ((BytesRead = Stream.Read(BuffBytes, 0, Length)) < Length)
{
Stream.Close();
throw new Exception("FATAL: IO error (" + Length + " vs. " + BytesRead + ")");
}
return BuffBytes;
}
/*
* Декодировать ("декармакизировать") данные по карте (unhuffman)
* а-ля CAL_CarmackExpand в оригинале
*/
private static ushort[] DecodeMapDataFile(byte[] Input, short NumberBytesAfterDecoding)
{
int WordCounter;
int InCounter;
int OutCounter;
ushort Copy;
ushort[] Output;
byte Low;
byte High;
int Offset;
WordCounter = NumberBytesAfterDecoding / 2;
InCounter = 0;
OutCounter = 0;
Output = new ushort[WordCounter];
do
{
Low = Input[InCounter++];
High = Input[InCounter++];
if (High == 0xA7)
{
// If high byte of value is A7 then:
if (Low == 0)
{
// If low byte is 00 then:
// - high byte of value is A7
// - low byte of value is the next byte
Output[OutCounter++] = (ushort)((High << 8) | Input[InCounter]);
++InCounter;
--WordCounter;
}
else
{
// - the low byte is a count (ie. # of integers we want to reuse).
// - the next byte is the # of integers we want to move back in order to reuse data we have already decoded.
Offset = Input[InCounter];
++InCounter;
Copy = (ushort)(OutCounter - Offset);
WordCounter -= Low;
while (Low-- != 0)
{
Output[OutCounter++] = Output[Copy++];
}
}
}
else if (High == 0xA8)
{
// If high byte of value is A8 then:
if (Low == 0)
{
// If low byte is 00 then:
// - high byte of value is A8
// - low byte of value is the next byte
Output[OutCounter++] = (ushort)((High << 8) | Input[InCounter]);
++InCounter;
--WordCounter;
}
else
{
// - the low byte is a count (ie. # of integers we want to reuse).
// - the next integer (2 bytes) is the # of integers
// we want to skip over, starting at the beginning of the buffer being used to hold data we have already decoded
// (the first integer in the buffer being the # of bytes after decompressing).
Offset = (ushort)((Input[InCounter + 1] << 8) | Input[InCounter]);
InCounter += 2;
Copy = (ushort)(0 + Offset - 1);
WordCounter -= Low;
while (Low-- != 0)
{
Output[OutCounter++] = Output[Copy++];
}
}
}
else
{
// If high byte is not A7 or A8, then store the value as is.
Output[OutCounter++] = (ushort)((High << 8) | Low);
--WordCounter;
}
} while (WordCounter != 0);
return Output;
}
/*
* Распаковать данные (unRLEW)
*/
private static ushort[] DecompressMapDataFile(ushort[] Data, short NumberOfBytesAfterDecompressing)
{
int WordsCount = NumberOfBytesAfterDecompressing / 2;
ushort[] Output = new ushort[WordsCount];
uint InCounter = 0;
uint OutCounter = 0;
ushort Number = 0;
ushort Repeat;
do
{
Number = Data[InCounter++];
if (Number == 0xABCD)
{
// If value == ABCD
// - the next integer is # of repetitions
// - the next integer is value to repeat
Repeat = Data[InCounter++];
Number = Data[InCounter++];
WordsCount -= Repeat;
while (Repeat-- > 0)
{
Output[OutCounter++] = Number;
}
}
else
{
// If value is not ABCD then store the value as is.
Output[OutCounter++] = Number;
WordsCount--;
}
} while (WordsCount > 0);
return Output;
}
/*
* Разбираем GAMEMAPS файл
*/
private static bool ParseMapDataFile(string FileName, Int32[] Offsets)
{
int Length = 0;
FileStream Stream;
byte[] BuffBytes;
Int32 DataShift = 0;
Int32 DataLength = 0;
short NumberBytesAfterDecoding;
short NumberOfBytesAfterDecompressing;
ushort[] Data;
string ExportData;
string LevelName;
int[] Shifts;
string FileNameSuffix;
Stream = new FileStream(FileName, FileMode.Open, FileAccess.Read);
Length = (int)Stream.Length;
Shifts = new int[] { 0, 1 };
// забираем данные по заголовкам карт
for (int i = 0; i < Offsets.Length; ++i)
{
if (Offsets[i] > 0)
{
for (int k = 0; k < Shifts.Length; ++k)
{
if (Shifts[k] == 0)
{
FileNameSuffix = "-Map";
}
else
{
FileNameSuffix = "-Objects";
}
/*
Level
Header Data
Offset Type Description
------ ---- -------------------------------
0000 long File offset of Map_Block
0004 long File offset of Object_Block
0008 long File offset of Unknown_Block
000C int File size of Map_Block
000E int File size of Object_Block
0010 int File size of Unknown_Block
0012 int Horizontal map size (# of squares)
0014 int Vertical map size (# of squares)
0016 char Level name (ascii) (null terminated)
0026 char "!ID!" (4 bytes)
*/
DataShift = BitConverter.ToInt32(Wolfenstein3DMapGrabber.ReadBytes(Stream, Offsets[i] + Shifts[k] * 4, 4), 0);
DataLength = BitConverter.ToInt16(Wolfenstein3DMapGrabber.ReadBytes(Stream, Offsets[i] + 12 + Shifts[k] * 2, 2), 0);
/*
Block Data
Offset Type Description
------ ---- --------------------------------------------
0000 int # of data bytes after decoding (count includes the two bytes at Block Offset 0002).
0002 int # of data bytes after decompresssing.
0004 int Data bytes. Values are stored as 2 byte integers (low byte first).
*/
NumberBytesAfterDecoding = BitConverter.ToInt16(Wolfenstein3DMapGrabber.ReadBytes(Stream, DataShift, 2), 0);
NumberOfBytesAfterDecompressing = BitConverter.ToInt16(Wolfenstein3DMapGrabber.ReadBytes(Stream, DataShift + 2, 2), 0);
BuffBytes = Wolfenstein3DMapGrabber.ReadBytes(Stream, DataShift + 4, DataLength);
LevelName = System.Text.Encoding.ASCII.GetString(Wolfenstein3DMapGrabber.ReadBytes(Stream, Offsets[i] + 22, 16)).Replace("\0", "");
Console.WriteLine("GAMEMAPS.WL6:");
Console.WriteLine(" LEVEL " + i);
Console.WriteLine(" DATA OFFSET: " + Offsets[i]);
Console.WriteLine(" NAME: " + LevelName);
Console.WriteLine(" NUMBER OF BYTES AFTER DECODING: " + NumberBytesAfterDecoding);
Console.WriteLine(" NUMBER OF BYTES AFTER DECOMPRESSING: " + NumberOfBytesAfterDecompressing);
Console.WriteLine(" BUFFER LENGTH: " + BuffBytes.Length);
// Pass1 (decoding):
Data = Wolfenstein3DMapGrabber.DecodeMapDataFile(BuffBytes, NumberBytesAfterDecoding);
// Pass2 (decompressing):
Data = Wolfenstein3DMapGrabber.DecompressMapDataFile(Data, NumberOfBytesAfterDecompressing);
// Export decompressing data
ExportData = "";
for (int j = 0; j < Data.Length; j++)
{
if (ExportData != "")
{
ExportData += ",";
}
ExportData += Data[j].ToString();
}
System.IO.StreamWriter OutputFile = new System.IO.StreamWriter(@".\" + LevelName + FileNameSuffix + ".txt");
OutputFile.WriteLine(ExportData);
OutputFile.Close();
}
}
}
Stream.Close();
return false;
}
/*
* Разобрать MAPHEAD файл
*/
private static Int32[] ParseMapHeadFile(string FileName)
{
int Length = 0;
int Iteration = 0;
int DataLength = 0;
int BlockLengthInBytes = 4;
FileStream Stream;
Int32[] Offsets;
byte[] BuffBytes;
try
{
if (!File.Exists(FileName))
{
throw new Exception("MAPHEAD.WL6: File not exists");
}
Stream = new FileStream(FileName, FileMode.Open, FileAccess.Read);
Length = (int)Stream.Length;
BuffBytes = Wolfenstein3DMapGrabber.ReadBytes(Stream, 0, 2);
// первые 2 байта - ключ для декомпрессии (см. DecompressMapDataFile)
if (BuffBytes[0] != 0xCD || BuffBytes[1] != 0xAB)
{
throw new Exception("MAPHEAD.WL6: Wrong file format");
}
DataLength = Length - 2;
if (DataLength <= 0 || DataLength % BlockLengthInBytes != 0)
{
throw new Exception("MAPHEAD.WL6: Wrong file format");
}
Offsets = new Int32[DataLength / BlockLengthInBytes];
Length = 2;
// файл содержит в себе набор int'ов (смещений в файле GAMEMAPS)
while ((BuffBytes = Wolfenstein3DMapGrabber.ReadBytes(Stream, Length, BlockLengthInBytes)).Length > 0)
{
Length += BuffBytes.Length;
Offsets[Iteration] = BitConverter.ToInt32(BuffBytes, 0);
Iteration++;
if (Iteration == Offsets.Length)
{
Stream.Close();
return Offsets;
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return new Int32[0];
}
}
}
На выходе получаем набор файликов (по 2 на уровень: *-Map.txt и *-Objects.txt). Они содержат (64 * 64) чисел. Как вы догадались - уровень представляет из себя матрицу 64x64 клетки.
На гитхабе можно посмотреть листинг этих кодов: https://github.....
Чуть более понятнный листинг ниже.
[MAP VALUES]
01 Wall: Grey stone cube
02 Wall: Grey stone cube
03 Wall: Grey stone cube with flag
04 Wall: Grey stone cube with picture
05 Wall: Blue stone cube with cell door
06 Wall: Grey stone cube with bird and archway
07 Wall: Blue stone cube with cell door and skeleton
08 Wall: Blue stone cube
09 Wall: Blue stone cube
0a Wall: Wood cube with picture of bird
0b Wall: Wood cube with picture
0c Wall: Wood cube
0d Eleva: Elevator door (no red door handle) (ie. from prvs level)
0e Wall: Steel cube (N/S="Verbotem", E/W="Achtung")
0f Wall: Steel cube
10 Exit: Landscape view (N/S=sky & green land, E/W=dark & stars?)
11 Wall: Red brick cube
12 Wall: Red brick cube with green wreath
13 Wall: Purple and green cube
14 Wall: Red brick cube with tapestry of bird
15 Wall: Inside elevator (N/S=hand rail, E/W=controls in down position)
16 Wall: Inside elevator (N/S=blank wall, E/W=controls in up position)
17 Wall: Wood cube with green branches over a cross
18 Wall: (v1.1+) Grey stone cube with green moss/slime
19 Wall: Pink and green cube
1a Wall: (v1.1+) Grey stone cube with green moss/slime
1b Wall: Grey stone cube
1c Wall: Grey stone cube (N/S="Verbotem", E/W="Achtung")
1d Wall: (Eps.2+) Brown cave
1e Wall: (Eps.2+) Brown cave with blood
1f Wall: (Eps.2+) Brown cave with blood
20 Wall: (Eps.2+) Brown cave with blood
21 Wall: (Eps.2+) Stained glass window of Hitler
22 Wall: (Eps.2+) Blue brick wall with skulls
23 Wall: (Eps.2+) Grey brick wall
24 Wall: (Eps.2+) Blue brick wall with swastikas
25 Wall: (Eps.2+) Grey brick wall with hole
26 Wall: (Eps.2+) Red/grey/brown wall
27 Wall: (Eps.2+) Grey brick wall with crack
28 Wall: (Eps.2+) Blue brick wall
29 Wall: (Eps.2+) Blue stone wall with verboten sign
2a Wall: (Eps.2+) Brown tiles
2b Wall: (Eps.2+) Grey brick wall with map
2c Wall: (Eps.2+) Orange stone wall
2d Wall: (Eps.2+) Orange stone wall
2e Wall: (Eps.2+) Brown tiles
2f Wall: (Eps.2+) Brown tiles with banner
30 Wall: (Eps.2+) Orange panel on wood wall
31 Wall: (Eps.2+) Grey brick wall with Hitler
40 Wall: Grey stone cube
41 Wall: Grey stone cube
42 Wall: Grey stone cube
43 Wall: Grey stone cube with flag
44 Wall: Grey stone cube with picture
45 Wall: Blue stone cube with cell door
46 Wall: Grey stone cube with bird and archway
47 Wall: Blue stone cube with cell door and skeleton
48 Wall: Blue stone cube
49 Wall: Blue stone cube
4a Wall: Wood cube with picture of bird
4b Wall: Wood cube with picture
4c Wall: Wood cube
4d Eleva: Elevator door (no red door handle)
4e Wall: Steel cube (N/S="Verbotem", E/W="Achtung")
4f Wall: Steel cube
50 Exit: Exit (N/S=sky & green land, E/W=dark & stars?)
51 Wall: Red brick cube
52 Wall: Red brick cube with green wreath
53 Wall: Pink and green cube
54 Wall: Red brick cube with tapestry of bird
55 Wall: Inside elevator (N/S=hand rail, E/W=controls in down position)
56 Wall: Inside elevator (N/S=blank wall, E/W=controls in up position)
57 Wall: Wood cube with green branches over a cross
59 Wall: Pink and green cube
5a VDoor: Steel door (east/west doorway) (vertical on map)
5b HDoor: Steel door (north/south doorway) (horizontal on map)
5c Lock: Locked version of 5a (need gold key to open)
5d Lock: Locked version of 5b (need gold key to open)
5e Lock: Locked version of 5a (need silver key to open)
5f Lock: Locked version of 5b (need silver key to open)
60 Lock: Locked version of 5a (can't open)
61 Lock: Locked version of 5b (can't open)
62 Lock: Locked version of 5a (can't open)
63 Lock: Locked version of 5b (can't open)
64 Eleva: Elevator door with a grey stone cube on north and south side
65 Eleva: Elevator door with a grey stone cube on east and west side
6b Floor: Floor
6c Floor: Floor
6d Floor: Floor
6e Floor: Floor
6f Floor: Floor
70 Floor: Floor
71 Floor: Floor
72 Floor: Floor
73 Floor: Floor
74 Floor: Floor
75 Floor: Floor
76 Floor: Floor
77 Floor: Floor
78 Floor: Floor
79 Floor: Floor
7a Floor: Floor
7b Floor: Floor
7c Floor: Floor
7d Floor: Floor
7e Floor: Floor
7f Floor: Floor
80 Floor: Floor
81 Floor: Floor
82 Floor: Floor
83 Floor: Floor
84 Floor: Floor
85 Floor: Floor
86 Floor: Floor
87 Floor: Floor
88 Floor: Floor
89 Floor: Floor
8a Floor: Floor
8b Floor: Floor
8c Floor: Floor
8d Floor: Floor
8e Floor: Floor
8f Floor: Floor
[OBJECT VALUES]
00 Nothi: Nothing
13 Start: Starting location, facing north
14 Start: Starting location, facing east
15 Start: Starting location, facing south
16 Start: Starting location, facing west
17 Objec: Puddle of water
18 Objec: Green barrel
19 Objec: Table and two chairs
1a Objec: Floor lamp
1b Objec: Chandelier
1c Objec: Skeleton handing from hook in ceiling
1d Food: Bowl of dog food
1e Objec: Stone pillar
1f Objec: Potted tree
20 Objec: Skeleton lying on ground
21 Objec: Sink
22 Objec: Potted plant
23 Objec: Blue vase
24 Objec: Round table
25 Objec: Ceiling light
26 Objec: 5 Pots and pans hanging from wood beam attached to ceiling
27 Objec: Suit of armour
28 Objec: Hanging cage
29 Objec: Hanging cage with skeleton inside
2a Objec: Pile of bones
2b GKey: Gold key
2c SKey: Silver key
2d Objec: Cot
2e Objec: Bucket
2f Food: Plate of food
30 Aid: First aid kit
31 Ammo: Ammo
32 Weap3: Weapon 3
33 Weap4: Weapon 4
34 Treas: Jewelled cross
35 Treas: Gold chalice
36 Treas: Jewelled box
37 Treas: Crown
38 Mirro: Blue mirror with face
39 Objec: Bones and blood
3a Objec: Wood barrel
3b Objec: Stone well with water
3c Objec: Stone well no water
3d Objec: Blood
3e Objec: Flag pole and flag
3f Objec: (v1.1) Floating sign: "CALL APOGEE. SAY "SNAPPITY" (v1.11) Floating sign: "CALL APOGEE. SAY "AARDWOLF"
40 Objec: (v1.1+) Broken glass on floor (or crushed bones?)
41 Objec: (v1.1+) Broken glass on floor (or crushed bones?)
42 Objec: (v1.1+) Broken glass on floor (or crushed bones?)
43 Objec: (v1.1+) Pot, pan, and ladle hanging from ceiling
44 Objec: (v1.1+) Iron wood-burning stove
45 Objec: (v1.1+) Rack of poles (spears ?)
46 Objec: (v1.1+) Green vines hanging from ceiling
49 ?????: Unknown purpose (found in episode 1, on level 3)
5a Face: Makes enemy face east
5b Face: Makes enemy face north east
5c Face: Makes enemy face north
5d Face: Makes enemy face north west
5e Face: Makes enemy face west
5f Face: Makes enemy face south west
60 Face: Makes enemy face south
61 Face: Makes enemy face south east
62 Secre: Block is a secret passage
63 End: Ends game
6c Guard: Tan soldier (skill level 1 & 2), standing still facing east
6d Guard: Tan soldier (skill level 1 & 2), standing still facing north
6e Guard: Tan soldier (skill level 1 & 2), standing still facing west
6f Guard: Tan soldier (skill level 1 & 2), standing still facing south
70 Guard: Tan soldier (skill level 1 & 2), walking east
71 Guard: Tan soldier (skill level 1 & 2), walking north
72 Guard: Tan soldier (skill level 1 & 2), walking west
73 Guard: Tan soldier (skill level 1 & 2), walking south
74 WGuar: (Eps.2+) White guard (skill level 1 & 2), standing facing east
75 WGuar: (Eps.2+) White guard (skill level 1 & 2), standing facing north
76 WGuar: (Eps.2+) White guard (skill level 1 & 2), standing facing west
77 WGuar: (Eps.2+) White guard (skill level 1 & 2), standing facing south
7c Objec: Dead tan soldier
7e SS: Blue soldier (skill level 1 & 2), standing still, facing east
7f SS: Blue soldier (skill level 1 & 2), standing still, facing north
80 SS: Blue soldier (skill level 1 & 2), standing still, facing west
81 SS: Blue soldier (skill level 1 & 2), standing still, facing south
82 SS: Blue soldier (skill level 1 & 2), walking east
83 SS: Blue soldier (skill level 1 & 2), walking north
84 SS: Blue soldier (skill level 1 & 2), walking west
85 SS: Blue soldier (skill level 1 & 2), walking south
8a Dog: Dog (skill level 1 & 2), running east
8b Dog: Dog (skill level 1 & 2), running north
8c Dog: Dog (skill level 1 & 2), running west
8d Dog: Dog (skill level 1 & 2), running south
90 Guar3: Tan soldier (skill level 3), standing still, facing east
91 Guar3: Tan soldier (skill level 3), standing still, facing north
92 Guar3: Tan soldier (skill level 3), standing still, facing west
93 Guar3: Tan soldier (skill level 3), standing still, facing south
94 Guar3: Tan soldier (skill level 3), walking east
95 Guar3: Tan soldier (skill level 3), walking north
96 Guar3: Tan soldier (skill level 3), walking west
97 Guar3: Tan soldier (skill level 3), walking south
98 WGua3: (Eps.2+) White guard (skill level 3), standing, facing east
99 WGua3: (Eps.2+) White guard (skill level 3), standing, facing north
9a WGua3: (Eps.2+) White guard (skill level 3), standing, facing west
9b WGua3: (Eps.2+) White guard (skill level 3), standing, facing south
a0 Pries: (Eps.2+) Black priest
a2 SS3: Blue soldier (skill level 3), standing still, facing east
a3 SS3: Blue soldier (skill level 3), standing still, facing north
a4 SS3: Blue soldier (skill level 3), standing still, facing west
a5 SS3: Blue soldier (skill level 3), standing still, facing south
a6 SS3: Blue soldier (skill level 3), walking east
a7 SS3: Blue soldier (skill level 3), walking north
a8 SS3: Blue soldier (skill level 3), walking west
a9 SS3: Blue soldier (skill level 3), walking south
ae Dog3: Dog (skill level 3), running east
af Dog3: Dog (skill level 3), running north
b0 Dog3: Dog (skill level 3), running west
b1 Dog3: Dog (skill level 3), running south
b2 Hitlr: (Eps.2+) Hitler
b4 Guar4: Tan soldier (skill level 4), standing still, facing east
b5 Guar4: Tan soldier (skill level 4), standing still, facing north
b6 Guar4: Tan soldier (skill level 4), standing still, facing west
b7 Guar4: Tan soldier (skill level 4), standing still, facing south
b8 Guar4: Tan soldier (skill level 4), walking east
b9 Guar4: Tan soldier (skill level 4), walking north
ba Guar4: Tan soldier (skill level 4), walking west
bb Guar4: Tan soldier (skill level 4), walking south
bc WGua4: (Eps.2+) White guard (skill level 4), standing, facing east
bd WGua4: (Eps.2+) White guard (skill level 4), standing, facing north
be WGua4: (Eps.2+) White guard (skill level 4), standing, facing west
bf WGua4: (Eps.2+) White guard (skill level 4), standing, facing south
c4 Schab: (Eps.2+) Dr. Schabbs
c6 SS4: Blue soldier (skill level 4), standing still, facing east
c7 SS4: Blue soldier (skill level 4), standing still, facing north
c8 SS4: Blue soldier (skill level 4), standing still, facing west
c9 SS4: Blue soldier (skill level 4), standing still, facing south
ca SS4: Blue soldier (skill level 4), walking east
cb SS4: Blue soldier (skill level 4), walking north
cc SS4: Blue soldier (skill level 4), walking west
cd SS4: Blue soldier (skill level 4), walking south
d2 Dog4: Dog (skill level 4), running east
d3 Dog4: Dog (skill level 4), running north
d4 Dog4: Dog (skill level 4), running west
d5 Dog4: Dog (skill level 4), running south
d6 Boss: Final boss (blue) ("Hans")
d8 Zomb1: (Eps.2+) Zombie (skill level 1 & 2), standing facing east
d9 Zomb1: (Eps.2+) Zombie (skill level 1 & 2), standing facing north
da Zomb1: (Eps.2+) Zombie (skill level 1 & 2), standing facing west
db Zomb1: (Eps.2+) Zombie (skill level 1 & 2), standing facing south
ea Zomb3: (Eps.2+) Zombie (skill level 3), standing facing east
eb Zomb3: (Eps.2+) Zombie (skill level 3), standing facing north
ec Zomb3: (Eps.2+) Zombie (skill level 3), standing facing west
ed Zomb3: (Eps.2+) Zombie (skill level 3), standing facing south
fc Zomb4: (Eps.2+) Zombie (skill level 4), standing facing east
fd Zomb4: (Eps.2+) Zombie (skill level 4), standing facing north
fe Zomb4: (Eps.2+) Zombie (skill level 4), standing facing west
ff Zomb4: (Eps.2+) Zombie (skill level 4), standing facing south