Code is just text
Code ist einfach Text
Every file of the game is plain text, the same letters you type in a message. It's written in a language the computer understands, called JavaScript.
Jede Datei des Spiels ist einfacher Text, dieselben Buchstaben, die du in einer Nachricht tippst. Er ist in einer Sprache geschrieben, die der Computer versteht: JavaScript.
The computer reads it from top to bottom, and does exactly what it says. Here is the start of a real file of the game. The colours help you read it:
Der Computer liest ihn von oben nach unten und macht genau, was dasteht. Hier ist der Anfang einer echten Datei des Spiels. Die Farben helfen dir beim Lesen:
- grey: comments. Everything after
//is a note for people. The computer skips it. - grau: Kommentare. Alles nach
//ist eine Notiz für Menschen. Der Computer überspringt sie. - orange: words of the language itself, like
const,function,if,return. There are only a few dozen. - orange: Wörter der Sprache selbst, wie
const,function,if,return. Davon gibt es nur ein paar Dutzend. - blue: numbers. green: text in quotes.
- blau: Zahlen. grün: Text in Anführungszeichen.
- white: names that the game's makers made up, like
CAST_TIME. - weiß: Namen, die sich die Macher des Spiels ausgedacht haben, wie
CAST_TIME.
That is the town portal scroll: you stand still for 5 seconds to open it, then stand in it for 1 second to go through.
Das ist die Stadtportal-Schriftrolle: Du stehst 5 Sekunden still, um es zu öffnen, und dann 1 Sekunde darin, um hindurchzugehen.
Names for values
Namen für Werte
Most lines of code give a name to a value, so the rest of the game can use the name. Such a name is called a variable.
Die meisten Codezeilen geben einem Wert einen Namen, damit der Rest des Spiels den Namen benutzen kann. So ein Name heißt Variable.
constmeans: this name is fixed.Nis 16, forever. A cube is 16 cells wide.constheißt: Dieser Name steht fest.Nist 16, für immer. Ein Würfel ist 16 Zellen breit.=does not mean "is equal", like in maths. It means "put this value into this name".=heißt nicht "ist gleich" wie in Mathe. Es heißt: "Leg diesen Wert in diesen Namen."exportmeans: other files may borrow it.exportheißt: Andere Dateien dürfen ihn sich leihen.- Numbers use a dot:
2.8, not2,8. - Zahlen haben einen Punkt statt eines Kommas:
2.8, nicht2,8.
Some values change while you play. Those are made with let. The music remembers its mood like this:
Manche Werte ändern sich beim Spielen. Die macht man mit let. Die Musik merkt sich ihre Stimmung so:
Later, when a fight starts, another line puts new text into mood, like 'fight'. Text is always in quotes, so the computer knows it isn't a name.
Später, wenn ein Kampf anfängt, legt eine andere Zeile neuen Text in mood, zum Beispiel 'fight'. Text steht immer in Anführungszeichen, damit der Computer weiß, dass es kein Name ist.
2.8 in twenty places, changing it would mean finding all twenty. With a name, you change one line.2.8 stünde, müsste man zum Ändern alle zwanzig finden. Mit einem Namen änderst du eine Zeile.Things with parts: objects
Dinge mit Teilen: Objekte
A spider has many numbers: health, damage, speed, size... Instead of fifty loose names, they are packed into one object: a value with named parts, written between curly brackets { }.
Eine Spinne hat viele Zahlen: Gesundheit, Schaden, Tempo, Größe... Statt fünfzig einzelner Namen werden sie in ein Objekt gepackt: einen Wert mit benannten Teilen, zwischen geschweiften Klammern { }.
Each part is name: value, with commas in between. To read one part, write the object, a dot, and the part's name:
Jeder Teil ist Name: Wert, mit Kommas dazwischen. Um einen Teil zu lesen, schreibst du das Objekt, einen Punkt und den Namen des Teils:
SPIDER.hpis 32: its health.SPIDER.hpist 32: ihre Gesundheit.SPIDER.aggrois 3: how close you can come before it notices you.SPIDER.aggroist 3: wie nah du kommen kannst, bevor sie dich bemerkt.SPIDER.labelis'Spider': the name you see in the game.SPIDER.labelist'Spider': der Name, den du im Spiel siehst.
Object.freeze(...) locks the object, so nothing can change a spider's numbers by mistake while the game runs.
Object.freeze(...) sperrt das Objekt ab, damit nichts aus Versehen die Zahlen einer Spinne ändert, während das Spiel läuft.
Try it: functions are little machines
Probier es: Funktionen sind kleine Maschinen
A function is a machine with a name. You put something in, it does its work, and something comes out.
Eine Funktion ist eine Maschine mit einem Namen. Du steckst etwas hinein, sie macht ihre Arbeit, und etwas kommt heraus.
Read it like this: "maxHealth is a machine. Give it a level, and => out comes 100, plus 10 for every level above 1." (The numbers come from the object PLAYER.)
Lies es so: "maxHealth ist eine Maschine. Gib ihr ein level, und => heraus kommt 100, plus 10 für jede Stufe über 1." (Die Zahlen kommen aus dem Objekt PLAYER.)
To use a machine, write its name and put the input in round brackets: maxHealth(5). That's called calling the function.
Um eine Maschine zu benutzen, schreibst du ihren Namen und die Eingabe in runde Klammern: maxHealth(5). Das nennt man die Funktion aufrufen.
Bigger machines are written with function name(input) { ... }, and say what comes out with return. Same idea.
Größere Maschinen schreibt man mit function name(eingabe) { ... }, und sie sagen mit return, was herauskommt. Dieselbe Idee.
Lists
Listen
A list holds several values in a row, between square brackets [ ]:
Eine Liste hält mehrere Werte hintereinander, zwischen eckigen Klammern [ ]:
- To get one thing out, write its position in square brackets. But careful: counting starts at 0.
FLUIDS[0]isWATER,FLUIDS[2]isACID. - Um ein Ding herauszuholen, schreibst du seine Position in eckige Klammern. Aber Vorsicht: Gezählt wird ab 0.
FLUIDS[0]istWATER,FLUIDS[2]istACID. - A list can hold lists.
DIRSholds the four steps to a neighbouring cell:[1, 0]is one step along x. - Eine Liste kann Listen enthalten.
DIRSenthält die vier Schritte zu einer Nachbarzelle:[1, 0]ist ein Schritt entlang x. FLUIDS.lengthsays how many things are in it: 3.FLUIDS.lengthsagt, wie viele Dinge darin sind: 3.
And a list can hold objects. Here are the game's three coins:
Und eine Liste kann Objekte enthalten. Hier sind die drei Münzen des Spiels:
COINS[1].name is 'Silver': the thing at position 1, then its part called name.
COINS[1].name ist 'Silver': das Ding an Position 1, und davon der Teil namens name.
The last line runs a little machine (.map) on every coin to add a few more parts. You'll meet machines that work through lists on the loops slide.
Die letzte Zeile lässt eine kleine Maschine (.map) über jede Münze laufen, um ein paar Teile hinzuzufügen. Maschinen, die Listen abarbeiten, triffst du auf der Folie über Schleifen.
Doing maths
Rechnen
Computers are very good at maths. Code writes it almost like you do:
Computer können sehr gut rechnen. Code schreibt es fast so wie du:
a + b, a - b | plus, minus | plus, minus |
a * b | times (there is no × key) | mal (es gibt keine ×-Taste) |
a / b | divided by | geteilt durch |
a ** 2 | a squared | a hoch 2 |
a % b | the remainder: what is left over after dividing. 7 % 3 is 1. | der Rest: was beim Teilen übrig bleibt. 7 % 3 ist 1. |
Math.floor(x) | round down: Math.floor(2.9) is 2 | abrunden: Math.floor(2.9) ist 2 |
Math.min(a, b) | the smaller one (Math.max: the bigger one) | die kleinere (Math.max: die größere) |
Your bag keeps all money as copper. To show it as gold, silver and copper, this machine splits it up:
Deine Tasche hebt alles Geld als Kupfer auf. Um es als Gold, Silber und Kupfer zu zeigen, teilt diese Maschine es auf:
With 1234 copper:
Mit 1234 Kupfer:
- gold:
Math.floor(1234 / 1000)= 1 - Gold:
Math.floor(1234 / 1000)= 1 - silver:
1234 % 1000is 234,/ 10rounded down = 23 - Silber:
1234 % 1000ist 234,/ 10abgerundet = 23 - copper:
1234 % 10= 4 - Kupfer:
1234 % 10= 4
So the bag shows 1g 23s 4c. Check it with the formatCoins machine on the slide before!
Also zeigt die Tasche 1g 23s 4c. Prüf es mit der formatCoins-Maschine auf der Folie davor!
Try it: making decisions with if
Probier es: Entscheidungen mit if
Games are full of decisions: did the sword hit? Is the spider close enough? Can you pay for that? Code decides with if:
Spiele sind voller Entscheidungen: Hat das Schwert getroffen? Ist die Spinne nah genug? Kannst du das bezahlen? Code entscheidet mit if (falls):
if (question) do this;
if (Frage) mach das;
The question goes in round brackets. If the answer is yes (true), the rest runs. If not (false), it is skipped. else can say what to do otherwise.
Die Frage steht in runden Klammern. Wenn die Antwort ja ist (true), läuft der Rest. Wenn nicht (false), wird er übersprungen. Mit else kann man sagen, was sonst passieren soll.
a < b, a > b | less than, more than | kleiner als, größer als |
a <= b | less than or the same | kleiner oder gleich |
a === b | the same? (three = signs, because one = means "put") | gleich? (drei =-Zeichen, weil ein = "leg hinein" heißt) |
!a | not | nicht |
a && b, a || b | and, or | und, oder |
return leaves the function at once, with an answer. Nothing after it runs.
return verlässt die Funktion sofort, mit einer Antwort. Nichts danach läuft noch.
Doing it again: loops
Noch mal und noch mal: Schleifen
Every frame, the game has to look at every monster: is it dead? Then you get its experience and its loot. Nobody would write that out for each monster. A loop does it:
In jedem Bild muss das Spiel sich jedes Monster anschauen: Ist es tot? Dann bekommst du seine Erfahrung und seine Beute. Niemand würde das für jedes Monster einzeln hinschreiben. Eine Schleife macht das:
for (const e of list) {
const reward = awardKill(player, state.run, e);
if (reward) {
xp += reward.xp; levels += reward.levels; killed++; dropLoot(e, reward.coins);
if (e.guardianGate) unlocked = true;
}
}
Read it as: "for each monster e of the list, do what's in the curly brackets". The first time round, e is the first monster, then the second, and so on, until the list is done.
Lies es so: "Für (for) jedes Monster e aus (of) der liste, mach, was in den geschweiften Klammern steht." Beim ersten Durchlauf ist e das erste Monster, dann das zweite, und so weiter, bis die Liste fertig ist.
xp += reward.xp is short for xp = xp + reward.xp: add to what's there. killed++ adds one.
xp += reward.xp ist kurz für xp = xp + reward.xp: zu dem dazuzählen, was schon da ist. killed++ zählt eins dazu.
Sometimes you just want to count. A guardian drops several weapons:
Manchmal willst du einfach zählen. Ein Wächter lässt mehrere Waffen fallen:
for (let i = 0; i < g.count; i++) out.push(rollWeapon(R, level, { ...g, favor }));
This one has three parts, separated by ;:
Diese hat drei Teile, getrennt durch ;:
let i = 0: start counting at 0,let i = 0: bei 0 anfangen zu zählen,i < g.count: keep going whileiis less than the number of weapons,i < g.count: weitermachen, solangeikleiner ist als die Zahl der Waffen,i++: after each round, add one.i++: nach jeder Runde eins dazuzählen.
Each round, out.push(...) rolls a new weapon and adds it to the end of the list out.
In jeder Runde würfelt out.push(...) eine neue Waffe und hängt sie hinten an die Liste out an.
{ ...g, favor } is a new object with everything from g in it, and also favor: the kind of weapon you fight with, so the machine rolls more of those.
{ ...g, favor } ist ein neues Objekt mit allem aus g darin und dazu favor: die Art Waffe, mit der du kämpfst, damit die Maschine mehr davon würfelt.
Borrowing from other files
Von anderen Dateien leihen
A file can only use what it made itself, or what it borrowed. The first lines of a file usually say what it borrows:
Eine Datei kann nur benutzen, was sie selbst gemacht hat, oder was sie sich geliehen hat. Die ersten Zeilen einer Datei sagen meistens, was sie sich leiht:
import { createInventory, addInstance, addItem, equipItem, assignBar, whole } from './inventory.js';
import { OLD_SWORD, TOWN_PORTAL, HEALTH_POTION } from './items.js';
import { fixedWeapon } from './gear/generate.js';
"From the file inventory.js next to me, borrow createInventory, addItem..." The other file must have put export in front of them, or they can't be borrowed.
"Aus der Datei inventory.js neben mir leih dir createInventory, addItem..." Die andere Datei muss export davorgeschrieben haben, sonst kann man sie nicht ausleihen.
That's how 200 small files become one game. Every arrow in the picture of the parts is made of lines like these.
So werden aus 200 kleinen Dateien ein Spiel. Jeder Pfeil im Bild der Teile besteht aus solchen Zeilen.
It also helps you find your way: if you wonder where OLD_SWORD comes from, the import line tells you: items.js.
Es hilft dir auch beim Zurechtfinden: Wenn du dich fragst, wo OLD_SWORD herkommt, sagt dir die Import-Zeile: items.js.
The real code: a file you've never seen
Der echte Code: eine Datei, die du noch nie gesehen hast
This is a whole file of the game: the rules of the town portal. Here's how to read a file you don't know:
Das ist eine ganze Datei des Spiels: die Regeln des Stadtportals. So liest du eine Datei, die du nicht kennst:
- Read the comment at the top. It says what the file is for, and what it doesn't do: "no rendering, no game state".
- Lies den Kommentar oben. Er sagt, wofür die Datei da ist, und was sie nicht macht: "kein Malen, kein Spielzustand".
- Look at what it borrows. Nothing! No
importlines: it's a rule file that needs no other part. - Schau, was sie sich leiht. Nichts! Keine
import-Zeilen: eine Regeldatei, die keinen anderen Teil braucht. - Look at what it offers: everything with
export.TRIGGER_RADIUShas none, so it's only for this file. - Schau, was sie anbietet: alles mit
export.TRIGGER_RADIUShat keins, also ist es nur für diese Datei. - Pick one function, read the comment above it, then its lines.
townPadIn: "where is the open portal in this cube?nullif there is none." - Such dir eine Funktion aus, lies den Kommentar darüber, dann ihre Zeilen.
townPadIn: "Wo ist das offene Portal in diesem Würfel?null, wenn es keins gibt."
New signs: ?. means "if there is one": run?.townPortal doesn't break when there is no run. null means "nothing".
Neue Zeichen: ?. heißt "falls es eins gibt": run?.townPortal geht nicht kaputt, wenn es kein run gibt. null heißt "nichts".
You don't need to understand every line. Programmers skip a lot, too, until they need it.
Du musst nicht jede Zeile verstehen. Programmierer überspringen auch viel, bis sie es brauchen.
The real code: one sword hit, line by line
Der echte Code: ein Schwerttreffer, Zeile für Zeile
This is the rule from the sword journey. You can read all of it now:
Das ist die Regel aus der Reise des Schwerthiebs. Du kannst sie jetzt ganz lesen:
roll = Math.random(): if nobody gives aroll, use a random number between 0 and 1.roll = Math.random(): Wenn niemand einrollmitgibt, nimm eine Zufallszahl zwischen 0 und 1.if (!inSwordArc(p, enemy)) return false;: not in front of the blade? Then no hit. Stop.if (!inSwordArc(p, enemy)) return false;: Nicht vor der Klinge? Dann kein Treffer. Stopp.p.sword.hits.add(enemy): remember this monster, so one swing can't hit it twice.p.sword.hits.add(enemy): Merk dir dieses Monster, damit ein Hieb es nicht zweimal trifft.weaponHit(...): the rest is its own function, because arrows, bolts and spells use it too.p.sword.stats || SWORD: the weapon's numbers, or the Old Sword's if there are none.weaponHit(...): Der Rest ist eine eigene Funktion, weil Pfeile, Bolzen und Zauber sie auch benutzen.p.sword.stats || SWORD: die Zahlen der Waffe, oder die des Alten Schwerts, wenn es keine gibt.stats.crit ?? 0: the sword's chance of a critical hit, or 0 if it has none (??means "if that's missing, use this").stats.crit ?? 0: die Chance des Schwerts auf einen kritischen Treffer, oder 0, wenn es keine hat (??heißt "wenn das fehlt, nimm das").crit ? A : B: a shortif. Ifcritis true, use A (multiply by 1.5 or more: bigger damage), otherwise B (multiply by 1: the same).crit ? A : B: ein kurzesif. Wenncritwahr ist, nimm A (mal 1,5 oder mehr: mehr Schaden), sonst B (mal 1: gleich viel).Math.max(1, Math.round(...)): round to a whole number, but never less than 1. (shareis 1 for a full hit; a spell's burst only does half to the monsters around it.)Math.max(1, Math.round(...)): auf eine ganze Zahl runden, aber nie weniger als 1. (shareist 1 bei einem vollen Treffer; die Explosion eines Zaubers macht bei den Monstern drumherum nur die Hälfte.)damageEnemy(...): another machine takes the health off.damageEnemy(...): eine andere Maschine zieht die Gesundheit ab.return { damage, crit }: the answer is a small object, so the game knows what number to show, and if it should be golden.return { damage, crit }: Die Antwort ist ein kleines Objekt, damit das Spiel weiß, welche Zahl es zeigen soll und ob sie golden sein soll.
A few signs that unlock all code
Ein paar Zeichen, die jeden Code aufschließen
Almost every line of the game is made of these. Once you know them, you can read the code in every room of this guide:
Fast jede Zeile des Spiels besteht daraus. Wenn du sie kennst, kannst du den Code in jedem Raum dieses Führers lesen:
| YOU SEE | DU SIEHST | IT MEANS | ES HEISST |
|---|---|---|---|
// ... | a note for people | eine Notiz für Menschen | |
const name = value | a name for a value (let: one that can change) | ein Name für einen Wert (let: einer, der sich ändern kann) | |
{ hp: 32, damage: 15 } | an object: a thing with named parts; read one with a dot: SPIDER.hp | ein Objekt: ein Ding mit benannten Teilen; lies einen mit einem Punkt: SPIDER.hp | |
[1, 2, 3] | a list; list[0] is the first thing | eine Liste; list[0] ist das erste Ding | |
x => ..., function f(x) { } | a machine; use it with f(5) | eine Maschine; benutze sie mit f(5) | |
if (...) ... else ... | a decision | eine Entscheidung | |
for (const e of list) | do it for every thing in a list | mach es für jedes Ding in einer Liste | |
import, export | borrow from another file, lend to other files | aus einer anderen Datei leihen, an andere Dateien verleihen |
Whenever a slide shows real code, try to read it before you read the explanation. You'll be surprised how much you understand.
Wenn eine Folie echten Code zeigt, versuch ihn zu lesen, bevor du die Erklärung liest. Du wirst staunen, wie viel du verstehst.