Amnesia: The Dark Descent Guide

How to Make an Amnesia Custom Story/Mod for Amnesia: The Dark Descent

How to Make an Amnesia Custom Story/Mod

Overview

In this guide, you will be making an amnesia CS with the language C++. I have made many Custom Stories and i hope to make more! You will need SOME experience with how this all works. This will make you have AMAZING experience with C++. This also works with mods. Just a few tweaks, and you’re all set for a mod

Part 0.1: How to program

What the hell am i doing, and how do i make a custom story
First off, you should really grasp the language that the HPL scripting language AngelScript is based off of. That language is C++.

This stuff is pretty low level so im going to be breaking this into chunks because this is going to be alot of information.

Getting started
You will need a compiler if you would like to follow along with this custom story:
On windows the only compiler is sadly Visual Studio, whether it is the build tools or the actual IDE itself.

EDIT: USE MINGW (windows port of gcc) IT IS A GODSEND

For the Unix-Like users (OS X and Linux), you will need to use either gcc or g++ (i believe this is also called gpp).

In windows the visual studio ide will compile for you and run the program, but, you will need to define a preprocessor variable at the top of the document, it should look like this.

#define _CRT_SECURE_NO_WARNINGS

It will tell you to define it without it. Pretty much, this just tells the compiler for windows that the c-standard library is safe to use (With some exceptions, ill get to that later). This is NOT needed on unix.

Lets create a main function. The main function tells the compiler what happens when we run the executable. This can be changed, but for now lets keep it at main.

In c++ this looks like this:

int main(void){ return 0; }

This pretty much just runs a program and says it was successful. The void type as a parameter can be a little confusing to some, what void means in c++ is that it has no type and therefore has no defined memory size. It isnt needed but it is good practice to use it for portability.

Everything inside the main function will be executed.

Part 0.2: How to program

Now that you know what the basic idea of the main function is, it is time to grasp the simple task of printing to the console.

As said before, you will need to define a preprocessor variable on windows but on unix-like systems, this is natively supported.

the print method is contained inside the stdio.h header, which is short for standard input/output, and, in my honest opinion, is the cleanest print function incorporated into any language ever.

Before we can use this method we need to add this under our preprocessor definition

#include <stdio.h>

All this does is tell the compiler to add whatever text is in the file stdio.h to whatever file this is included in.

The method is named printf and is defined like so:

printf(const char* format, …);

The format argument is something not seen before in many beginner level languages. It has an unusual type. What is a const char*?

A const char* is a low level definition of a string (fancy word for character array). In fact it isnt even an array, it is a pointer. The const is short for constant which means the string has to be defined literally (the argument wont take a variable unless it is constant).

The ellipsis (…) is the way of telling a compiler it can take an infinite number of parameters. This is where the variables can go and they can be any type, as long as they are in the same order as the format.

lets look at an example of this…

printf(“%sn”, “Hello World!”);

This prints out “Hello World!” and a newline. The %s means that it will place a string in that position of the format. Ultimately, the format is a set of instructions.

There are other format types, for example:
%i is for integers
%s is for strings
%c is for characters
%f is for floating point numbers (decimals)

adding the printf function inside of our main method will print out whatever we feed it:

#include <stdio.h> int main(void){ printf(“%s %s! %in”, “Hello”, “World”, 1000); }

This will print out “Hello World! 1000”

Part 0.3: How to program

Now that the basic idea of printing to a console has been grasped, why not make things a little more challenging? File IO seems like a step in the right direction considering they are relatively the same.

Why are they the same? you might be asking… Well, printing to a file is the same as printing to a console. They are both buffers in fact, the printf method on windows defaults to printing out to a file instead of the console.

There is no additional header needed as stdio has everything needed.

to create a file buffer you need to initialize it using the method fopen which is defined like so:

fopen(const char* directory, const char* mod);

The directory parameter is the directory of the file we want to open. Keep in mind that the c standard library is defined in c not c++, so it doesnt have exception handling instead it has realtime error checking which is treading into a more complex region of this lession. Generally, avoiding this will involve giving a directory to a file not a ‘folder’.

Now, fopen returns a FILE* type which is essentially a pointer to an initialized instance of the type FILE.

To actually create and use a file, that will look something like this:

FILE* file = fopen(“C:\your_directory\textFile.txt”, “w”);

This creates a file buffer in write mode.

This is where the file io and the printf function share similarities. printing to a file uses a method called fprintf. It is the same as printf, although it has an additional parameter that takes a file pointer.

The definition looks like this:

fprintf(FILE* f, const char* format, …);

If we were to print the same thing we did in Part 0.2 into a file it would look like this:

fprintf(file, “%s %s! %in”, “Hello”, “World”, 1000);

It is the same thing, just with a file as the first parameter.

Part 0.4: Really starting to program…

Lets start method creation. Method creation in c and c++ are both the same, but they are different from other languages in the sense that the order they are defined in matters.

Let me explain that last part better:

void method1(){ } void method2(){ method1(); }

This one above works while the one below does not:

void method1(){ method2(); } void method2(){ }

The reason behind that is the fault of monolithic compiling (running through each line in a ‘linear’ motion).
Example 1 has method1() defined before method2(), therefore method2() can use method1().
Example 2 has method2() defined after method1(), therefore method1() has no idea method2() even exists.

Think of it as a group of people and they are all facing to the left. Each person can describe the person infront of them but not the person behind them.

The way of fixing this is by prototyping. Think of prototyping as a mirror so every person can see eachother in the reflection.

If we were to reformat Example 2 so that it works we simply put this at the top of the file:

void method2(); void method1(){ method2(); } void method2(){ }

We pretty much created an undefined method. Now, any method can be used within the file. Another great way to look at this from a compiler’s point of view is that the prototyped methods are telling the compiler that they exist SOMEWHERE within the file.

Prototyping is actually what headers are mainly used for. It reduces the overhead by telling the compiler that the methods exist and that they will be compiled/defined in some other file. In other words, when you include stdio.h you arent including the 4000+ line file that is just solid definitions, youre including the 400 line file that includes declarations.

So i can have two files now:

Header.h

void method1(); void method2(int arg); int method3();

And

File.cpp

#include <stdio.h> #include “Header.h” void method1(){ method2(method3()); } void method2(int arg){ printf(“%in”, arg); } int method3(){ return 8 * 2 + (4 * 2); }

It is also worth noting:
an include with “” (quotations) is going to be relative to either a special user defined location within the compiler or the relative location of the file it is written in.

an include with <> is going to be a system defined include path.

a project with user-made headers is most likely going to use quotations.

Parameter names in prototyped method declarations do not have to be the same in their definitions.

To reduce clutter even more from headers a good practice is by creating preprocessor namespaces like so:
Header.h

#ifndef HEADER #define HEADER void method1(); void method2(int arg); int method3(); #endif

The compiler may see multiple occurances of header includes but will only compile the one that comes first in line. This can drastically improve compile times.

Part 0.5: Memory management

The reason that this seciton is being taught after everything above is mainly because memory management is more complex in c and c++ than it is in other languages. The memory isnt dynamic and needs to be managed dynamically. You will also notice that strings and objects have quite a bit in common when it comes to memory allocation.

C and C++ lack what is called a garbage collector. A garbage collector is essentially a program that runs in unison with the user-written program. It constantly scans calls and the main scope and checks whether instances of objects are being used at all. If they are not, they are freed from memory.

Enough of that, lets go over how C/C++ manages memory. The C standard library by default has this as the language would be pretty useless without it. The header we need to include is stdlib.h which includes all of the general methods needed for memory and the works.

Inside of the header there are 4 specific memory functions that are used more than others. These are malloc, realloc, calloc and free as well as a native language method sizeof. Let me explain what each does:

malloc

malloc is the general memory allocation function. It takes a size_t parameter which tells malloc how many bytes are needed. When paired with sizeof it can be used to allocate memory for arrays and pointers. It returns the pointer to the newly allocated memory.

Example:

char* string = (char*)malloc(20 * sizeof(char));

realloc

Same as malloc really. The only difference is the additional paramter which is the buffer needed to be resized. It returns the reallocated memory pointer.

Example:

int* intsOld = (int*)malloc(10 * sizeof(int)); int* intsNew = (int*)realloc(intsOld, 20);

calloc

The exact same thing as malloc except it initializes all the values as full bytes (11111111). The only difference is that it takes two parameters, len and size. Essentially written like so:

void* calloc(size_t len, size_t size){ return malloc(len * size); }

Example:

char* string = (char*)calloc(20, sizeof(char));

free

free is the opposite of malloc in the sense that it “destroys” pointers in memory. General rule of thumb is that for every memory allocation in the program, a free should be used.

Example:

int* ints = (int*)malloc(5 * sizeof(int)); free(ints);

sizeof

this takes a parameter that is a type and returns the size in bytes that the type needs to store one unit of that data type.

Example:

printf(“The size of an integer is %in”, sizeof(int)); //Prints “The size of an integer is 4”

Recently, i’ve taken a fond liking of using sizeof on the dereferenced pointer (*var). The reasoning behind this is that sizeof can recognize the types of variables, for example:

int* i = (int*)malloc(3 * sizeof(*i));

This way is generally easier in the sense that incase variable i’s type ever needs to be changed, the parameter of sizeof will always change, making the programming and the program itself, much safer than before.

General note about the above

When using any memory allocation function, you will always need to cast the pointer. This is so the compiler knows how many bytes to move when indexing. For anybody that doesnt know what a cast is, it is essentially changing what type the variable being used is and looks like this:

char theChar = ‘c’; int charToInt = (int)theChar;

Memory Leaks

Memory leaks happen when a non-used pointer is lingering in memory, which can degrade performance dramatically.

An example of a memory leak is:

#include <stdlib.h> int main(void){ int* ints; while(1){ ints = (int*)malloc(10 * sizeof(int)); } return 0; }

The fix for this is simply adding a free before the memory is allocated like so:

#include <stdlib.h> int main(void){ int* ints; while(1){ free(ints); ints = (int*)malloc(10 * sizeof(int)); } return 0; }

Part 0.6: Structures and intro to OOP in C/C++

Structures
For anyone who has programmed in C# before knows that C# contains structs. Structs in C are MUCH more efficient than structs in C#, the reason behind that is structs in C are not an actual type like they are in C#, it is just a fancy pointer.

Think of structs as arrays that can consist of many different types. They can contain multiple variables and can be allocated like a normal variable. The size of a struct is essentially the sum of all the sizes of the variables inside.

Visually this looks like:

struct myStruct{ int v1; int v2; char v3; }; sizeof(myStruct) = sizeof(v1) + sizeof(v2) + sizeof(v3) = 9

You might be asking “why am i using structs instead of classes? does C++ even contain classes?” The answer to the first question is:

structs are the simplified version of classes so i thought it would be more appropriate to teach first instead of classes

and to the second question:

Yes C++ does contain classes, but there is one huge difference between structs and classes and that is methods

Methods in structures
Methods are technically not supported for use with structs. But, since void is a variable type in C/C++, they can be pointed to from within a struct.

If you remember previously, i used the term dereferencing pointer which is stepping down a level in terms of pointer reference. Dereferencing an int* makes it an int. So, if we can do that to an int, which is a variable, we can do it to a void, which is also a variable. The dereferenced version of a void* is a regular void.

By doing this:

void method1(int i, int j){ printf(“%i %i”, i, j); } void (*myMethod)(int, int); *myMethod = &method1;

We created two instances of method1, except in this case, myMethod can be any method as long as it has two parameters that are ints.

The basic formatting for this notation is:

void (*methodName)(<Insert parameters here>);

It is written almost entirely the same, except we are telling the compiler that the method variable needs dereferencing and not the type itself.

Why not put this into a struct? It is essentially the same concept and is useful for when methods need to be interchanged. The threading library (pthread.h) on unix machines utilizes this feature.

Fun fact: voids do not have sizes, therefore, using a void pointer (void*) inside of a struct is the same as using any other variable pointer (int*, float*, double*, etc). This makes structs more efficient than classes.

Anywho, here is an example:

struct myStructButWithAMethod{ void (*method1)(void); //Void as a parameter is good practice for cross-platform applications… }; void otherMethod(void){ printf(“Hello”); } myStructButWithAMethod s; s.method1 = &otherMethod; s.method1();

Rewritting the this function from other oop languages is essentially:

struct myStruct{ void (*method)(myStruct* this); //Pretend the this keyword doesnt exist in c++, it doesnt in c… int memberVariable; }; void test(myStruct* this){ this->memberVariable = 2; } myStruct s; s.method = &test(); s.method(&s);

for return methods just change void to an actual type:

int method1(int i, int j){ return i + j; } int (*myMethod)(int, int); *myMethod = &method1;

Enough of this abstraction, lets move onto some more oop…

Part 0.65: Bitfields and structs

Bitfields
Bitfields can really make a program more efficient. Bitfields are little declarations that tell a structure how many bits of a byte or bytes it should use for one variable. Writting a struct that is a nyble (4 bit number) would look like this:

struct nyble{ short int value : 4; };

This isnt the best example, and i will get to one soon, but essentially, you can allocate how many bits are used for any variable type.

The best example of this is using many booleans. C lacks a boolean type but the concept is the same. booleans are the same as ints fyi.

see this for example:

struct booleans{ unsigned int b1, b2, b3, b4, b5, b6, b7, b8; //Unsigned doubles the max value of an int but removes the ability to represent a negative number… };

Using sizeof on this struct would show that it is 32 bytes! And for only 8 1-bit values. That is crazy! What if we were to use bit fields? Would that decrease the size of the struct?

The answer is yes, it can.

Rewritting the struct as so:

struct booleans{ unsigned int b1 : 1; unsigned int b2 : 1; unsigned int b3 : 1; unsigned int b4 : 1; unsigned int b5 : 1; unsigned int b6 : 1; unsigned int b7 : 1; unsigned int b8 : 1; };

And retrieving the size would show that the struct is only 4 bytes now! How neat is that? we went from 32 bytes to 4.

Using unsigned char instead of unsigned int would decrease the size to 1.

That is pretty much the basic of bit fields. A simple concept.

THIS CAN ONLY BE USED ON REGULAR RESERVED TYPES (int, float, double, long, etc.)

Part 0.7: Classes

Classes
Classes exist in only C++ and the reasoning behind that is C lacks the native memory safety to handle classes. C++ was also designed to be more user friendly. They are almost the same as classes in other languages, such as java and C#, but there are two major differences, and those are syntax and prototyping. In C++, classes can utilize the prototyping feature similar to what methods have.

Writing a class
Prototyping a class is a very good practice. Definitions of classes are very different and they contain a new operator and a new type. Those are :: and this.

::
:: is a static referencer, using a :: like so:

class myClass; myClass::member = 20;

would set the static variable, member, to a value of 20. This is actually how class definitions are written.

Lets learn how to prototype a class before actually defining one.

Lets say we have two files, class.h and class.cpp. We would need to write the class like we would in any other language. For people who are experienced in java, the class name and the filename do not need to be the same. In fact, you can declare multiple classes in one header. Lets create a class named myClass that contains two ints, x and y and one method that returns and int to calculate distance:

class myClass{ int x, y; int distance(); };

That looks easy enough right? well, it would be unusable for anything but itself right now. Class members in c++ have a default member inheritance of private, which means only the class can access those members.

There are different types of class member inheritance that effect all sorts of parameters, but since classes and OOP are such a massive topic, im only going to focus on public and private.

Introduction: Syntax and Basic code structure

Angel script is directly influenced by C++, which is C but with a little more features, such as OOP (Object Oriented Programming).

Reserved Types

Since it is influenced by C++, i should give you the basic reserved types for AS:

and abstract* auto bool break case cast class const continue default do double else enum false final* float for from* funcdef function* get* if import in inout int interface int8 int16 int32 int64 is mixin namespace not null or out override* private protected return set* shared* super* switch this* true typedef uint uint8 uint16 uint32 uint64 void while xor

You will most likely only use:

int float double bool void true false if (or switch if youre that guy) for while

special types such as:

and or xor not

will be use by:

&& (and) || (or) ((x || y) && !(x && y)) (xor) !(boolean expression) (not)

Methods

There are 2 types of methods, Void and Return

Void methods are used to represent logical functions that do not return a value, hense the name Void.

Void methods look like this:

void function(){ spooky_monster_spawn(); }

Return methods are used for math and logic. while a void method can do this as well, this eliminates the use of a variable needing to be defined for the value.

Return methods look like this:

//Where T is int, float, double, bool, or uintX T math(T a, T b){ return a + b; }

You might have noticed i put in a dynamic type (T). This is not supported by angelscript i am using it as an identifier for a type.

The difference between the two can be shown below:

int number = 0; //set number to i; void mathVoid(int i){ number = i; } //For future use, i can just do number = mathReturn(2); and number will equal 2. It can also be used with a variable other than number. int mathReturn(int i){ return i; }

LOOPS COMING NEXT

Part 1: Setting up the Custom Story

FIRST AND MOST IMPORTANT! Make you CS folder, i’ve named mine Black Plauge.

Now open a text editor, i use Text Wrangler for mac, windows users should use notepad ++, it keeps your coding files organized. Linux users can use whatever, im not sure what they use.

Take a look in the guts of the amnesia file folder. All you’ll need is a few folders. DON’T COPY THEM! you can use them in your CS without them being in the CS folder.

Now you’ll need to make a few folders (for custom monsters, sounds, walls, floors, etc.)inside the CS folder.

These are the folders you’ll need:

  • A folder named “maps”
  • A folder named “entities”
  • A folder named “sounds”
  • A folder named “Textures”
  • A folder named “Music”

Make a file in your text editor called custom_story_settings.cfg
Inside, put the following code:

<Main ImgFile = “customstory.png” Name = “Black Plauge” Author = “Cr1mby” MapsFolder = “maps/” StartMap = “00_house.map” StartPos = “PlayerStartArea_1” />

Just change your start map to whatever your first map is, the author to your name, and the CS name.

Next, we will choose a picture for the start menu.

You’ll need a certain program that allows picture editing, i use photoshop, GIMP is an alternative aswell. Once you saved it, make the size, 588 x 455

This is mine:

You’re all done setting up the roots for the startup and info! Next is the .lang, or, language file.

The .lang file, or language file.

You know how when you pick up a lantern in-game, it says “picked up lantern”? Well that’s the lang file being shown as a message. The description of your CS is also the lang file. Open a text editor and copy this code:

<LANGUAGE> <CATEGORY Name=”CustomStoryMain”> <Entry Name=”Description”>Description of Custom Story.</Entry> </CATEGORY> <CATEGORY Name=”Journal”> ///////////////////////////////////////////////////////////////////////////////// //————————————Notes————————————// ///////////////////////////////////////////////////////////////////////////////// <Entry Name=”Note_NoteStranger_Name”>Daryl’s Dream Log (1/4)</Entry> <Entry Name=”Note_NoteStranger_Text”>Night one…[br][br]My dreams of this man have grown to my fullest fear, his clothing, his figure, his voices. The voices arent even voices, there moans of despair. My friend Halei, the phsycyotrist, has much experience with dreams. Maybe i should look for him.</Entry> ///////////////////////////////////////////////////////////////////////////////// //———————————–Diaries———————————–// ///////////////////////////////////////////////////////////////////////////////// <Entry Name=”Diary_diary1_Name”>End of the World</Entry> <Entry Name=”Diary_diary1_Text”>21 December, 2012[br][br][br]The ground is literally shaking beneath my feat.</Entry> ///////////////////////////////////////////////////////////////////////////////// //———————————–Mementos———————————-// ///////////////////////////////////////////////////////////////////////////////// <Entry Name=”Note_book1_Text”>July 4, 1421,[br][br]Final House Assessment:[br]House width: 80000 x 80000 ft[br]House height: 5 Stories[br]House DOC: May 6, 1418[br]House Owner: Mr. Cylis Johann Halei[br][br]House Price/month: $1800[br][br]ADDITIONAL SERVICES:[br]Maid[x][br]Bard[x][br]40 Guards[x][br][br]GRAND TOTAL/MONTH:[br]$600,000</Entry> <Entry Name=”Note_book1_Name”>Cylis Halei’s House Ownership Log</Entry> <Entry Name=”Note_rat_Name”>Rations</Entry> <Entry Name=”Note_rat_Text”>As of August 17, 1478[br][br]Total Amounts[br][br][br]Food[br][br]3 Crates[br][br][br]Weapons[br][br]1 Crate[br][br][br]Other[br][br]4 Crates</Entry> <Entry Name=”Note_lever_Name”>Lever Instructions</Entry> <Entry Name=”Note_lever_Text”>This lever is for the large iron door outside this room. The door was designed to withstand the infectious disease called “Black Plague”.[br][br]PROCEED WITH CAUTION!</Entry> </CATEGORY> ///////////////////////////////////////////////////////////////////////////////// //—————————-Item Names/Descriptions————————–// ///////////////////////////////////////////////////////////////////////////////// <CATEGORY Name=”Inventory”> <Entry Name=”ItemName_key_study_1″>Mysterious Key</Entry> <Entry Name=”ItemDesc_key_study_1″>Unknown key Tagged “Memories”</Entry> <Entry Name=”ItemName_key_tower_1″>Hall Key</Entry> <Entry Name=”ItemDesc_key_tower_1″>Key to open the Hall</Entry> <Entry Name=”ItemName_key_jar_1_item”>Gliserous Entrance Key</Entry> <Entry Name=”ItemDesc_key_jar_1_item”>Key to open the Glicerous Grand Hall</Entry> <Entry Name=”ItemName_key_gate_1″>Hall Gate Key</Entry> <Entry Name=”ItemDesc_key_gate_1″>Key Tagged Gate</Entry> <Entry Name=”ItemName_key_level_1″>Bedroom Hall Key</Entry> <Entry Name=”ItemDesc_key_level_1″>Key Tagged Bedrooms</Entry> </CATEGORY> ///////////////////////////////////////////////////////////////////////////////// //————————————Signs————————————// ///////////////////////////////////////////////////////////////////////////////// <CATEGORY Name=”Thoughts”> <Entry Name=”Entry1″>The book has a few torn pages… I only took important ones.</Entry> </CATEGORY> ///////////////////////////////////////////////////////////////////////////////// //————————————Levels———————————–// ///////////////////////////////////////////////////////////////////////////////// <CATEGORY Name=”Levels”> <Entry Name=”Entry1″>Flight of Glicerous</Entry> <Entry Name=”Entry2″>Cellar</Entry> <Entry Name=”Entry3″>Entrance Hall</Entry> <Entry Name=”Entry4″>Bedroom Hall</Entry> </CATEGORY> <CATEGORY Name=”Death”> <Entry Name=”Dream”>Don’t Play With the Fire!</Entry> </CATEGORY> <CATEGORY Name=”chapters”> <Entry Name=”dream”>Introduction</Entry> <Entry Name=”epo”>Chapter 1:[br]The House</Entry> <Entry Name=”outside”>Chapter 2:[br]The Field</Entry> <Entry Name=”wilhelm”>Chapter 3:[br]The Recovery</Entry> </CATEGORY>///////////////////////////////////////////////////////////////////////////////// //———————————-Death Hints——————————–// ///////////////////////////////////////////////////////////////////////////////// <CATEGORY Name=”DeathHint”> <Entry Name=”Entry1″>You have to carry on…</Entry> </CATEGORY> <CATEGORY Name=”LoadingText”> <Entry Name=”Ch01_Wake”>The man saw his wife laying dead, next to him. He children screamed as they saw their mother’s throat, slit open, freshly too. The blood was still flowing, a voice called out, MURDERER!</Entry> <Entry Name=”Ch02_House”>When we die, we ascend to heaven according to the bible. For me, i get my grave dug 6 feet under, and land in Satan’s pot[br][br]-Cylis Halei</Entry> </CATEGORY> <CATEGORY Name=”Locked”> <Entry Name=”locked”>The door is locked</Entry> <Entry Name=”barred”>The door is barred</Entry> <Entry Name=”challenge”>The door is locked with no key-hole</Entry> <Entry Name=”broken”>The door has no handle or its deadbolt is broken</Entry> <Entry Name=”shelf”>There is a faint light coming from the door</Entry> </CATEGORY> <CATEGORY Name=”Sign”> <Entry Name=”1″>Door Machine</Entry> </CATEGORY> </LANGUAGE>

feel free to play around with it.

See that category at the top?

It looks like this:

<CATEGORY Name=”CustomStoryMain”> <Entry Name=”Description”>Description of Custom Story.</Entry> </CATEGORY>

That’s the decription! Change that, and you’ll get this:

Just keep the Category names the same, some items need certain categories. Change the item names to the name of the items. Ex: Study Room Key would be named key_study_1

Map Editing

To make a map, you’ll need the Editor Suite[wiki.frictionalgames.com]

Install it.

Now, on the start up screen, you’ll see the editor suite tab. Click that and click ‘Level Editor’

This should pop up:

See the perspective box? Hover your mouse over that, and press the space bar

Now, the tabs in ORDER!:
Select
Lights
Billboards
Particle Systems
Sounds
Static Objects
Entities
Areas
Primitives
Decals
Fog
Combine

To make a simple room, open the static objects tab, and choose your category of choice

Make a square box with the walls, and use welders to connect them and make it look nice and pretty.

Enitities are AWESOME! They consist of keys, objects, ladders, and monsters.
They can all be enabled, so that they cannot be seen (Used with poofers).

Use primitives for floors and ceilings.

Areas consist of many types. Here they all are.

Script: Trigger’s a script when the player looks at it, collides with it or interacts with it.

PosNode: This is used in combination with Rope areas, to show where the rope ends.

Flashback: Trigger’s a flashback sequence.

Sign: Displays text when the user looks at it.

SlimeDamage: A damage area for the red slime (The Shadow).

Examine: This gives an area a description when it is examined. Also allows for different

descriptions depending on the player’s sanity.

Insanity: It can trigger sanity events repeatedly depending on your current sanity level (Note: this
area doesn’t have any callback functions, so if you need to do a complex sanity event use the script area). These sanity events include bugs crawling the screen, flies flying around, and insanity sounds. Check “redist > misc > main_sanity_events.cfg” for more information.

Ladder: This area defines where a ladder is (Note: This is not the actual ladder players will see in the game, but just the area that lets them climb. Make sure you add a ladder model or the players will be climbing on air).

Liquid: This area defines where water is (Note: Just like the ladder this doesn’t have the actual water effect associated with it).

Sticky: A sticky area is used to make entities “stick”. When an entity is in contact with the area, it will automatically snap to the center of the area. There are several options for tweaking the behavior.

Rope: Create’s a rope. For more information on what this does see How to Make a Rope.

PathNode: Guide’s enemy monsters around the map.

PlayerStart: Tell’s the game where to start the player (maps uses PlayerStartArea_1 as the default starting place).

Now, once you’ve created your basic map, it’s time for the script!

Scripting

There are many tabs within scripting called Engine Scripts[wiki.frictionalgames.com]

Start off in your text editor with this code:

void OnStart() { }

Save it as (mapname).hps

This declares what will happen on the first time we start a level in a game. If Start is pressed for a new game, then this starts all over again.

This is good for playing music, sounds, and intros!

Intros sounds cool doesn’t it?

To make an introduction. We will need to use timers.

In the void OnStart() we will put the code:

AddTimer(“T1”, 2.0f, “Intro”); AddTimer(“T2”, 4.0f, “Intro”); AddTimer(“T3”, 6.0f, “Intro”); AddTimer(“T4”, 8.0f, “Intro”); AddTimer(“T5”, 10.0f, “Intro”); SetPlayerCrouching(true); SetPlayerActive(false);

YES! you can control the player!

Repeat this as many times as you’d like, but change T1 to T2, ect. List it. and make the 2.0f bigger each time.

Make 2 areas randomly around the player’s 360 FOV

Next, we will need a function!

Copy this code:

void Intro(string &in asTimer) { string x = asTimer; if (x == “T1”) { PlaySoundAtEntity(“”, “react_sigh.snt”, “Player”, 0, false); FadeOut(3); } else if (x == “T2”) { FadeIn(3); PlaySoundAtEntity(“”, “react_breath.snt”, “Player”, 0, false); StartPlayerLookAt(“ScriptArea_1”, 2, 2, “”); } else if (x == “T3”) { StopPlayerLookAt(); StartPlayerLookAt(“ScriptArea_2”, 2, 2, “”); } else if (x == “T4”) { PlaySoundAtEntity(“”, “react_breath_slow.snt”, “Player”, 0, false); StopPlayerLookAt(); } else if (x == “T5”) { SetPlayerCrouching(false); SetPlayerActive(true); SetMessage(“chapters”, “epo”, 0); } }

What will happen:

In 2 seconds, the player will sigh, and blink.
In 4 seconds, the player will breath and look at the target
In 6 seconds, the player will look at another target.
In 8 seconds, breath
In 10 seconds, the player will stand up and be able to control, and a message will pop up.

Here’s a video on making an intro, and using the texture editor:

Enemy Patrol Nodes

To make an enemy walk, we will need patrol nodes.

To make patrol nodes, we will need to add ANOTHER code to void onstart, or, the function called when an entity is activated.

The code is:

AddEnemyPatrolNode(string& asName, string& asNodeName, float afWaitTime, string& asAnimation);

asName – internal name of the enemy
asNodeName – path node
afWaitTime – time in seconds that the enemy waits at the path node before continuing
asAnimation – the animation the enemy uses when reaching the path node

NOTE: YOU DON’T NEED TO ADD ALL PATROL NODES! ONLY WHEN THERE IS A TURN IN THE PATH, OR THE ENDING!

DISREGARD THIS IF THE ENEMY SHOULD GO BACK AND FORTH, OR A LOOP!
At the end of the path node, make an area the size of a door frame, find the internal name of it, and add this function code:

void OnStart() { AddEntityCollideCallback(“MONSTER NAME”, “SCRIPTAREA NAME”, “MonsterNodeEnd”, true, 1); } void MonsterNodeEnd(string &in asParent, string &in asChild, int alState) { SetEntityActive(“MONSTER NAME”, false); }

For a loop in path nodes:

Instead of:

void OnStart() { AddEntityCollideCallback(“MONSTER NAME”, “SCRIPTAREA NAME”, “MonsterNodeEnd”, true, 1); }

we’ll put:

void OnStart() { AddEnemyPatrolNode(string& asName, string& asNodeName, float afWaitTime, string& asAnimation); AddEnemyPatrolNode(string& asName, string& asNodeName, float afWaitTime, string& asAnimation); AddEnemyPatrolNode(string& asName, string& asNodeName, float afWaitTime, string& asAnimation); AddEnemyPatrolNode(string& asName, string& asNodeName, float afWaitTime, string& asAnimation); }

This would be a square loop where it would go around 4 corners continuously.

Originally posted by Yourcomputer:

It is possible to make the monster to follow a set of patrol nodes and never stop repeating them until the monster is disabled manually by SetEntityActive or until the player is out of the monster’s activation distance. By default, monsters will automatically loop through the path nodes assigned to them, but there are many factors that would cause them to auto-disable before then. Before the release of Justine, people had relied on continuously adding path nodes to the monster while having increased the monster’s activation distance to a large amount, therefore the monster never reaches the final path node in its memory and so never given a chance to auto-disable and the player is always within its activation distance. Since the release of Justine, we’ve had the option to disable “auto-disable on final path node” for the monster. By default, the enemy suitors Basile and Malo have auto-disable disabled for them. In order to do the same for other monsters, you need to manually edit the ENT file for the monster.
Open the monster ENT in a plain text editor and add the following line under the UserDefinedVariables element:

<Var Name=”AutoRemoveAtPathEnd” Value=”false” />

For more information on tools, and stuff, go here.

Frictional Game Wiki[wiki.frictionalgames.com]

Scares and Other cool scripts

Scares in Amnesia are nothing too expert. Just a simple script and sometimes a PS (Particle System).

NOTE: These are all the ones I know

For a door slam.
Go into your map.hps, and on the void OnStart() put:

Place an area

AddEntityCollideCallback(“Player”, “script_slam”, “func_slam”, true, 1);

Place an Area down that covers either a whole narrow passage way (Width=long Length=short) or infront of a door frame, and name it script_slam. Next we’ll need the function for the door to slam shut

Get the name internally of your door, replace door_1 with it, and put this script in map.hps:

This is what you’re probably expecting:

void func_slam(string &in asParent, string &in asChild, int alState) { SetSwingDoorClosed(“door1”, true, true); }

But that only makes the door slam with no motion, it’s first at a 90 degree angle, then it’s at a 0 degree angle, with none in between.

So, we will put instead:

void func_slam(string &in asParent, string &in asChild, int alState) { SetMoveObjectStateExt(“door1”, 0, 5, 10, 10, false); PlaySoundAtEntity(“”, “react_breath_slow.snt”, “Player”, 0, false); PlaySoundAtEntity(“”, “react_scare”, “Player”, 0, false); PlaySoundAtEntity(“”, “close_door.snt”, “Player”, 0, false); GiveSanityDamage(5.0f, true); }

Uploading your Custom Story

Now, once you’ve finished you’ll want to post this on the interwebs so others can play it. There is no official Amnesia CS database for custom stories so you’ll have to put it on ModDB. Take note; ModDB is for mods. Just putting a suggestion.

Alternatives

An Easier way to do this is through DropBox, and if you can, make a youtube account for amnesia CSes, and make little videos on them with links in the description. Also, use Bit.ly. You dont want people getting your other stuff.

Another way is to make a website or use a website. You can use Dreamweaver by Adobe for the HTML, OR you can use Google Drive. In conclusion, i’d use an actual website, because inorder to host a website, you need a super powerful computer with over 64GB RAM.

Table of Links*
Website
Link
Bit.ly
ModDB Amnesia Addons
DropBox

* All of these websites REQUIRE an account to upload.

Other tutorials

I have been working on this guide for a long time now and it is kinda making me lag behind MY story. So here i give you the MOST HELPFUL tutorials on the internet

I present you,

In response to grunt torso gaming

Your code, based on method calls, for the most part is ok, i think you just misread the use of parameters.

AddEnemyPatrolNode(string& asName, string& asNodeName, float afWaitTime, string& walk.anim);

string& asName

This parameter is the name of the entity inside the HPL level editor.

Example, if named Monster1 this parameter should be “Monster1” (With quotations).

string& asNodeName

This parameter is the name of the node within the HPL level editor. Do you see a pattern?

Example, if named Node1 this parameter should be “Node1” (With quotations).

These nodes should be added in order, as you are giving the engine a user-defined pathfinding route.

ALSO wherever you see string& in a parameter, use quotations (“”), because a string is a string of characters.

float afWaitTime

Float is short for floating decimal, this parameter tells the engine how long the monster should wait before moving to the next node. You do need to put a lowercase “f” at the end of the number to indicate that it is a float. “sec” is not needed.

string& asAnimation

Animation the engine should play. you got this one right, but, it wasnt in quotations.

Final thoughts

I sadly cannot help you with this project as i would need access to the whole project. Watch some tutorials on the subject because you do need some prior knowledge in programming to work with this kind of stuff (Which is said in the overview).

The crashes are most likely happening because the engine has no clue what to do with your jumbled code.

Examples:
void OnStart AddEntityCollideCallback(“grunt”, “areacollide”, “node_5”, true, 1); (
this made my skin crawl when i saw this. Not only because it is poorly structured, just by how your first line of code is diabolical.

You start by doing void OnStart AddEntityCollideCallback which i assume is supposed to be the method AddEntityCollideCallback inside of the method OnStart

The last item i noticed was the opening parenthesis at the end. If you are trying to declare a method body, use the curly bracket ( { ) instead.

SetPlayerActive(false);){ }
Here is where that parenthesis ends, with a method body afterwards with no declaring method.

AddEnemyPatrolNode(string& asName, string& asNodeName, float 3.5, string& walk.anim);
AddEnemyPatrolNode(string& asName, string& asNodeName, float 3.5, string& walk.anim);
AddEnemyPatrolNode(string& asName, string& asNodeName, float 3.5, string& walk.anim);
AddEnemyPatrolNode(string& asName, string& asNodeName, float 3.5, string& walk.anim);

These are outside of a method, and are in what is called a field. Where methods and variables are declared. Fields are like global variables that are limited to one file. I’ll get into this another time.

Last note on this error is that you just have the declaring parameters where they should be logic types (int, float, bool, string, object, etc).

Please read more on basic scripting and strengthen your skills. I dont want to walk someone through this when they have the internet as a whole to look through.

Code reformatting
void OnStart() { AddEntityCollideCallback(“grunt”, “areacollide”, “node_5”, true, 1); //Not needed if path route is a loop AddTimer(“T1”, 2.0f, “Intro”); AddTimer(“T2”, 4.0f, “Intro”); AddTimer(“T3”, 6.0f, “Intro”); AddTimer(“T4”, 8.0f, “Intro”); AddTimer(“T5”, 10.0f, “Intro”); SetPlayerCrouching(true); SetPlayerActive(false); //AddEnemyPatrolNode(“grunt”, “node1”, 3.5f, “walk.anim”); //Disabled this based on how MonsterNodeEnd disables grunt } void MonsterNodeEnd(string &in player, string &in grunt, int alState) { SetEntityActive(“grunt”, false); }

This should fix the crashes, but they are still prone to happen.

SteamSolo.com