这个答案是在某种 JS 伪代码中给出的,因为我对我的面向对象的 JS 没有信心(我通常使用 TS)。
你的法师可能有一个 Character 的基类,或者什么。毕竟,每个人都有名字和健康。我省略了这一点,因为它与答案并不相关。问题在于你的法术是如何构成的。
我非常有信心命令模式是您所需要的。
Mage 有一些属性和两种施法方法。第一个决定法师是否可以施放该法术。你可以让法术有一个类别(或法术学校),或者你想检查权限。
pre-cast 和 post-cast 的方法虽然不是您问题的明确部分,但可能会出现。也许法术需要在调用它的施法方法之前检查目标是否有效。。
class Mage {
mana: number;
health: number;
name: string;
canCast(spell) {
// check if the mage knows the spell, or knows the school of magic, or whatever.
// can also check that the mage has the mana, though since this is common to every cast and doesn't vary, that can be moved into the actual cast method.
// return true/false
// this method can vary as needed
}
// should be the same for all mages.
// we call the spells pre-cast hooks before casting, for composite spells this ensures each sub-spell pre-hook is called before any of the spells
// are cast. This hook can be used to verify the spell *can* be cast (e.g. you have enough health)
cast(spell, target) {
if (spell.getCost() > mana) {
// not enough mana.
// this isn't part of canCast, because this applies to every mage, and canCast can vary.
// return or throw an error
}
console.log("Casting....");
if (!spell.preCast(this, target)) {
// maybe the target isn't valid for this spell?
// we do this before *any* spells are cast, so if one of them is not valid,
// there's nothing to "roll back" or "undo".
// either throw an error or return. either way, don't continue casting.
}
spell.cast(this, target);
spell.postCast(this, target);
this.deductMana(spell.getCost());
console.log("Done casting. Did we win?");
}
}
基础法术,没有任何功能,但充满了那种叫做“爱”的东西:
class Spell {
getName(): string;
getCost(): number;
preCast(target, caster, etc.) {}
cast(target, caster, etc.) {}
postCast(target, caster, etc.) {}
}
你的复合法术。一个类应该让你做任意数量的组合,除非你需要一些非常专业的东西。例如,结合两个火焰法术可能会放大伤害,同时降低总法力消耗。这需要一个特殊的复合咒语,SynergizingCompositeSpell 可能吗?
class CompositeSpell : Spell {
spells: Spell[];
getName {
// return the subspell names
}
getCost (
// return sum of subspell costs.
}
preCast(target, caster, etc.) {
// call each subspell's preCast
// if any return false, return false. otherwise, return true.
}
cast(target, caster, etc.) {
// call each spell's cast
}
postCast(target, caster, etc.) {
// call each spells post-cast
}
constructor(spell, spell, spell, etc). // stores the spells into the property
}
一个示例咒语:
class Drain : Spell {
getName() { return "Drain!"; }
getCost() { return 3; } // costs 3 mana
cast(target, caster, etc.) {
target.harm(1); // let the target deduct its hp
console.log(`The ${target.name} was drained for 3 hp and looks hoppin' mad.`)
}
}
使用时的样子,施法会消耗生命值,让我的牙齿有光泽和铬
var mage = ... // a mage
var target = ... // you, obviously
var spellToCast = new CompositeSpell(new Drain(), new ShinyAndChrome());
mage.cast(spellToCast, target);
CompositeSpell 构造函数可以检查它给出的咒语是否“兼容”,无论这在您的游戏中可能意味着什么。 Spells 也可以有一个canBeCastWith(spell) 方法来验证兼容性。也许将Drain 和Heal 组合在一起没有意义并且不应该被允许?或者一个咒语接受一个目标,而另一个不接受?
值得注意的是,preCast / cast / postCast 方法应该采用相同的参数,即使它们并不总是需要。您正在使用一种千篇一律的模式,因此您需要包含任何咒语可能需要的所有内容。我想该列表仅限于:
- 施法者
- 目标
- 区域(用于法术效果区域)
- 法术选项(在龙与地下城中,施法者选择将某人变形为什么)
我想指出的一件事是,不要直接对你的生命值或法力使用加法/减法(例如state.mana--),而是使用函数调用(例如state.useMana(1)。这让你的选择保持开放未来的发展。
例如,如果你的法师有一个技能会在他/她的生命值降低时触发?这个咒语不知道它应该触发什么。这就看性格了。这也可以让您覆盖该方法,这是简单的加法/减法无法做到的。
我希望这个答案会有所帮助。