Files
wsmud/os/skill/skill.js
2026-05-26 16:41:20 +08:00

649 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*global SKILL_TYPES SKILL BASE PROPERTIES WORLD FAMILIES*/
require("../util/util.js");
SKILL = function () {
this.id = "";
this.name = "";
this.type = SKILL_TYPES.SKILL;
this.grade = 1;
this.score = 0;
}
SKILL.inherits(BASE);
SKILL.prototype.query_attack_action = function (me, target) {
if (this.attack_actions)
return this.attack_actions.random();
return "";
}
SKILL.prototype.query_dodge_action = function () {
if (!this.dodge_actions) {
return "";
}
return this.dodge_actions.random();
}
SKILL.prototype.query_parry_action = function (me, target, w2) {
var w1 = me.query_weapon();
w2 = w2 || target.query_weapon();
var act;
if (w1 && w2) {
act = this.weapon_vs_weapon_actions || this.parry_actions;
} else if (w1) {
act = this.weapon_vs_unarmed_actions || this.parry_actions;
} else if (w2) {
act = this.unarmed_vs_weapon_actions || this.parry_actions;
} else {
act = this.parry_actions;
}
if (!act) {
act = this.parry_actions = SKILL.get("parry").parry_actions;
}
if (act) {
return act.random();
}
}
SKILL.prototype.level_exp = function (lv, me) {
var grd = this.query_grade(me);
return (lv + 1) * (grd + 1) * 5;
}
SKILL.prototype.query_needexp = function (level, me) {
if (level > 100) {
var grd = this.query_grade(me);
var exp = (100 + level) * (level - 100) / 2;
return exp * (grd + 1) * 5;
} else {
return 0;
}
}
SKILL.prototype.set_default = function (type) {
WORLD.DEFAULT_SKILLS[type] = this;
}
SKILL.prototype.release_prop = function (me, lv) {
if (!lv) return;
var prop = this.query_prop(lv, me);
if (prop) {
me.change_prop(prop, false);
}
prop = this.query_enable_prop(lv, me);
if (prop) {
for (var item in prop) {
if (me.is_enable_skill(this.id, item)) {
me.change_prop(prop[item], false);
}
}
}
prop = this.query_addin_prop(me, lv);
if (prop) {
if (this.is_enable(me)) {
me.change_prop(prop, false);
}
}
}
SKILL.prototype.attach_prop = function (me, lv) {
if (!lv) return;
var prop = this.query_prop(lv, me);
if (prop) {
me.change_prop(prop, true);
}
prop = this.query_enable_prop(lv, me);
if (prop) {
for (var item in prop) {
if (me.is_enable_skill(this.id, item)) {
me.change_prop(prop[item], true);
}
}
}
prop = this.query_addin_prop(me, lv);
if (prop) {
if (this.is_enable(me)) {
me.change_prop(prop, true);
}
}
}
SKILL.prototype.query_enable_prop = function (lv) {
}
SKILL.prototype.query_prop = function (lv) {
}
SKILL.prototype.query_grade = function (me) {
var sk = me.skills[this.id];
var lv = this.grade;
if (sk) {
if (sk.addin)
lv += sk.addin.length;
if (sk.ref)
lv += 1;
}
return lv;
}
SKILL.prototype.query_color_name = function (me) {
var desc = level_color[this.query_grade(me)];
return "<" + desc + ">" + this.name + "</" + desc + ">";
}
SKILL.prototype.query_addin_prop = function (me, lv) {
var sk = me.skills[this.id];
if (sk.addin && sk.addin.length) {
var prop = {};
var grd = this.grade + sk.addin.length;
for (let slot of sk.addin) {
let item = this.query_slot(slot);
if (!item) continue;
if (item.prop) {
prop[item.prop] = (prop[item.prop] ?? 0)
+ parseInt(item.value(lv, grd));
}
}
return prop;
}
return null;
}
SKILL.prototype.is_enable = function (me) {
if (this.type !== SKILL_TYPES.SKILL) return true;
var skill = me.skills[this.id];
for (var i = 0; i < this.can_enables.length; i++) {
if (skill[this.can_enables[i]]) return true;
}
return false;
}
SKILL.prototype.is_enable2 = function (me, baseskill) {
var skill = me.skills[this.id];
return skill ? skill[baseskill] : false;
}
//激活技能,附加装备的部分属性
SKILL.prototype.enable = function (me, type) {
if (!this.can_enables || !this.can_enables.contain(type)) return false;
if (this.on_enable && this.on_enable(me, type) === false) return false;
var lv = me.query_skill(this.id);
var prop = this.query_enable_prop(lv);
if (prop) {
var enable_prop = prop[type];
if (enable_prop) {
me.change_prop(enable_prop, true);
}
}
//附加进阶属性
prop = this.query_addin_prop(me, lv);
if (prop) {
if (!this.is_enable(me)) {
me.change_prop(prop, true);
}
}
return true;
}
//取消激活技能,解除装备的部分属性
SKILL.prototype.disenable = function (me, type) {
this.on_disenable && this.on_disenable(me, type);
var lv = me.query_skill(this.id);
var prop = this.query_enable_prop(lv);
if (prop) {
var enable_prop = prop[type];
if (enable_prop) {
me.change_prop(enable_prop, false);
}
}
//附加进阶属性
prop = this.query_addin_prop(me, lv);
if (prop) {
if (!this.is_enable(me)) {
me.change_prop(prop, false);
}
}
return true;
}
SKILL.prototype.do_learn = function (me) {
if (this.on_learn && this.on_learn(me) === false) return false;
if (this.learn_condition) {
for (var key in this.learn_condition) {
var val = this.learn_condition[key];
switch (key) {
case "skill":
for (var sk in val) {
if (me.query_skill(sk, 0) < val[sk] && me.query_skill(sk + "2", 0) < val[sk]) {
var sk_base = SKILL.get(sk);
return me.notify_fail("你的" + sk_base.color_name + "等级不够" + val[sk] + ",无法学习" + this.color_name + "。");
}
}
break;
case "str1":
case "con1":
case "dex1":
case "int1":
if (me.is_player && me[key.replace("1", "")] < val) {
return me.notify_fail("你的" + PROPERTIES[key] + "不够" + val + ",无法学习" + this.color_name + "。");
}
break;
case "str":
case "con":
case "dex":
case "int":
if (me[key] + me.query_prop(key) < val) {
return me.notify_fail("你的" + PROPERTIES[key] + "不够" + val + ",无法学习" + this.color_name + "。");
}
break;
case "gender":
if (me.gender !== val) return me.notify_fail("你不是" + (val === 1 ? "男性" : val === 2 ? "女性" : "无性") + ",无法学习" + this.color_name + "。");
break;
case "desc":
break;
default:
var me_val = me[key] || 0;
me_val = me_val + me.query_prop(key);
if (!me_val || me_val < val) {
return me.notify_fail("你的" + PROPERTIES[key] + "不够" + val + ",无法学习" + this.color_name + "。");
}
break;
}
}
} else {
if (this.type === SKILL_TYPES.SKILL && this.can_enables) {
for (var i = 0; i < this.can_enables.length; i++) {
if (!me.query_skill(this.can_enables[i], 0)) {
var skill = SKILL.get(this.can_enables[i]);
return me.notify_fail("你还不会" + skill.color_name + ",无法学习" + this.color_name + "。");
}
}
}
}
return true;
}
SKILL.prototype.condition_tostring = function (me) {
if (this.learn_condition_string) return this.learn_condition_string;
var str = [];
if (this.learn_condition) {
for (var key in this.learn_condition) {
var val = this.learn_condition[key];
switch (key) {
case "skill":
for (var sk in val) {
var sk_base = SKILL.get(sk);
str.push(sk_base.name + "" + val[sk] + "级");
}
break;
case "desc":
str.push(val);
break;
case "gender":
str.push("性别:" + (val === 1 ? "男" : (val === 2 ? "女" : "无性")));
break;
default:
str.push(PROPERTIES[key] + "" + val);
break;
}
}
}
this.learn_condition_string = str.join("\n");
return this.learn_condition_string;
}
SKILL.prototype.item_to_json = function (str, skill_item, me) {
str.push('{"id":"');
str.push(this.id);
str.push('","name":"');
str.push(this.query_color_name(me));
str.push('",grade:', this.query_grade(me));
str.push(',"level":');
str.push(me.query_skill(this.id));
str.push(',"exp":');
skill_item.exp = skill_item.exp || 0;
str.push(parseInt(skill_item.exp * 100 / this.level_exp(skill_item.level, me)));
if (this.can_enables) {
str.push(',"can_enables":[');
for (var i = 0; i < this.can_enables.length; i++) {
if (i > 0) str.push(",");
str.push('"');
str.push(this.can_enables[i]);
str.push('"');
}
str.push(']');
}
if (skill_item.enable_skill) {
str.push(',"enable_skill":"');
str.push(skill_item.enable_skill);
str.push('"');
}
str.push('}');
}
SKILL.prototype.add_exp = function (me, exp) {
var skill = me.skills[this.id];
if (!skill) {
skill = {
// id: this.id,
level: 0,
exp: 0
};
var str = ['{type:"dialog",dialog:"skills",item:'];
this.item_to_json(str, skill, me);
str.push("}");
me.notify(str.join(""));
me.skills[this.id] = skill;
if (this.type === SKILL_TYPES.BASE) {
me.init_skill();
}
}
var need_exp = this.level_exp(skill.level, me);
skill.exp += exp;
if (skill.exp >= need_exp) {
this.release_prop(me, me.query_skill(this.id));
var sum_score = 0;
var color_name = this.query_color_name(me);
var one_score = this.query_one_score(me);
while (skill.exp >= need_exp) {
skill.exp -= need_exp;
need_exp = this.level_exp(skill.level, me);
me.notify("<hiy>你的" + color_name + "等级提升了!</hiy>");
skill.level++;
if (skill.level > 100)
sum_score += one_score;
}
var lv = me.query_skill(this.id);
this.attach_prop(me, lv);
me.notify('{type:"dialog",dialog:"skills",id:"' + this.id + '",level:' + lv + ',exp:' + parseInt(skill.exp * 100 / need_exp) + '}');
me.recount();
me.add_score(sum_score);
return true;
} else {
me.notify('{type:"dialog",dialog:"skills",id:"' + this.id + '",exp:' + parseInt(skill.exp * 100 / need_exp) + '}');
}
}
SKILL.prototype.query_score = function (lv, me) {
if (lv <= 100) return 0;
return (lv - 100) * this.query_one_score(me);
}
SKILL.prototype.query_one_score = function (me) {
var sc = 0;
if (this.type === SKILL_TYPES.SKILL) {
sc = this.query_grade(me);
} else if (this.type === SKILL_TYPES.BASE) {
sc = 1;
}
return sc;
}
SKILL.prototype.grade_up = function (me, target_skill) {
var skill = me.skills[this.id];
if (!skill || !(skill.level >= 1000)) return false;
if (me.remove_skill(this.id) === false) return false;
me.notify('{type:"dialog",dialog:"skills",remove:"' + this.id + '"}');
var pot = this.query_needexp(skill.level, me);
var lv = pot * 2 / 5 / (target_skill.grade + 1);
skill = {
level: parseInt(Math.pow(lv, 0.5)),
exp: 0
};
me.skills[target_skill.id] = skill;
var str = ['{type:"dialog",dialog:"skills",item:'];
target_skill.item_to_json(str, skill, me);
str.push("}");
me.notify(str.join(""));
me.add_score(target_skill.query_score(skill.level, me));
target_skill.attach_prop(me, skill.level);
me.recount();
return true;
}
SKILL.prototype.get_pfm = function (name) {
if (this.pfm) {
return this.pfm[name];
}
}
SKILL.prototype.set_pfm = function (name, obj) {
if (!this.pfm) {
this.pfm = {};
}
this.pfm[name] = obj;
}
var level_color = ["wht", "hig", "hic", "hiy", "hiz", "hio", "ord"];
var level_desc = ["基本技能", "普通技能", "高级技能", "稀有武技", "绝世武功", "绝世神功", "无上神武"];
SKILL.prototype.create = function (fname) {
if (WORLD.SKILLS[this.id]) {
console.log("%s [%s] is repeated ", this.id, fname);
}
this.update(fname);
}
SKILL.prototype.store = function () {
if (this.type === SKILL_TYPES.KNOWLEDGE
|| this.type === SKILL_TYPES.BASE
) return;
for (var i = 0; i < this.can_enables.length; i++) {
if (!SKILL[this.can_enables[i]]) SKILL[this.can_enables[i]] = new Array(7);
if (!SKILL[this.can_enables[i]][this.grade]) SKILL[this.can_enables[i]][this.grade] = [];
SKILL[this.can_enables[i]][this.grade].push(this);
}
}
SKILL.prototype.update = function (fname) {
WORLD.SKILLS[this.id] = this;
var fam = this.family || FAMILIES.NONE;
if (!fam.skills2) fam.skills2 = [];
if (!fam.skills) fam.skills = [];
if (!fam.skills3) fam.skills3 = [];
if (!fam.skills4) fam.skills4 = [];
var isAddIn = false;
var ary = this.source_skill ?
(this.is_ultimate ? fam.skills3 : fam.skills2) :
(this.is_ultimate ? fam.skills4 : fam.skills);
if (this.type === SKILL_TYPES.KNOWLEDGE || this.is_hidden) {
if (!fam.skills0) fam.skills0 = [];
ary = fam.skills0;
}
for (var i = 0; i < ary.length; i++) {
if (ary[i].id === this.id) {
ary[i] = this;
isAddIn = true;
break;
}
if (ary[i].grade > this.grade) {
ary.splice(i, 0, this);
isAddIn = true;
break;
}
}
if (!isAddIn) {
ary.push(this);
}
this.store();
var desc = level_color[this.grade];
this.color_name = "<" + desc + ">" + this.name + "</" + desc + ">";
if (this.pfm) {
for (var key in this.pfm) {
var pfm = this.pfm[key];
if (pfm.enable_skill === 'sword' || pfm.enable_skill === 'blade' || pfm.enable_skill === 'whip'
|| pfm.enable_skill === 'staff' || pfm.enable_skill === 'club') {
pfm.is_weapon = true;
}
pfm.id = this.id + "/" + key;
pfm.pid = key;
pfm.__proto__ = PERFORM.prototype;
}
}
}
SKILL.get = function (id) {
return WORLD.SKILLS[id];
}
SKILL.prototype.query_desc = function (me, lv) {
var str = [];
var grd = this.query_grade(me);
var cc = level_color[grd];
str.push("<" + cc + ">" + this.name + "</" + cc + ">");
str.push("\n");
if (this.family) {
str.push(this.family.name);
} else {
str.push("公共");
}
str.push(level_desc[grd]);
str.push("\n");
str.push(this.desc);
str.push("\n");
var prop = this.query_prop(lv, me);
if (prop) {
str.push("<");
str.push(cc);
str.push(">");
str.push(UTIL.prop_toString(prop));
str.push("</");
str.push(cc);
str.push(">\n");
}
prop = this.query_enable_prop(lv, me);
var isEnable = this.type === SKILL_TYPES.KNOWLEDGE;
if (prop) {
for (var item in prop) {
var is_enable = me.is_enable_skill(this.id, item);
if (is_enable) isEnable = true;
str.push("<");
str.push(is_enable ? cc : "blk");
str.push(">当装备为");
str.push(SKILL.get(item).name);
str.push("时:\n");
str.push(UTIL.prop_toString(prop[item]));
str.push("</");
str.push(is_enable ? cc : "blk");
str.push(">\n");
}
}
var sk = me.skills[this.id];
if (sk && sk.addin && sk.addin.length) {
str.push("\n<");
str.push(isEnable ? cc : "blk");
str.push(">");
let grd = this.grade + sk.addin.length;
for (let slot of sk.addin) {
let item = this.query_slot(slot);
if (item) {
str.push("◆");
if (item.name) {
str.push(item.name);
str.push(" ");
}
str.push(item.format(parseInt(item.value(lv, grd))));
str.push("\n");
}
}
str.push("</");
str.push(isEnable ? cc : "blk");
str.push(">\n");
}
if (this.pfm) {
str.push("<line>绝招</line>\n");
for (let item in this.pfm) {
var p_item = this.pfm[item];
if (!p_item.name) continue;
this.query_pfm_desc(me, p_item, str, lv);
str.push("\n\n");
}
}
if (sk && sk.ref) {
var refs = sk.ref.split("/");
var sp_skill = SKILL.get(refs[0]);
if (sp_skill) {
var pfm = sp_skill.get_pfm(refs[1]);
if (pfm) {
this.query_pfm_desc(me, pfm, str, lv, sp_skill.name);
str.push("\n\n");
}
}
}
return str.join("");
}
SKILL.prototype.query_slot = function (index) {
if (index < 500) {
return SKILL.PROPERTIES[index];
} else {
return this.slots ? this.slots[index - 500] : null;
}
}
SKILL.REF_CD = 2;
SKILL.SLOTS = {};
SKILL.prototype.query_pfm_desc = function (me, p_item, str, lv, pname) {
var canuse = !p_item.check || p_item.check(me, lv) === true;
var color = canuse ? "hic" : "red";
if (pname) color = 'hir';
str.push("<");
str.push(color);
str.push(">【");
if (pname) {
str.push(pname);
str.push("•");
}
str.push(p_item.name);
str.push("】");
if (!canuse) {
str.push(p_item.use_condition || "");
}
str.push("</");
str.push(color);
str.push(">");
if (pname) lv = parseInt(lv / 2);
str.push("\n内力消耗");
str.push(p_item.query_mp(me, lv));
str.push("\t出招时间");
str.push(p_item.query_releasetime(me, lv) / 1000);
str.push("秒\t冷却时间");
str.push(p_item.query_distime(me, lv, pname) / 1000);
str.push("秒\n");
str.push(p_item.query_desc(me, lv));
}
PERFORM = function () {
this.name = "";
}
PERFORM.inherits(BASE);
PERFORM.prototype.query_name = function (me) {
return this.name;
}
PERFORM.prototype.change_distime = function (me, id, add_time) {
if (me.is_player) {
var dis_time = me.temp["pfm/" + id];
if (dis_time) {
if (add_time)
dis_time.e += add_time;
else {
add_time = -dis_time.time;
dis_time.e = 1;
}
me.notify('{type:"changepfm",id:"' + id + '",time:' + add_time + '}');
}
} else if (me.auto_skills) {
for (var i = 0; i < me.auto_skills.length; i++) {
var item = me.auto_skills[i];
if (item.pfm === this) {
if (add_time)
item.release_time += add_time;
else
item.release_time = 0;
}
}
}
}