Refactoring
An dieser Stelle sollte dein Code wie folgt aussehen:
function valyrianSteelSwordAttack(unit) {
return {
action: true,
description:
'Attack a unit in the given direction (forward by default) with your Valyrian steel sword, dealing 5 HP of damage.',
perform(direction = 'forward') {
const receiver = unit.getSpaceAt(direction).getUnit();
if (receiver) {
unit.log(`attacks ${direction} and hits ${receiver}`);
unit.damage(receiver, 5);
} else {
unit.log(`attacks ${direction} and hits nothing`);
}
},
};
}
function iceCrystalSwordAttack(unit) {
return {
action: true,
description:
'Attack a unit in the given direction (forward by default) with your ice blade, dealing 3 HP of damage.',
perform(direction = 'forward') {
const receiver = unit.getSpaceAt(direction).getUnit();
if (receiver) {
unit.log(`attacks ${direction} and hits ${receiver}`);
unit.damage(receiver, 3);
} else {
unit.log(`attacks ${direction} and hits nothing`);
}
},
};
}
function feel(unit) {
return {
description:
'Return the adjacent space in the given direction (forward by default).',
perform(direction = 'forward') {
return unit.getSensedSpaceAt(direction);
},
};
}
function walk(unit) {
return {
action: true,
description: 'Move one space in the given direction (forward by default).',
perform(direction = 'forward') {
const space = unit.getSpaceAt(direction);
if (space.isEmpty()) {
unit.move(direction);
unit.log(`walks ${direction}`);
} else {
unit.log(`walks ${direction} and bumps into ${space}`);
}
},
};
}
const WhiteWalker = {
name: 'White Walker',
character: 'w',
maxHealth: 12,
abilities: {
attack: iceCrystalSwordAttack,
feel: feel,
},
playTurn(whiteWalker) {
const enemyDirection = ['forward', 'right', 'backward', 'left'].find(
direction => {
const unit = whiteWalker.feel(direction).getUnit();
return unit && unit.isEnemy();
},
);
if (enemyDirection) {
whiteWalker.attack(enemyDirection);
}
},
};
const Level1 = {
description:
"You've entered the ancient castle of Eastwatch to escape from a blizzard. But it's deadly cold inside too.",
tip:
"Call `warrior.walk()` to walk forward in the Player's `playTurn` method.",
timeBonus: 15,
aceScore: 10,
floor: {
size: {
width: 8,
height: 1,
},
stairs: {
x: 7,
y: 0,
},
warrior: {
character: '@',
maxHealth: 20,
position: {
x: 0,
y: 0,
facing: 'east',
},
abilities: {
walk: walk,
},
},
},
};
const Level2 = {
description:
'The cold became more intense. In the distance, you see a pair of deep and blue eyes, a blue that burns like ice.',
tip:
"Use `warrior.feel().isEmpty()` to see if there's anything in front of you, and `warrior.attack()` to fight it. Remember, you can only do one action per turn.",
clue:
'Add an if/else condition using `warrior.feel().isEmpty()` to decide whether to attack or walk.',
timeBonus: 20,
aceScore: 26,
floor: {
size: {
width: 8,
height: 1,
},
stairs: {
x: 7,
y: 0,
},
warrior: {
character: '@',
maxHealth: 20,
position: {
x: 0,
y: 0,
facing: 'east',
},
abilities: {
attack: valyrianSteelSwordAttack,
feel: feel,
},
},
units: [
{
...WhiteWalker,
position: {
x: 4,
y: 0,
facing: 'west',
},
},
],
},
};
module.exports = {
name: 'Game of Thrones',
description:
'There is only one war that matters: the Great War. And it is here.',
levels: [Level1, Level2],
};
Wie bei der Definierung des Weißen Wanderers können wir die gemeinsamen Felder des Kämpfers in ein Objekt auslagern und dann Spread-Properties verwenden, um sie jedem Level hinzuzufügen:
const Warrior = {
character: '@',
maxHealth: 20,
};
const Level1 = {
description:
"You've entered the ancient castle of Eastwatch to escape from a blizzard. But it's deadly cold inside too.",
tip:
"Call `warrior.walk()` to walk forward in the Player's `playTurn` method.",
timeBonus: 15,
aceScore: 10,
floor: {
size: {
width: 8,
height: 1,
},
stairs: {
x: 7,
y: 0,
},
warrior: {
...Warrior,
abilities: {
walk: walk,
},
position: {
x: 0,
y: 0,
facing: 'east',
},
},
},
};
const Level2 = {
description:
'The cold became more intense. In the distance, you see a pair of deep and blue eyes, a blue that burns like ice.',
tip:
"Use `warrior.feel().isEmpty()` to see if there's anything in front of you, and `warrior.attack()` to fight it. Remember, you can only do one action per turn.",
clue:
'Add an if/else condition using `warrior.feel().isEmpty()` to decide whether to attack or walk.',
timeBonus: 20,
aceScore: 26,
floor: {
size: {
width: 8,
height: 1,
},
stairs: {
x: 7,
y: 0,
},
warrior: {
...Warrior,
abilities: {
attack: valyrianSteelSwordAttack,
feel: feel,
},
position: {
x: 0,
y: 0,
facing: 'east',
},
},
units: [
{
...WhiteWalker,
position: {
x: 4,
y: 0,
facing: 'west',
},
},
],
},
};
In Bezug auf die Fähigkeiten können wir feststellen, dass beide Angriff-Fähigkeiten sehr ähnlich sind. Da können wir etwas gegen tun:
function attackCreator({ power, weapon }) {
return unit => ({
action: true,
description: `Attack a unit in the given direction (forward by default) with your ${weapon}, dealing ${power} HP of damage.`,
perform(direction = 'forward') {
const receiver = unit.getSpaceAt(direction).getUnit();
if (receiver) {
unit.log(`attacks ${direction} and hits ${receiver}`);
unit.damage(receiver, power);
} else {
unit.log(`attacks ${direction} and hits nothing`);
}
},
});
}
const valyrianSteelSwordAttack = attackCreator({
power: 5,
weapon: 'Valyrian steel sword',
});
const iceCrystalSwordAttack = attackCreator({
power: 3,
weapon: 'ice blade',
});
Wir nennen dies das "Ability Creator"-Muster, bei dem wir eine Funktion (den Ability-Creator) definieren, die eine andere Funktion angepasst anhand der übergebenen Paramater zurückgibt (die Fähigkeit).
Um das Refactoring abzuschließen, lass uns alle Magic-Strings loswerden, die Richtungen angeben. Es gibt ein offizielles Package namens @warriorjs/geography
, das eine Reihe von Konstanten und Methoden in Bezug auf Richtungen bereit stellt. Lass uns das nutzen:
const {
EAST,
FORWARD,
RELATIVE_DIRECTIONS,
WEST,
} = require('@warriorjs/geography');
function attackCreator({ power, weapon }) {
return unit => ({
action: true,
description: `Attack a unit in the given direction (forward by default) with your ${weapon}, dealing ${power} HP of damage.`,
perform(direction = FORWARD) {
const receiver = unit.getSpaceAt(direction).getUnit();
if (receiver) {
unit.log(`attacks ${direction} and hits ${receiver}`);
unit.damage(receiver, power);
} else {
unit.log(`attacks ${direction} and hits nothing`);
}
},
});
}
const valyrianSteelSwordAttack = attackCreator({
power: 5,
weapon: 'Valyrian steel sword',
});
const iceCrystalSwordAttack = attackCreator({
power: 3,
weapon: 'ice blade',
});
function feel(unit) {
return {
description:
'Return the adjacent space in the given direction (forward by default).',
perform(direction = FORWARD) {
return unit.getSensedSpaceAt(direction);
},
};
}
function walk(unit) {
return {
action: true,
description: 'Move one space in the given direction (forward by default).',
perform(direction = FORWARD) {
const space = unit.getSpaceAt(direction);
if (space.isEmpty()) {
unit.move(direction);
unit.log(`walks ${direction}`);
} else {
unit.log(`walks ${direction} and bumps into ${space}`);
}
},
};
}
const Warrior = {
character: '@',
maxHealth: 20,
};
const WhiteWalker = {
name: 'White Walker',
character: 'w',
maxHealth: 12,
abilities: {
attack: iceCrystalSwordAttack,
feel: feel,
},
playTurn(whiteWalker) {
const enemyDirection = RELATIVE_DIRECTIONS.find(direction => {
const unit = whiteWalker.feel(direction).getUnit();
return unit && unit.isEnemy();
});
if (enemyDirection) {
whiteWalker.attack(enemyDirection);
}
},
};
const Level1 = {
description:
"You've entered the ancient castle of Eastwatch to escape from a blizzard. But it's deadly cold inside too.",
tip:
"Call `warrior.walk()` to walk forward in the Player's `playTurn` method.",
timeBonus: 15,
aceScore: 10,
floor: {
size: {
width: 8,
height: 1,
},
stairs: {
x: 7,
y: 0,
},
warrior: {
...Warrior,
abilities: {
walk: walk,
},
position: {
x: 0,
y: 0,
facing: EAST,
},
},
},
};
const Level2 = {
description:
'The cold became more intense. In the distance, you see a pair of deep and blue eyes, a blue that burns like ice.',
tip:
"Use `warrior.feel().isEmpty()` to see if there's anything in front of you, and `warrior.attack()` to fight it. Remember, you can only do one action per turn.",
clue:
'Add an if/else condition using `warrior.feel().isEmpty()` to decide whether to attack or walk.',
timeBonus: 20,
aceScore: 26,
floor: {
size: {
width: 8,
height: 1,
},
stairs: {
x: 7,
y: 0,
},
warrior: {
...Warrior,
abilities: {
attack: valyrianSteelSwordAttack,
feel: feel,
},
position: {
x: 0,
y: 0,
facing: EAST,
},
},
units: [
{
...WhiteWalker,
position: {
x: 4,
y: 0,
facing: WEST,
},
},
],
},
};
module.exports = {
name: 'Game of Thrones',
description:
'There is only one war that matters: the Great War. And it is here.',
levels: [Level1, Level2],
};
Viel besser! Lese weiter, um zu lernen deinen Turm zu testen und zu veröffentlichen, sodass auch andere Spieler ihn spielen können!