init: add workspace files

This commit is contained in:
2026-05-26 16:41:20 +08:00
commit f4f2393f72
1041 changed files with 67405 additions and 0 deletions

179
os/base.js Normal file
View File

@@ -0,0 +1,179 @@
/*
定义当对象从文件生成时的基类,
当文件内部调用this.inherit(父类名)后,将真正继承父类的原型和实例属性。
但是文件内部不能修改this.prototype修改将会作用到父类上面。
可以用this.XXX扩展属性和方法
*/
BASE = function () {
}
BASE.prototype.set = function (pars) {
if (!pars) return;
for (var item in pars) {
this[item] = pars[item];
}
}
/*
该方法使用直接赋值方式继承父对象的所有原型方法,
如果子对象对原型修改,将会改变父对象的原型。
所以该方法只能获取父对象的原型不能修改它,
实际上是作为new的另外一种方式通过apply获取实例属性通过__proto__获取父类原型
*/
BASE.prototype.inherits = function (ctor) {
this.__proto__ = ctor.prototype;
ctor.apply(this);
}
/*
create方法由继承自base类的类自己实现当对象被从文件创建时候调用
参数fname=该对象的文件的相对路径,ctor=构造方法
*/
BASE.prototype.create = function (fname, ctor) {
}
BASE.prototype.add_event = function (fname, func, time) {
///在time秒内用新的func替换旧的fname方法
if (!this[fname])
this[fname] = this.fire_event.bind(this, fname);
if (!this._events) this._events = {};
if (!this._events[fname]) this._events[fname] = [];
this._events[fname].push({
func: func,
time: time ? (Date.now() + time) : Number.MAX_SAFE_INTEGER
});
//var old_func = this[fname];
//this[fname] = func;
//if (time) this.call_out(() => this[fname] = old_func, time);
//return old_func;
}
BASE.prototype.remove_event = function (name, func) {
if (!this._events) return;
var evts = this._events[name];
if (!evts) return;
for (var i = 0; i < evts.length; i++) {
if (evts[i].func === func) {
evts.splice(i, 1);
i--;
}
}
if (!evts.length) {
this._events[name] = null;
this[name] = null;
}
}
BASE.prototype.fire_event = function (name) {
if (!this._events) return;
var evts = this._events[name];
if (!evts) return;
var dt = Date.now();
for (var i = 0; i < evts.length; i++) {
if (evts[i].time > dt) {
if (evts[i].func.call(this) == false) return false;
} else {
evts.splice(i, 1);
i--;
}
}
if (!evts.length) {
this._events[name] = null;
this[name] = null;
}
}
/*
生成对象标识前8位是毫秒级时间戳的36进制形式后4位随机码
最起码保证每毫秒生成的标识不会重复
*/
var key = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
BASE.prototype.create_uid = function () {
var str = [];
str.push(Date.now().toString(36));
var length = key.length;
for (var i = 0; i < 4; i++) {
str.push(key[Math.floor(Math.random() * length)]);
}
return str.join("");
}
BASE.prototype.random = function (num) {
return Math.floor(Math.random() * num);
}
BASE.prototype.call_out = function (func, time, arg1, arg2) {
return setTimeout(func.bind(this, arg1, arg2), time);
}
BASE.prototype.call_interval = function (func, time, count, end_func) {
count--;
var index = 0;
if (func(index++) === false || count === 0) {
return end_func && end_func();
}
var handler = 0;
handler = setInterval(function () {
count--;
if (func(index++) === false || count === 0) {
clearInterval(handler);
end_func && end_func();
}
}, time);
return handler;
}
const vm = require('vm');
const fs = require("fs");
//根据文件路径new一个对象
BASE.ITEMS = {};
BASE.CREATE = function (path, fname) {
var ary = BASE.PATH_REG.exec(fname);
if (!ary) {
return console.error("path %s is incorrect:", path + fname);
}
fname = ary[1];
var paras = ary[2];
var fkey = path + fname;
var func = BASE.ITEMS[fkey];
if (func) {
return BASE.NEW(fname, func, paras);
}
const filepath = fkey + ".js";
try {
const script = fs.readFileSync(filepath);
func = vm.compileFunction(script.toString(), [],
{ filename: filepath });
BASE.ITEMS[fkey] = func;
return BASE.NEW(fname, func, paras);
} catch (e) {
console.error("create %s%s error:", filepath, e, e.stack);
}
}
BASE.CLONE = function (fname) {
}
BASE.PATH_REG = /^(\w+(?:\/\w+)*)(#\w+)?$/;
BASE.NEW = function (fname, func, par) {
var obj = new BASE();
func.apply(obj);
obj.path = fname;
obj.create(fname, par);
return obj;
}
BASE.UPDATE = function (path, fname) {
var ary = BASE.PATH_REG.exec(fname);
if (!ary) {
throw "path " + fname + " is incorrect:";
}
fname = ary[1];
var fkey = path + fname;
var data = fs.readFileSync(fkey + ".js");
var func = new Function(data);
BASE.ITEMS[fkey] = func;
var obj = new BASE();
func.apply(obj);
obj.path = fname;
obj.update && obj.update(fname, ary[2]);
}

234
os/char/chara_comm.js Normal file
View File

@@ -0,0 +1,234 @@
require("./character");
var level_descs = ["普通百姓", "武士", "武师", "宗师", "武圣", "武帝", "武神"];
var level_color = ["", "wht", "hig", "hiy", "hiz", "hio", "ord"];
CHARACTER.prototype.get_level_desc = function () {
if (!this.level) return level_descs[this.level];
var cc = level_color[this.level];
return "<" + cc + ">" + level_descs[this.level] + "</" + cc + ">";
}
CHARACTER.prototype.get_level_color = function () {
return level_color[this.level];
}
//发送给当前对象的消息
CHARACTER.prototype.long_name = function () {
if (this.title) return this.title + " " + this.name;
return this.name;
}
CHARACTER.prototype.query_title = function () {
return this.title;
}
CHARACTER.prototype.query_age = function () {
return this.age;
}
CHARACTER.prototype.call = function (isbad) {
return this.family.call(this, isbad);
}
CHARACTER.prototype.callme = function (isbad) {
return this.family.call_me(this);
}
CHARACTER.prototype.fam_call = function (target) {
let age1 = target.query_age();
let age2 = this.query_age();
if (age1 < age2) {
return this.gender === 1 ? "师兄" : "师姐";
}
return this.gender === 1 ? "师弟" : "师妹";
}
CHARACTER.prototype.call3 = function () {
return this.gender == 1 ? "他" : "她";
}
CHARACTER.prototype.is_hidden = function () {
return this.hp <= 0 || this.query_temp('hidden');
}
CHARACTER.prototype.is_team = function (p) {
if (!p) return false;
if (p.team)
return this.team == p.team;
return this.family == p.family;
}
CHARACTER.prototype.send_team = function (msg, nome) {
if (!this.team) return this.send(msg);
for (var i = 0; i < this.team.length; i++) {
if (nome && this.team[i] == this) continue;
this.team[i].send(msg);
}
}
CHARACTER.prototype.query_teamid = function () {
if (this.follow_target) return this.follow_target.query_teamid();
return null;
}
CHARACTER.prototype.query_status = function () {
var ary = ["{type:\"status\",hp:"];
ary.push(this.hp);
ary.push(",max_hp:");
ary.push(this.max_hp);
ary.push(",mp:");
ary.push(this.mp);
ary.push(",max_mp:");
ary.push(this.max_mp);
ary.push(",name:\"");
ary.push(this.name);
ary.push("\",id:\"");
ary.push(this.id);
ary.push("\"}");
return ary.join("");
}
CHARACTER.prototype.query_commands = function () {
}
CHARACTER.prototype.query_desc = function (me, eqcmd) {
var str = [];
str.push(this.long_name());
str.push("\n");
var call3 = this == me ? "你" : this.call3();
str.push(call3, "看起来约", get_agestr(this.query_age()), "岁。\n");
str.push(call3, "长得", get_perdesc(this), "。\n");
str.push(call3, get_skill_desc(this.query_skill(this.attack_skill.id)), "。\n");
str.push(call3, get_status(this), "\n");
this.format_equipments(call3, str, eqcmd);
return str.join("");
}
CHARACTER.prototype.format_equipments = function (call3, str, eqcmd) {
if (this.query_setting("hide_equip")) {
return str.push("看样子", call3, "不想让别人看自己的装备。");
} else if (this.equipment && this.equipment.length) {
var eqstr = [];
for (var i = 0; i < this.equipment.length; i++) {
var item = this.equipment[i];
if (!item) continue;
eqstr.push("<span cmd='", eqcmd || "look", " ", (i),
" of ", this.id, "'>◆", item.color_name, "</span>\n");
}
if (eqstr.length) {
return str.push(call3, "装备着:\n", eqstr.join(""));
}
}
str.push(call3, "光着身子,什么都没穿。\n");
}
function get_perdesc(obj) {
var ary = obj.gender === 1 ? boy_pers : girl_pers;
var per = obj.per + obj.query_prop("per");
var index = parseInt(per / 2);
if (index < 0) index = 0;
if (index >= ary.length) index = ary.length - 1;
return ary[index];
}
function get_agestr(age) {
var index = parseInt((age || 10) / 10);
if (index >= age_strs.length) index = age_strs.length - 1;
return age_strs[index];
}
function get_skill_desc(level) {
if (!level) return "看上去似乎不会任何武功。";
if (level < 1000)
return "的武功看上去似乎" + skill_levels[parseInt(level / 50)];
var v = parseInt((level - 1000) / 500);
if (v > 6) v = 6;
return "的武功看上去似乎" + skill_levels[v + 20];
}
function get_status(obj) {
var p = parseInt(obj.hp * 10 / obj.max_hp);
if (p < 0) p = 0;
if (p >= Look_status.length) p = Look_status.length - 1;
return Look_status[p];
}
var boy_pers = [
"<BLU>眉歪眼斜,瘌头癣脚,不象人样</BLU>",
"<BLU>呲牙咧嘴,黑如锅底,奇丑无比</BLU>",
"<BLU>面如桔皮,头肿如猪,让人不想再看第二眼</BLU>",
"<HIB>贼眉鼠眼,身高三尺,宛若猴状</HIB>",
"<HIB>肥头大耳,腹圆如鼓,手脚短粗,令人发笑</HIB>",
"<NOR>面颊凹陷,瘦骨伶仃,可怜可叹</NOR>",
"<NOR>傻头傻脑,痴痴憨憨,看来倒也老实</NOR>",
"<NOR>相貌平平,不会给人留下什么印象</NOR>",
"<YEL>膀大腰圆,满脸横肉,恶形恶相</YEL>",
"<YEL>腰圆背厚,面阔口方,骨格不凡</YEL>",
"<RED>眉目清秀,端正大方,一表人才</RED>",
"<RED>双眼光华莹润,透出摄人心魄的光芒</RED>",
"<HIY>举动如行云游水,独蕴风情,吸引所有异性目光</HIY>",
"<HIY>双目如星,眉梢传情,所见者无不为之心动</HIY>",
"<HIR>粉面朱唇,身姿俊俏,举止风流无限</HIR>",
"<HIR>丰神如玉,目似朗星,令人过目难忘</HIR>",
"<MAG>面如美玉,粉妆玉琢,俊美不凡</MAG>",
"<MAG>飘逸出尘,潇洒绝伦</MAG>",
"<MAG>丰神俊朗,长身玉立,宛如玉树临风</MAG>",
"<HIM>神清气爽,骨格清奇,宛若仙人</HIM>",
"<HIM>一派神人气度,仙风道骨,举止出尘</HIM>"
];
var girl_pers = [
"<BLU>丑如无盐,状如夜叉</BLU>",
"<BLU>歪鼻斜眼,脸色灰败,直如鬼怪一般</BLU>",
"<BLU>八字眉,三角眼,鸡皮黄发,让人一见就想吐</BLU>",
"<HIB>眼小如豆,眉毛稀疏,手如猴爪,不成人样</HIB>",
"<HIB>一嘴大暴牙,让人一看就没好感</HIB>",
"<NOR>满脸疙瘩,皮色粗黑,丑陋不堪</NOR>",
"<NOR>干黄枯瘦,脸色腊黄,毫无女人味</NOR>",
"<YEL>身材瘦小,肌肤无光,两眼无神</YEL>",
"<YEL>虽不标致,倒也白净,有些动人之处</YEL>",
"<RED>肌肤微丰,雅淡温宛,清新可人</RED>",
"<RED>鲜艳妍媚,肌肤莹透,引人遐思</RED>",
"<HIR>娇小玲珑,宛如飞燕再世,楚楚动人</HIR>",
"<HIR>腮凝新荔,肌肤胜雪,目若秋水</HIR>",
"<HIW>粉嫩白至,如芍药笼烟,雾里看花</HIW>",
"<HIW>丰胸细腰,妖娆多姿,让人一看就心跳不已</HIW>",
"<MAG>娇若春花,媚如秋月,真的能沉鱼落雁</MAG>",
"<MAG>眉目如画,肌肤胜雪,真可谓闭月羞花</MAG>",
"<MAG>气质美如兰,才华馥比山,令人见之忘俗</MAG>",
"<HIM>灿若明霞,宝润如玉,恍如神妃仙子</HIM>",
"<HIM>美若天仙,不沾一丝烟尘</HIM>",
"<HIM>宛如<HIW>玉雕冰塑</HIW>,似梦似幻,已不再是凡间人物</HIM>"
];
var Look_status = [
"<HIR>受伤过重,已经有如风中残烛,随时都可能断气。</HIR>",
"<HIR>受伤过重,已经奄奄一息,命在旦夕了。</HIR>",
"<HIR>伤重之下已经难以支撑,眼看就要倒在地上。</HIR>",
"<RED>受了相当重的伤,只怕会有生命危险。</RED>",
"<RED>已经伤痕累累,正在勉力支撑著不倒下去。</RED>",
"<RED>气息粗重,动作开始散乱,看来所受的伤著实不轻。</RED>",
"<HIY>受伤不轻,看起来状况并不太好。</HIY>",
"<HIY>受了几处伤,不过似乎并不碍事。</HIY>",
"<HIY>看起来可能受了点轻伤。</HIY>",
"<HIG>似乎受了点轻伤,不过光从外表看不大出来。</HIG>",
"<HIG>看起来气血充盈,并没有受伤。</HIG>"
];
var age_strs = ["几", "十多", "二十多", "三十多", "四十多", "五十多", "六十多", "七十多", "八十多", "九十多"
, "一百多"];
var skill_levels = [
"<BLU>初学乍练</BLU>",
"<BLU>不知所以</BLU>",
"<HIB>粗通皮毛</HIB>",
"<HIB>渐有所悟</HIB>",
"<YEL>半生不熟</YEL>",
"<YEL>马马虎虎</YEL>",
"<HIY>平淡无奇</HIY>",
"<HIY>触类旁通</HIY>",
"<HIG>心领神会</HIG>",
"<HIG>挥洒自如</HIG>",
"<HIC>驾轻就熟</HIC>",
"<HIC>出类拔萃</HIC>",
"<CYN>初入佳境</CYN>",
"<CYN>神乎其技</CYN>",
"<MAG>威不可当</MAG>",
"<HIW>豁然贯通</HIW>",
"<HIW>超群绝伦</HIW>",
"<RED>登峰造极</RED>",
"<WHT>登堂入室</WHT>",
"<HIM>一代宗师</HIM>",
"<WHT>超凡入圣</WHT>",
"<HIO>出神入化</HIO>",
"<HIO>独步天下</HIO>",
"<HIR>空前绝后</HIR>",
"<HIR>旷古绝伦</HIR>",
"<HIW>深不可测</HIW>",
"<HIW>返璞归真</HIW>"];

175
os/char/chara_equip.js Normal file
View File

@@ -0,0 +1,175 @@
require("./character.js");
CHARACTER.prototype.set_objects = function () {
if (!arguments.length) return;
if (!this.items) this.items = [];
for (var i = 0; i < arguments.length; i++) {
var item = arguments[i];
var obj = OBJ.CREATE(item[0], item[1]);
if (!obj) continue;
if (item[2] && obj.is_equipment) {
if (!this.equipment) this.equipment = []
this.equipment[obj.eq_type] = obj;
} else {
this.items.push(obj);
}
}
}
CHARACTER.prototype.unequip = function (obj, notsend, recover_time = 0) {
if (!obj || !obj.is_equipment || !this.equipment) return;
if (obj.uneq(this, notsend) == false) {
return false;
}
if (obj != this.equipment[obj.eq_type]) return;
if (!this.items) this.items = [];
this.items.push(obj);
this.equipment[obj.eq_type] = null;
if (obj.eq_type == EQUIP_TYPE.WEAPON) {
this.remove_status('weapon', true);
if (obj.is_shortcut) {
this.send("{type:'addAction',id:'" + obj.id + "',name:'" + obj.name + "'}");
}
}
if (obj.eq_type === EQUIP_TYPE.WEAPON)
this.weapon_changed(false);
this.recount();
if (recover_time > 0 && this.is_player) {
this.set_temp('eq_wea', obj.id, 60000);
}
}
CHARACTER.prototype.equip = function (obj) {
if (!obj || !obj.is_equipment) return;
if (!this.equipment) this.equipment = []
var equiped = this.equipment[obj.eq_type];
if (equiped == obj) {
return;
}
if (equiped) {
equiped.uneq(this);
this.equipment[equiped.eq_type] = null;
this.items.push(equiped);
if (equiped.eq_type == EQUIP_TYPE.WEAPON) {
this.remove_status('weapon', true);
}
if (equiped.on_use) {
equiped.notify_action(this, false);
}
if (equiped.is_shortcut) {
this.send("{type:'addAction',id:'" + equiped.id + "',name:'" + equiped.name + "'}");
}
}
if (obj.eq(this) == false) {
if (equiped) {
if (obj.eq_type === EQUIP_TYPE.WEAPON)
this.weapon_changed(false);
this.recount();
}
return false;
}
this.items.remove(obj);
this.equipment[obj.eq_type] = obj;
if (obj.eq_type === EQUIP_TYPE.WEAPON)
this.weapon_changed(true);
this.recount();
if (obj.is_shortcut) {
this.send("{type:'removeAction',id:'" + obj.id + "'}");
}
if (obj.eq_type == EQUIP_TYPE.WEAPON) {
if (this.fight_type) {
this.release_time = 3000 + Date.now();
this.send('{type:"dispfm",rtime:3000}');
}
if (this.query_temp('jxtm')) {
this.remove_status('force');
}
}
}
const WEAPON_TYPES = {
"sword": true,
"blade": true,
"staff": true,
"club": true,
"whip": true
}
CHARACTER.prototype.weapon_changed = function (iseq) {
this.attack_skill = this.query_used_skill(this.query_weapon_type());
this.on_skillchanged && this.on_skillchanged();
if (!this.auto_skills) return;
for (let item of this.auto_skills) {
if (WEAPON_TYPES[item.type]) {
item.ban_use = !iseq;
}
}
}
CHARACTER.prototype.add_obj = function (obj, count) {
if (!obj) return;
if (typeof obj == "string") {
obj = OBJ.clone_to(obj, this, count);
if (!obj) return;
} else {
obj = this.push_item(obj);
}
return obj;
}
CHARACTER.prototype.remove_obj = function (obj, count) {
if (typeof obj == "string") {
obj = this.find_obj(obj);
}
if (!obj) return;
count = count || obj.count || 1;
this.remove_item(obj, count);
return obj;
}
CHARACTER.prototype.query_weapon = function () {
if (this.equipment) {
return this.equipment[EQUIP_TYPE.WEAPON];
}
}
CHARACTER.prototype.query_weapon_type = function () {
if (this.equipment) {
var eq = this.equipment[EQUIP_TYPE.WEAPON];
if (eq) return eq.weapon_type;
}
return WEAPON_TYPE.NONE;
}
CHARACTER.prototype.weapon_name = function () {
if (this.equipment && this.equipment[EQUIP_TYPE.WEAPON]) {
return this.equipment[EQUIP_TYPE.WEAPON].color_name;
}
return "";
}
CHARACTER.prototype.throwing_name = function () {
if (this.equipment && this.equipment[EQUIP_TYPE.THROWING]) {
return this.equipment[EQUIP_TYPE.THROWING].color_name;
}
return "";
}
CHARACTER.prototype.can_throwing = function () {
if (this.equipment) {
var th = this.equipment[EQUIP_TYPE.THROWING];
if (!th) return false;
return true;
}
return false;
}
CHARACTER.prototype.get_equipment = function (type) {
return this.equipment && this.equipment[type];
}
CHARACTER.prototype.set_drop = function () {
this.drop_list = this.drop_list || [];
for (var i = 0; i < arguments.length; i++) {
this.drop_list.push(arguments[i]);
}
}
CHARACTER.prototype.query_drop = function () {
if (!this.drop_list) return;
return OBJ.create_by_odds(this.drop_list);
}

169
os/char/chara_move.js Normal file
View File

@@ -0,0 +1,169 @@
CHARACTER.prototype.moveto = function (rm, leave_msg, in_msg, dir) {
var cur_room = this.environment;
var next_room = rm;
if (typeof rm === "string") {
next_room = ROOM.Get(rm);
if (!next_room) return false;
if (next_room.parent === cur_room.parent) {
if (cur_room.owner) {
next_room = next_room.query_copy(cur_room.owner);
}
} else {
var my_room = next_room.query_copy2(this);
if (my_room) {
next_room = my_room;
}
}
}
if (!next_room) return false;
if (next_room.is_full()) {
//如果房间人数过多,创建投影
next_room = next_room.create_shadow();
if (!next_room) {
//如果创建失败就返回,房间设置不允许投影,或别的原因
return this.notify("那里人太多了,你过不去。");
}
}
if (cur_room) {
if (cur_room.do_leave(this, dir, leave_msg) === false) {
return false;
}
}
next_room.do_enter(this, true, in_msg);
this.notify_follower(dir);
if (cur_room && next_room.parent !== cur_room.parent) {
cur_room.parent.on_leaved(this);
}
}
CHARACTER.prototype.do_follow = function (target) {
if (target) {
if (target.query_setting("no_follow")) {
return this.notify(target.name + "不允许别人跟随。");
}
if (this.follow_target) {
this.follow_target.follow_targets.remove(this);
this.send_room("$N不再跟随$n一起行动。", this.follow_target);
}
this.follow_target = target;
if (!target.follow_targets) {
target.follow_targets = [];
}
target.follow_targets.push(this);
this.send_room("<hig>$N决定跟随$n一起行动。</hig>", target);
} else {
if (!this.follow_target) return this.notify("你目前没有跟随别人一起行动。");
this.follow_target.follow_targets.remove(this);
this.send_room("$N不再跟随$n一起行动。", this.follow_target);
this.follow_target = null;
}
}
CHARACTER.prototype.clear_follow = function () {
if (this.follow_target) {
this.follow_target.follow_targets.remove(this);
this.follow_target = null;
}
if (this.follow_targets) {
for (let item of this.follow_targets) {
item.follow_target = null;
}
this.follow_targets = null;
}
}
CHARACTER.prototype.notify_follower = function (dir) {
if (this.follow_targets) {
for (var i = 0; i < this.follow_targets.length; i++) {
var item = this.follow_targets[i];
if (!item.environment) continue;
if (!item.is_player) {
if (item.on_master_leave) {
if (item.on_master_leave(this, this.environment) == false) {
continue;
}
} else {
if (item.environment.is_fb() != this.environment.is_fb()) {
continue;
}
}
//if (item.state && item.set_state) {
// item.set_state(null);
//}
item.moveto(this.environment,
sendOutMessage(item, dir), sendInMessage(item));
} else {
item.moveto(this.environment,
sendOutMessage(item, dir), sendInMessage(item));
}
}
}
}
CHARACTER.prototype.do_escape = function () {
var eny = this.query_enemy();
if (!eny) return true;
if (eny.on_escape) return eny.on_escape(this);
var is_esc = this.random(this.ds / 2) + this.ds > eny.mz;
if (eny.is_faint) is_esc = true;
if (!is_esc) {
this.send_room("<cyn>$N转身想跑$n一把拦住$P想跑没门\n</cyn>", eny);
this.add_status({
id: "busy",
name: "忙乱",
duration: this.gjsd,
is_busy: true
});
}
return is_esc;
}
CHARACTER.prototype.team_out = function (msg) {
var tm = this.team;
if (!tm) return;
for (var i = 0; i < tm.length; i++) {
if (!tm[i].is_player && tm[i].master == this.id) {
tm[i].on_teamout && tm[i].on_teamout();
this.notify(tm[i].name + "退出了队伍。");
tm[i].team = null;
tm.splice(i, 1);
i--;
}
}
this.team = null;
var iscap = this == tm[0];
tm.remove(this);
checkTeamfb(this);
if (!tm.length) {
this.send("你的队伍解散了。");
this.send('{"type":"dialog","dialog":"team",dismiss:true}');
} else {
var first = tm[0];
var dissmsg = '{"type":"dialog","dialog":"team",dismiss:true}';
this.send("<hic>你退出了队伍。</hic>");
if (tm.length > 1) {
if (iscap) {
first.send_team("<hic>" + this.name + msg + "" + first.name + "现在是队长。</hic>");
} else {
first.send_team("<hic>" + this.name + msg + "。</hic>");
}
first.send_team('{"type":"dialog","dialog":"team",remove:"' + this.id + '"}');
this.send(dissmsg);
} else {
if (first.team) {
checkTeamfb(first);
first.send("<hic>" + this.name + msg + ",你的队伍解散了。</hic>");
first.team.length = 0;
first.team = null;
first.send(dissmsg);
this.send(dissmsg);
}
}
}
}

137
os/char/chara_prop.js Normal file
View File

@@ -0,0 +1,137 @@
require("./character.js");
CHARACTER.prototype.add_prop = function (p, v) {
if (!p) return;
if (!this.prop) {
this.prop = {};
}
var v1 = this.prop[p] || 0;
this.prop[p] = v1 + v;
}
CHARACTER.prototype.clear_prop = function () {
this.prop = {};
}
CHARACTER.prototype.query_prop = function (name) {
if (this.prop)
return this.prop[name] || 0;
return 0;
}
CHARACTER.prototype.query_force_rad = function () {
if (this.force_skill && this.force_skill.force_rad)
return this.force_skill.force_rad || 0.1;
return 0.1;
}
CHARACTER.prototype.add_maxmp = function (count) {
this.max_mp += count;
this.recount();
this.notify("<hig>你增加了" + count + "点内力。</hig>");
}
CHARACTER.prototype.query_temp = function (name, def) {
if (!this.temp) return def;
var item = this.temp[name];
if (item && item.e) {
if (Date.now() <= item.e) {
return item.v;
}
this.temp[name] = null;
return def;
}
return item ?? def;
}
CHARACTER.prototype.set_temp = function (name, value, time) {
if (!this.temp) this.temp = {};
if (time) {
this.temp[name] = {
v: value,
e: Date.now() + time
};
} else {
this.temp[name] = value;
}
}
CHARACTER.prototype.remove_temp = function (name) {
if (!this.temp) return;
this.temp[name] = null;
}
CHARACTER.prototype.add_temp = function (name, value, time) {
let val = this.query_temp(name, 0) + value;
this.set_temp(name, val, time);
return val;
}
CHARACTER.prototype.change_prop = function (prop, isadd) {
if (!prop) return;
for (var item in prop) {
switch (item) {
case "desc":
break;
case "skill":
var sks = prop[item];
for (var sk in sks) {
var lv = this.query_skill(sk, 0);
if (!lv) {
this.add_prop(sk, isadd ? sks[sk] : -sks[sk]);
continue;
}
var sk_base = SKILL.get(sk);
if (!sk_base) continue;
sk_base.release_prop(this, lv);
this.add_prop(sk, isadd ? sks[sk] : -sks[sk]);
lv = this.query_skill(sk, 0);
sk_base.attach_prop(this, lv);
if (this.is_player) {
this.notify('{type:"dialog",dialog:"skills",id:"' + sk + '",level:' + lv + '}');
}
}
break;
default:
this.add_prop(item, isadd ? prop[item] : -prop[item]);
break;
}
}
}
CHARACTER.prototype.add_fbscore = function (v, max) {
var fb = this.environment.query_fb_first(this.query_teamid());
if (!fb) return;
fb.score = (fb.score || 0) + v;
if (max > 0 && fb.score > max) fb.score = max;
}
CHARACTER.prototype.query_fbscore = function (v) {
var first_room = this.environment.query_fb_first(this.query_teamid());
if (!first_room) return 0;
return first_room.score || 0;
}
CHARACTER.prototype.add_score = function (val) {
}
CHARACTER.prototype.add_combat_prop = function (name, val) {
this.add_prop(name, val);
if (!this.combat_props) this.combat_props = [];
this.combat_props.push([name, val]);
if (name === 'max_hp') {
this.max_hp += val;
}
}
CHARACTER.prototype.clear_combat_prop = function (name, val) {
if (this.combat_props) {
for (let i = 0; i < this.combat_props.length; i++) {
this.add_prop(this.combat_props[i][0], -this.combat_props[i][1]);
if (this.combat_props[i][0] === 'max_hp') {
this.max_hp -= this.combat_props[i][1];
this.notify_hp();
}
}
this.combat_props = null;
this.recount();
}
}

566
os/char/chara_skill.js Normal file
View File

@@ -0,0 +1,566 @@
require("./character.js");
CHARACTER.prototype.query_skill = function (name, def) {
if (!this.skills || !this.skills[name]) return def || 0;
return this.skills[name].level + this.query_prop(name);
}
CHARACTER.prototype.skill_map = function () {
//给NPC初始化技能用的不做任何判断
this.skills = this.skills || {};
for (var i = 0; i < arguments.length; i++) {
var item = arguments[i];
if (!item) continue;
var skill_base = SKILL.get(item[0]);
if (!skill_base) {
console.log(item[0] + " not exits")
continue;
}
var skill = {
level: item[1] || 1,
exp: 0
};
this.skills[skill_base.id] = skill;
if (item[2]) {
var enables = item[2];
if (typeof enables == "string") enables = [enables];
for (var j = 0; j < enables.length; j++) {
skill[enables[j]] = true;
this.skills[enables[j]].enable_skill = item[0];
}
}
}
}
CHARACTER.prototype.remove_skill = function (skillid) {
var skill = this.skills[skillid];
if (!skill) return;
var baseskill = SKILL.get(skillid);
if (!baseskill) return false;
if (baseskill.type == SKILL_TYPES.BASE) {
if (skill.enable_skill) {
var old_skill = SKILL.get(skill.enable_skill);
if (!old_skill || !this.skills[skill.enable_skill]) {
return false;
}
old_skill.disenable(this, skillid);
this.skills[skill.enable_skill][skillid] = false;
}
} else {
for (var key in this.skills) {
if (this.skills[key].enable_skill == skillid) {
this.skills[key].enable_skill = null;
}
}
}
baseskill.release_prop(this, this.query_skill(skillid));
delete this.skills[skillid];
this.init_skill();
this.recount();
this.add_score(-baseskill.query_score(skill.level, this));
baseskill.on_remove && baseskill.on_remove(this);
return true;
}
//增加技能
CHARACTER.prototype.set_skill = function (skid, level) {
if (!this.skills) this.skills = {};
var item = this.skills[skid];
var skill_base = SKILL.get(skid);
if (!skill_base) return;
if (!item) {
item = {
//id: skid,
level: level,
exp: 0
};
this.skills[skid] = item;
skill_base.attach_prop(this, level);
} else {
skill_base.release_prop(this, this.query_skill(skid));
item.level = level;
skill_base.attach_prop(this, this.query_skill(skid));
}
this.init_skill();
this.recount();
}
CHARACTER.prototype.skill_limit = function () {
if (this.exp < 100) return 10;
switch (this.level) {
case 1: return Math.round(Math.pow(this.exp * 20, 1 / 3));
case 2: return Math.round(Math.pow(this.exp * 30, 1 / 3));
case 3: return Math.round(Math.pow(this.exp * 40, 1 / 3));
case 4: return Math.round(Math.pow(this.exp * 50, 1 / 3));
case 5: return Math.round(Math.pow(this.exp * 60, 1 / 3));
case 6: return Math.round(Math.pow(this.exp * 60, 1 / 3));
default:
return Math.round(Math.pow(this.exp * 10, 1 / 3));
}
}
CHARACTER.prototype.query_ref_skill = function (skill) {
if (!skill || !skill.ref) return;
var refs = skill.ref.split("/");
var sp_skill = SKILL.get(refs[0]);
if (sp_skill) {
return sp_skill.get_pfm(refs[1]);
}
}
//判断某个技能是否装备到某个基本技能上
CHARACTER.prototype.is_enable_skill = function (skid, type) {
if (!this.skills) return;
var item = this.skills[skid];
if (!item) return;
return item[type];
}
//装备技能到基本技能
CHARACTER.prototype.enable_skill = function (base, skill) {
if (!this.skills) return;
var baseskill = this.skills[base];
if (!baseskill) return;
if (baseskill.enable_skill) {
var old_skill = this.skills[baseskill.enable_skill];
if (!old_skill) return;
old_skill[base] = false;
old_skill = SKILL.get(baseskill.enable_skill);
if (old_skill) {
old_skill.disenable(this, base);
}
}
if (skill) {
var sp_skill = this.skills[skill];
if (baseskill && sp_skill) {
var sp_skill_base = SKILL.get(skill);
if (!sp_skill_base || sp_skill_base.enable(this, base) !== true)
return false;
baseskill.enable_skill = skill;
sp_skill[base] = true;
}
} else {
baseskill.enable_skill = null;
}
this.init_skill();
this.recount();
return true;
}
//初始化人物使用的技能
CHARACTER.prototype.init_skill = function () {
this.attack_skill = this.query_used_skill(this.query_weapon_type());
this.noweapon_skill = this.query_used_skill(WEAPON_TYPE.NONE);
this.dodge_skill = this.query_used_skill(BASE_SKILLS.DODGE);
this.parry_skill = this.query_used_skill(BASE_SKILLS.PARRY);
this.force_skill = this.query_used_skill(BASE_SKILLS.FORCE);
this.on_skillchanged && this.on_skillchanged();
this.auto_skills = null;
}
CHARACTER.prototype.query_used_skill = function (skname) {
if (!this.skills) {
return WORLD.DEFAULT_SKILLS[skname];
}
var skill = this.skills[skname];
if (skill) {
var skill_base = SKILL.get(skill.enable_skill || skname);
if (!skill_base) skill.enable_skill = null;
else return skill_base;
}
return WORLD.DEFAULT_SKILLS[skname];
}
CHARACTER.prototype.query_status = function (sid) {
if (!this.status) return 0;
for (var i = 0; i < this.status.length; i++) {
if (this.status[i].id == sid) {
return this.status[i].count;
}
}
return 0;
}
CHARACTER.prototype.add_status = function (buff, from) {
if (this.hp <= 0) return false;
if (!this.status) this.status = [];
var sid = buff.id;
buff.override = buff.override || 0;
buff.count = buff.count || 1;
buff.max_count = buff.max_count || 10;
buff.start_time = Date.now();
if (buff.on_interval) {
buff.over_count = buff.over_count || 0;
}
if (this.ig_control && !buff.no_diff) {
if (buff.is_busy || buff.is_faint || buff.is_miss || buff.is_rash) {
return false;
}
}
if (buff.downside && buff.duration && !buff.no_diff) {
//buff.duration = buff.duration - this.query_prop("diff_downside");
buff.duration = Math.round(buff.duration * (100 - this.query_prop("diff_downside_per")) / 100);
if (buff.duration <= 0) {
return false;
}
}
if (buff.is_busy && !buff.no_diff) {
if (from) {
buff.duration = buff.duration + from.query_prop("busy");
buff.duration = buff.duration * (100 + from.query_prop("busy_per")) / 100;
}
buff.duration = buff.duration - this.query_prop("diff_busy");
buff.duration = Math.round(buff.duration * (100 - this.query_prop("diff_busy_per")) / 100);
if (buff.duration <= 0) {
return false;
}
}
for (var i = 0; i < this.status.length; i++) {
if (this.status[i].id == sid) {
var item = this.status[i];
if (item.override != buff.override) return false;
if (item.override == 0) {
//0不覆盖 1叠加 2覆盖替换
return false;
}
if (item.override == 1) {
if (item.max_count <= item.count) {
return false;
}
item.count += buff.count;
clearTimeout(item.handler);
if (item.duration)
item.handler = this.call_out(this.remove_status, item.duration, sid);
this.change_buff(item, true, buff.count);
item.start_time = buff.start_time;
this.status_changed(item, "refresh");
} else {
item.handler && clearTimeout(item.handler);
this.change_buff(item, false, item.count);//覆盖先移除 后添加 重新计时
this.status_changed(item, "remove");
if (buff.duration)
item.handler = this.call_out(this.remove_status, buff.duration, sid);
this.status[i] = buff;
buff.start_time = buff.start_time;
this.change_buff(buff, true, buff.count);
this.status_changed(buff, "add");
}
return;
}
}
if (buff.duration)
buff.handler = this.call_out(this.remove_status, buff.duration, sid);
this.status.push(buff);
this.change_buff(buff, true, buff.count);
this.status_changed(buff, "add");
}
CHARACTER.prototype.clear_downside = function (type) {
if (!this.status) return;
var removed = "0";
var count = 0;
for (var i = 0; i < this.status.length; i++) {
var item = this.status[i];
if ((item.downside || false) == type && !item.no_clear) {
this.change_buff(item, false, item.count);
item.handler && clearTimeout(item.handler);
this.status.splice(i, 1);
i--;
removed += ',"' + item.id + '"';
count++;
}
}
if (removed.length == 1 || !this.environment) return;
var items = this.environment.items;
var msg = '{type:"status",id:"' + this.id + '",sid:[' + removed + '],action:"remove"}';
for (var i = 0; i < items.length; i++) {
var player = items[i];
if (player.is_player) {
player.send(msg);
}
}
return count;
}
CHARACTER.prototype.clear_combat_status = function () {
if (!this.status || !this.status.length) return;
var removed = "0";
for (var i = this.status.length - 1; i >= 0; i--) {
var item = this.status[i];
if (item.only_combat) {
this.change_buff(item, false, item.count);
item.handler && clearTimeout(item.handler);
this.status.splice(i, 1);
removed += ',"' + item.id + '"';
}
}
if (removed.length == 1 || !this.environment) return;
var items = this.environment.items;
var msg = '{type:"status",id:"' + this.id + '",sid:[' + removed + '],action:"remove"}';
for (var i = 0; i < items.length; i++) {
var player = items[i];
if (player.is_player) {
player.send(msg);
}
}
}
CHARACTER.prototype.remvoe_statuses = function (func) {
if (!this.status || !this.status.length) return;
var removed = "0";
for (var i = this.status.length - 1; i >= 0; i--) {
var item = this.status[i];
if (func(item)) {
this.change_buff(item, false, item.count);
item.handler && clearTimeout(item.handler);
this.status.splice(i, 1);
removed += ',"' + item.id + '"';
}
}
if (removed.length == 1 || !this.environment) return;
var items = this.environment.items;
var msg = '{type:"status",id:"' + this.id + '",sid:[' + removed + '],action:"remove"}';
for (var i = 0; i < items.length; i++) {
var player = items[i];
if (player.is_player) {
player.send(msg);
}
}
}
CHARACTER.prototype.clear_status = function () {
if (!this.status || !this.status.length) return;
for (var i = 0; i < this.status.length; i++) {
var item = this.status[i];
this.change_buff(item, false, item.count);
item.handler && clearTimeout(item.handler);
}
this.status.length = 0;
if (!this.environment) return;
var msg = '{type:"status",id:"' + this.id + '",action:"clear"}';
var items = this.environment.items;
for (var i = 0; i < items.length; i++) {
var player = items[i];
if (player.is_player) {
player.send(msg);
}
}
}
CHARACTER.prototype.change_buff = function (buff, isadd, buff_count) {
if (isadd) {
if (buff.prop) {
for (var i = 0; i < buff_count; i++) {
this.change_prop(buff.prop, true);
}
this.recount();
}
if (buff.on_attach) buff.on_attach(this);
if (buff.ig_control) this.ig_control = buff.duration;
if (buff.start_msg) {
this.send_room(buff.start_msg);
}
if (buff.is_busy) this.is_busy = buff.duration;
if (buff.is_miss) this.is_miss = buff.duration;
if (buff.is_rash) this.is_rash = buff.duration;
if (buff.is_shadow) this.is_shadow = buff.duration;
if (buff.is_faint) {
this.is_faint = buff.duration;
}
} else {
if (buff.prop) {
for (var i = 0; i < buff_count; i++) {
this.change_prop(buff.prop, false);
}
this.recount();
}
if (buff.on_expire) buff.on_expire(this);
if (buff.is_busy) this.is_busy = 0;
if (buff.is_miss) this.is_miss = 0;
if (buff.is_rash) this.is_rash = 0;
if (buff.ig_control) this.ig_control = 0;
if (buff.is_shadow) this.is_shadow = 0;
if (buff.is_faint) {
this.is_faint = 0;
}
if (this.hp > 0 && buff.finish_msg) {
this.send_room(buff.finish_msg);
}
this.remove_temp(buff.id);
}
}
CHARACTER.prototype.appdend_status = function (str) {
if (!this.status) return;
var now = Date.now();
str.push(",status:[");
for (var i = 0; i < this.status.length; i++) {
if (i > 0) str.push(",");
var item = this.status[i];
str.push('{sid:"');
str.push(item.id);
str.push('",name:"');
str.push(item.name);
str.push('",duration:');
if (item.on_interval) {
str.push(item.duration * item.duration_count);
} else {
str.push(item.duration);
}
str.push(',overtime:');
str.push(now - item.start_time);
if (item.override === 1) {
str.push(',"count":');
str.push(item.count);
}
if (item.downside) {
str.push(',downside:');
str.push(true);
}
str.push('}');
}
str.push("]");
}
CHARACTER.prototype.notify_status = function () {
if (!this.status) return;
var str = ['{type:"status",id:\"'];
str.push(this.id);
str.push("\",action:'load',items:[");
var now = Date.now();
for (var i = 0; i < this.status.length; i++) {
if (i > 0) str.push(",");
var item = this.status[i];
str.push('{sid:"');
str.push(item.id);
str.push('",name:"');
str.push(item.name);
str.push('",duration:');
if (item.on_interval) {
str.push(item.duration * item.duration_count);
} else {
str.push(item.duration);
}
str.push(',overtime:');
str.push(now - item.start_time);
if (item.override === 1) {
str.push(',"count":');
str.push(item.count);
}
if (item.downside) {
str.push(',downside:');
str.push(true);
}
str.push('}');
}
str.push("]}");
this.send(str.join(""));
}
CHARACTER.prototype.status_changed = function (item, type) {
var str = [];
str.push('{type:"status","action":"');
str.push(type);
str.push('",id:"');
str.push(this.id);
str.push('",sid:"');
str.push(item.id);
str.push('"');
if (type === "add") {
str.push(',"name":"');
str.push(item.name);
str.push('","duration":');
if (item.on_interval) {
str.push(item.duration * item.duration_count);
} else {
str.push(item.duration);
}
if (item.override === 1) {
str.push(',"count":');
str.push(item.count);
}
if (item.downside) {
str.push(',downside:');
str.push(true);
}
} else if (type === "refresh") {
str.push(',count:');
str.push(item.count);
}
str.push('}');
if (!this.environment) return;
var msg = str.join("");
var items = this.environment.items;
for (var i = 0; i < items.length; i++) {
var player = items[i];
if (player.is_player) {
player.send(msg);
}
}
}
CHARACTER.prototype.remove_status = function (sid, isall) {
if (!this.status) return;
for (var i = this.status.length - 1; i >= 0; i--) {
if (this.status[i].id === sid) {
var item = this.status[i];
if (item.handler) clearTimeout(item.handler);
item.handler = null;
if (item.on_interval && !isall) {
item.over_count++;
if (item.on_interval) {
if (item.on_interval(this, item.over_count) === false) {
item.duration_count = item.duration_count || 2;
item.over_count = item.duration_count;
}
}
if (item.duration_count === 0 || (item.duration_count > 1 && item.duration_count > item.over_count)) {
if (item.duration)
item.handler = this.call_out(this.remove_status, item.duration, sid);
// item.start_time = Date.now();
return;
}
}
this.change_buff(item, false, 1);
if (item.override === 1) {
item.count--;
if (item.count === 0 || isall) {
this.change_buff(item, false, item.count);
this.status_changed(item, "remove");
this._splice_status(i, item);
} else {
if (item.duration)
item.handler = this.call_out(this.remove_status, item.duration, sid);
item.start_time = Date.now();
this.status_changed(item, "refresh");
}
} else {
//覆盖和不覆盖的都直接移除
this.status_changed(item, "remove");
this._splice_status(i, item);
}
return;
}
}
}
CHARACTER.prototype._splice_status = function (index, item) {
if (this.status[index] === item) {
return this.status.splice(index, 1);
}
for (var i = 0; i < this.status.length; i++) {
if (this.status[i] === item) {
return this.status.splice(i, 1);
}
}
}

351
os/char/character.js Normal file
View File

@@ -0,0 +1,351 @@
require("../item");
/*global CHARACTER ROOM ITEM*/
CHARACTER = function () {
this.name = "生物";
this.hp = this.max_hp = 100;
this.mp = this.max_mp = 100;
this.str = this.con = this.dex = this.int = 20;
this.money = 0;
}
CHARACTER.inherits(ITEM);
CHARACTER.prototype.send = function () {
}
CHARACTER.prototype.notify = function () {
}
CHARACTER.prototype.send_commands = function () {
}
CHARACTER.prototype.notify_fail = function () {
return false;
}
CHARACTER.prototype.is_living = function () {
return this.hp > 0;
}
CHARACTER.prototype.is_in = function (path) {
if (!this.environment) return false;
return this.environment.path === path;
}
CHARACTER.prototype.is_here = function (obj) {
if (!this.environment || !obj.environment) return false;
return this.environment === obj.environment;
}
CHARACTER.prototype.find_obj = function (oid, parent) {
var items = this.items;
if (parent) items = parent.items;
return this.find_obj_byid(items, oid);
}
CHARACTER.prototype.send_message = function (msg, include_me) {
if (!msg || !this.environment) return;
var list = this.environment.items;
for (var i = 0; i < list.length; i++) {
if (list[i].is_player) {
if (list[i] === this && !include_me) continue;
if (!list[i].no_message)
list[i].notify(msg);
}
}
}
CHARACTER.prototype.send_combat = function (text, target) {
if (!this.environment || !text) return;
var list = this.environment.items;
var th_vision, item;
for (var i = 0; i < list.length; i++) {
item = list[i];
if (item.is_player) {
if (item === this) {
if (!item.query_setting("no_mcmsg"))
item.notify(splitmessage(this, text, 1, target));
} else if (item === target) {
if (!item.query_setting("no_mcmsg"))
item.notify(splitmessage(this, text, 2, target));
} else if (!item.no_message && !item.query_setting("no_combatmsg")) {
if (!th_vision) th_vision = splitmessage(this, text, 3, target);
item.notify(th_vision);
}
}
}
}
CHARACTER.prototype.send_room = function (text, target, excludeself) {
if (!this.environment || !text) return;
var list = this.environment.items;
var th_vision;
for (var i = 0; i < list.length; i++) {
if (list[i].is_player) {
if (list[i] === this) {
!excludeself && list[i].notify(splitmessage(this, text, 1, target));
} else if (list[i] === target) {
list[i].notify(splitmessage(this, text, 2, target));
} else if (!list[i].no_message) {
if (!th_vision) th_vision = splitmessage(this, text, 3, target);
list[i].notify(th_vision);
}
}
}
}
CHARACTER.prototype.query_setting = function (name) {
return false;
}
function splitmessage(me, text, type, target) {
if (text.length < 3) return text;
var str = [];
var start = 0;
for (var i = 0; i < text.length; i++) {
if (text[i] === "$") {
start < i && str.push(text.substring(start, i));
var ch = text[++i];
start = i + 1;
//type 1本人视角 2目标视角 3第三人称视角
switch (ch) {
case "N"://本人
str.push(type === 1 ? "你" : me.name);
break;
case "n"://目标
str.push(type === 2 ? "你" : target.name);
break;
case "P":
str.push(type === 1 ? "你" : me.call3());
break;
case "p":
str.push(type === 2 ? "你" : target.call3());
break;
case "l":
str.push(me.attack_part ? me.attack_part.name : "");
break;
case "W":
case "w":
str.push(me.weapon_name() || "手");
break;
case "i":
str.push(target.weapon_name() || "手");
break;
case "T":
str.push(me.throwing_name());
break;
}
}
}
start < i && str.push(text.substring(start, i));
return str.join("");
}
//执行一串命令
CHARACTER.prototype.command = function (req) {
if (this.wait_input) { //如果用户等待输入
this.wait_input.apply(this, [this, req]);
return;
}
var cmd = null, pars = null, start = 0, i = 0;
for (; i < req.length; i++) {
if (req[i] === ' ') {
if (start < i) {
cmd = req.substring(start, i);
pars = req.substring(i + 1);
break;
}
start = i;
}
}
if (!cmd) cmd = req;
this.do_command(cmd, pars);
}
CHARACTER.prototype.do_command = function (cmdName, str) {
var cmd = WORLD.COMMANDS[cmdName];
var pars;
if (cmd && cmd.regex && str) {
pars = cmd.regex.exec(str);
pars ? pars[0] = this : pars = [this, str];
} else {
pars = [this, str];
}
if (this.do_item_action(this.environment, cmdName, pars)) {
//如果环境触发这个命令就返回
return;
}
cmd = cmd || WORLD.DEFAULT_COMMAND;
if (cmd) {
if (!this.check_command(cmd))
return;//不允许执行
if (cmd.enter.apply(cmd, pars) !== false) {
return;//命令执行完成。
}
}
if (str) {
//如果有参数,尝试找下这个物件
if (this.do_item_action(this.find_obj(str, this.environment), cmdName, pars)) {
//如果物件触发这个命令就返回
return;
}
}
this.send("什么?");
}
CHARACTER.prototype.do_item_action = function (item, cmd, pars) {
if (!item || !item.actions) return;
var cmdItem = item.actions[cmd];
if (!cmdItem || !cmdItem.action) return;
if (!this.check_command(cmdItem))
return true;//返回true 不允许执行
if (cmdItem.action.apply(item, pars) !== false)
return true; //返回true表示不继续执行该命令
}
CHARACTER.prototype.check_command = function (cmd) {
if (cmd.allow_level > this.user_level) {
this.send("什么?");
return false;
}
if (this.hp <= 0 && !cmd.allow_die) {
//死亡状态,除了一些特殊指令都不能做
return this.notify_fail("你现在是灵魂状态,不能那么做。");
}
if (!cmd.allow_faint && this.is_faint) {
this.send("你正在昏迷中!");
return false;
}
if (!cmd.allow_state && this.state) {
return this.notify_fail("你正在" + this.state.title + ",没时间这么做。");
}
if (!cmd.allow_fight && this.fight_type > 0) {
return this.notify_fail("你正在战斗,待会再说。");
}
if (!cmd.allow_busy && this.is_busy) {
return this.notify_fail("你现在正忙。");
}
return true;
}
CHARACTER.prototype.create = function (path, par) {
//模板被创建,实际上没真正用
if (par) this.path = path + par;
this.on_create && this.on_create(path, par);
WORLD.NPC_STROE.set(this.path, this);
}
CHARACTER.prototype.update = function (path, par) {
this.create(path, par);
}
//真正被复制到房间
CHARACTER.prototype.clone = function () {
if (this.temp) this.temp = Object.create(this.temp);
if (this.prop) this.prop = Object.create(this.prop);
if (this.equipment) {
let eqs = [];
for (let i = 0; i < this.equipment.length; i++) {
let item = this.equipment[i];
if (item) {
eqs[i] = OBJ.CREATE(item.path);
}
}
this.equipment = eqs;
}
//装备和道具只保留引用应该没事2个相同NPC的装备和背包是同一个道具
//好像随从不行
if (this.items) {
let items = [];
for (let i = 0; i < this.items.length; i++) {
items[i] = OBJ.CREATE(this.items[i].path);
}
this.items = items;
}
this.create_id();
this.init();
this.recount();
this.hp = this.max_hp;
this.mp = this.max_mp;
this.on_clone && this.on_clone();
}
CHARACTER.prototype.init = function () {
if (this.equipment) {
let groups = {};
for (let i = 0; i < this.equipment.length; i++) {
let item = this.equipment[i];
if (item) {
item.change_prop(this, true);
item.on_eq && item.on_eq(this);
if (item && item.group_name) {
groups[item.group_name] = (groups[item.group_name] || 0) + 1;
var prop = item.group_prop(groups[item.group_name]);
if (prop) {
this.change_prop(prop, true);
}
}
}
}
}
if (this.is_player) this.score = 0;
if (this.skills) {
for (let item in this.skills) {
var base_skill = SKILL.get(item);
if (!base_skill) {
delete this.skills[item];
continue;
}
base_skill.attach_prop(this, this.query_skill(item));
if (this.is_player) {
this.score += base_skill.query_score(this.skills[item].level, this);
}
}
}
this.init_skill();
}
CHARACTER.EXP_LIMIT = [300000, 3000000, 20000000];
CHARACTER.prototype.add_exp = function (exp, pot, money) {
if (exp) {
exp += this.query_temp("exp_up", 0);
}
if (pot) {
pot += this.query_temp("pot_up", 0);
}
var str = ["<hig>你获得了"];
if (exp) {
this.exp += exp;
str.push(exp);
str.push("点经验");
}
if (pot) {
this.pot += pot;
if (exp) str.push("");
str.push(pot);
str.push("点潜能");
}
if (money) {
this.money += money;
if (exp || pot) str.push("");
str.push(UTIL.moneyToStr(money));
}
if (str.length === 1) return;
str.push("。</hig>");
this.send(str.join(""));
}
CHARACTER.prototype.add_money = function (val) {
this.money += val;
}
CHARACTER.prototype.check_groupeq = function () {
var eqs = {};
for (var i = 0; i < this.equipment.length; i++) {
var item = this.equipment[i];
if (item && item.group_name) {
eqs[item.group_name] = (eqs[item.group_name] || 0) + 1;
var prop = item.group_prop(eqs[item.group_name]);
if (prop) {
this.change_prop(prop, true);
}
}
}
}

250
os/char/combat.js Normal file
View File

@@ -0,0 +1,250 @@
require("./character.js");
CHARACTER.prototype.begin_attack = function (target, type) {
if (!target || target === this) return;
if (!this.attack_skill) {
return this.send("error skill");
}
this.add_enemy(target);
this.fight_type = this.fight_type || 0;
if (type > this.fight_type) {
if (this.force_skill && this.force_skill.on_beginfight) {
this.force_skill.on_beginfight(this, target);
}
if (this.attack_skill && this.attack_skill.on_beginfight) {
this.attack_skill.on_beginfight(this, target);
}
if (!this.fight_type) {
if (this.attack_handler) clearTimeout(this.attack_handler);
this.attack_handler = this.call_out(this.auto_attack, Math.random() * this.gjsd);
this.send('{type:"combat",start:1}');
}
this.fight_type = type; //1fight hp<30% 2 kill 0
}
}
CHARACTER.prototype.do_fight = function (target) {
this.begin_attack(target, 1);
}
CHARACTER.prototype.do_kill = function (target) {
if (this.fight_type == 2 && this.query_enemy() == target) return;
this.begin_attack(target, 2);
target.begin_attack(this, 2);
target.notify("<hir>看起来" + this.name + "想杀死你!</hir>\n");
this.notify("<hir>看起来" + target.name + "想杀死你!</hir>\n");
}
CHARACTER.prototype.add_enemy = function (target) {
if (!this.enemy) {
this.enemy = [];
}
this.enemy.push(target);
}
CHARACTER.prototype.notify_hp = function (type, val) {
if (!this.environment) return;
var items = this.environment.items;
var str = null;
if (type) {
str = "{type:\"sc\",id:\"" + this.id + "\"," + type + ":" + val + "";
} else {
var ary = ["{type:\"sc\",id:\""];
ary.push(this.id);
ary.push("\",hp:");
ary.push(this.hp);
ary.push(",max_hp:");
ary.push(this.max_hp);
ary.push(",mp:");
ary.push(this.mp);
ary.push(",max_mp:");
ary.push(this.max_mp);
str = ary.join("");
}
var showdamage = type ? type === "hp" : false;
for (var i = 0; i < items.length; i++) {
var player = items[i];
if (player.is_player) {
if (showdamage && this.damages && player.query_setting('show_damage')) {
player.send(str + ",damage:" + (this.damages[player.id] || 0) + "}");
} else {
player.send(str + "}");
}
}
}
}
CHARACTER.prototype.add_hp = function (v) {
if (v > this.max_hp - this.hp) v = this.max_hp - this.hp;
else if (v < -this.hp) v = -this.hp;
if (!v) return 0;
this.hp += v;
this.notify_hp("hp", this.hp);
return v;
}
CHARACTER.prototype.add_mp = function (v) {
var mp = this.mp + v;
if (mp > this.max_mp) mp = this.max_mp;
if (mp < 0) mp = 0;
if (mp === this.mp) return;
this.mp = mp;
this.notify_hp("mp", this.mp);
return v;
}
CHARACTER.prototype.is_fighting = function (p) {
if (!this.fight_type) return false;
if (!this.enemy || !this.enemy.length) {
this.fight_type = 0;
this.clear_combat_status();
return false;
}
if (p) {
if (p.environment !== this.environment) return false;
return this.enemy.contain(p);
}
return true;
}
CHARACTER.prototype.end_fight = function () {
if (this.enemy) this.enemy.length = 0;
this.release_time = 0;
if (!this.fight_type) return;
this.send('{type:"combat",end:1}');
if (this.record_damage && this.hp > 0) this.damages = null;
this.fight_type = 0;
if (this.attack_handler) clearTimeout(this.attack_handler);
this.attack_handler = null;
this.clear_combat_status();
this.clear_combat_prop();
return false;
}
CHARACTER.prototype.query_enemy = function () {
if (!this.enemy) return;
for (var i = 0; i < this.enemy.length; i++) {
if (this.enemy[i].hp <= 0 || !this.is_here(this.enemy[i])
|| !this.enemy[i].fight_type) {
this.enemy.splice(i, 1);
i--;
}
}
return this.enemy[0];
}
CHARACTER.prototype.can_attack = function () {
return this.hp > 0 && this.fight_type > 0 && !this.is_faint && !this.is_busy;
}
CHARACTER.prototype.end_attack = function (target) {
if (!target) return;
if (!this.fight_type) return;
if (this.attack_skill.on_end_attack) {
this.attack_skill.on_end_attack(this, target);
}
if (this.fight_type === 1 && target.hp / target.max_hp < 0.3) {
if (target.hp <= 0) target.hp = 1;
if (target.is_faint) {
this.send_room(WINNER_MSG[this.random(4)], target);
} else {
this.send_room(WINNER_MSG.random(), target);
}
target.on_fight_over && target.on_fight_over(this, false);
this.on_fight_over && this.on_fight_over(target, true);
target.end_fight();
return this.end_fight();
} else if (target.hp <= 0 && target.die(this) !== false) {
target.end_fight();
this.enemy.remove(target);
if (!this.enemy.length) {
if (this.hp <= 0) {
this.hp = 1;
}
return this.end_fight();
}
}
return true;//是否继续攻击
}
CHARACTER.prototype.query_part = function () {
return CHARACTER_PARTS.random();
}
CHARACTER.prototype.full = function () {
this.hp = this.max_hp;
this.mp = this.max_mp;
this.clear_distime();
this.release_time = 0;
this.notify_hp();
}
CHARACTER.prototype.clear_distime = function (pfmid) {
if (this.auto_skills) {
if (!pfmid) {
for (let i = 0; i < this.auto_skills.length; i++) {
let item = this.auto_skills[i];
item.release_time = 0;
}
} else {
for (let i = 0; i < this.auto_skills.length; i++) {
let item = this.auto_skills[i];
if (item.pfm.id === pfmid) {
item.release_time = 0;
break;
}
}
}
}
}
CHARACTER.prototype.do_attacks = function (par) {
var targets = par.targets;
if (!targets) {
targets = new Array(this.enemy.length);
for (var i = 0; i < this.enemy.length; i++) {
targets[i] = this.enemy[i];
}
}
if (!targets.length) return;
var attack_msg = par.attack_msg;
if (attack_msg === undefined) attack_msg = (par.no_weapon ? this.noweapon_skill : this.attack_skill).query_attack_action(this, targets[0]);
if (attack_msg !== "") this.send_combat(attack_msg, targets[0]);
par.attack_msg = "";
for (let i = 0; i < targets.length; i++) {
par.target = targets[i];
this.do_attack(par);
this.end_attack(targets[i]);
}
}
var CHARACTER_PARTS = [
{ name: "左脚", hert: 0.8, crit: 0 },
{ name: "右脚", hert: 0.8, crit: 0 },
{ name: "左腿", hert: 0.85, crit: 0 },
{ name: "右腿", hert: 0.85, crit: 0 },
{ name: "小腹", hert: 0.91, crit: 3 },
{ name: "胸前", hert: 0.95, crit: 4 },
{ name: "后背", hert: 0.97, crit: 4 },
{ name: "头部", hert: 1.2, crit: 10 },
{ name: "颈部", hert: 1.1, crit: 5 },
{ name: "后心", hert: 1, crit: 4 },
{ name: "左肩", hert: 0.85, crit: 1 },
{ name: "右肩", hert: 0.89, crit: 1 },
{ name: "左手", hert: 0.85, crit: 0 },
{ name: "左手", hert: 0.85, crit: 0 },
{ name: "腰间", hert: 0.99, crit: 5 }
];
var WINNER_MSG = [
"<CYN>$N哈哈大笑愉快地说道承让了</CYN>",
"<CYN>$N双手一拱笑著说道知道我的厉害了吧</CYN>",
"<CYN>$N哈哈大笑双手一拱笑著说道承让</CYN>",
"<CYN>$N胜了这招向后跃开三尺笑道承让</CYN>",
"<CYN>$n脸色微变说道佩服佩服</CYN>",
"<CYN>$n向后退了几步说道这场比试算我输了佩服佩服</CYN>",
"<CYN>$n向后一纵躬身做揖说道阁下武艺不凡果然高明</CYN>",
];

485
os/char/follower.js Normal file
View File

@@ -0,0 +1,485 @@
require("../util/util.js");
require("./user.js");
FOLLOWER = function () {
this.hp = this.max_hp = 100;
this.mp = this.max_mp = 100;
this.str = this.con = this.dex = this.int = this.per = this.age = 20;
this.family = FAMILIES.NONE;
this.auto_pfm = true;
this.master = null;
this.level = 3;
this.master_name = null;
this.max_item_count = 10;
this.settings = {
auto_kill: 1,
auto_dice: 1
};
}
FOLLOWER.inherits(CHARACTER);
FOLLOWER.prototype.query_setting = function (name) {
if (!this.settings) return 0;
return this.settings[name] || 0;
}
FOLLOWER.prototype.set_setting = function (name, value) {
if (!this.settings) this.settings = {};
if (!value || value == "0") {
delete this.settings[name];
} else {
if (value == "1") value = 1;
this.settings[name] = value;
}
this.login_message = null;
}
FOLLOWER.prototype.send = function (text) {
if (this.listener) {
text = text.replace('你', this.name);
this.listener.send(text);
}
}
FOLLOWER.prototype.notify_fail = function (text) {
this.send(text);
return false;
}
FOLLOWER.prototype.notify = function (text) {
this.send(text);
}
FOLLOWER.prototype.set_listener = function (me, target) {
if (me.id == this.master) this.listener = target;
}
FOLLOWER.STORES = new Map();
FOLLOWER.CLEAR = function (me) {
if (!me.follower) return;
for (var i = 0; i < me.follower.length; i++) {
var npc = FOLLOWER.STORES.get(me.id + "_" + me.follower[i].id);
if (npc) {
FOLLOWER.STORES.delete(me.id + "_" + me.follower[i].id);
npc.set_state(null);
npc.environment && npc.environment.item_changed(npc, false);
npc.clear_status();
}
}
}
FOLLOWER.RESET = function (me) {
if (!me.follower) return;
for (var i = 0; i < me.follower.length; i++) {
var npc = FOLLOWER.STORES.get(me.id + "_" + me.follower[i].id);
if (npc) {
npc.set_state(null);
npc.environment && npc.environment.item_changed(npc, false);
}
}
}
FOLLOWER.INIT_FROM_USER = function (me, datas) {
for (var j = 0; j < datas.length; j++) {
var data = datas[j];
var my_npc = FOLLOWER.STORES.get(me.id + "_" + data.id);
if (my_npc) continue;
if (!datas[j].id) continue;
my_npc = new FOLLOWER();
var obj = NPC.CLONE(data.path);
for (var i = 0; i < SAVE_NUMPROP.length; i++) {
my_npc[SAVE_NUMPROP[i]] = data.prop[i] || 0;
}
for (var i = 0; i < SAVE_STRPROP.length; i++) {
my_npc[SAVE_STRPROP[i]] = data[SAVE_STRPROP[i]] || obj[SAVE_STRPROP[i]];
}
my_npc.on_makelove = obj ? obj.on_makelove : null;
my_npc.on_master_enter = obj ? obj.on_master_enter : null;
my_npc.equipment = data.temp;
my_npc.settings = data.settings;
my_npc.skills = data.skills;
my_npc.path = obj.path;
my_npc.items = me.read_items(data.items);
my_npc.equipment = me.read_items(data.eq);
my_npc.level = my_npc.level || obj.level || 3;
my_npc.init();
my_npc.recount();
my_npc.hp = my_npc.max_hp;
my_npc.mp = my_npc.max_mp;
FOLLOWER.STORES.set(me.id + "_" + my_npc.id, my_npc);
my_npc.master = me.id;
my_npc.master_name = me.name;
}
}
FOLLOWER.INIT = function (me, par) {
if (!par || !par.path) return;
var npc;
var id = me.id + "_" + par.id;
if (par.id) {
npc = FOLLOWER.STORES.get(id);
if (npc) return npc;
}
var obj = NPC.CLONE(par.path);
if (!obj) return;
npc = new FOLLOWER();
for (var i = 0; i < SAVE_NUMPROP.length; i++) {
npc[SAVE_NUMPROP[i]] = obj[SAVE_NUMPROP[i]] || 0;
}
for (var i = 0; i < SAVE_STRPROP.length; i++) {
npc[SAVE_STRPROP[i]] = obj[SAVE_STRPROP[i]] || "";
}
npc.on_makelove = obj.on_makelove;
npc.on_master_enter = obj.on_master_enter;
npc.equipment = obj.equipment;
npc.skills = obj.skills;
npc.items = obj.items;
npc.id = par.id;
npc.path = obj.path;
npc.level = obj.level || 3;
npc.init();
npc.recount();
FOLLOWER.STORES.set(id, npc);
npc.master = me.id;
npc.master_name = me.name;
return npc;
}
FOLLOWER.REPLACE = function (me, old, npc) {
if (!old || !npc) return;
var copys = ["str", "con", "dex", "int", "gender", "kar", "per", "name", "title", "desc", "on_master_learn", "on_master_enter"];
for (var i = 0; i < copys.length; i++) {
old[copys[i]] = npc[copys[i]];
}
if (!old.skills) old.skills = {};
if (!old.equipment) old.equipment = [];
if (npc.equipment && npc.equipment[0]) {
if (old.equipment[0]) {
npc.items.push(npc.equipment[0]);
} else {
old.equipment[0] = npc.equipment[0];
}
npc.equipment[0] = null;
}
if (!old.items) old.items = [];
if (npc.items && npc.items.length) {
for (var i = 0; i < npc.items.length; i++) {
old.items.push(npc.items[i]);
}
npc.items.length = 0;
}
for (var sk in npc.skills) {
var oldSkill = old.skills[sk];
if (oldSkill && oldSkill.addin && oldSkill.addin.length)
continue;//已经进阶的不覆盖
if (!oldSkill || oldSkill.level < npc.skills[sk].level) {
old.skills[sk] = npc.skills[sk];
}
}
if (npc.exp > old.exp) old.exp = npc.exp;
if (npc.pot > old.pot) old.pot = npc.pot;
if (npc.max_mp > old.max_mp) old.max_mp = npc.max_mp;
old.prop = {};
old.init();
if (old.status) {
for (var j = old.status.length - 1; j >= 0; j--) {
var item = old.status[j];
old.change_buff(item, true, item.count);
}
}
old.recount();
old.master_json = null;
old.color_name = null;
old.on_master_enter = npc.on_master_enter;
old.on_makelove = npc.on_makelove;
old.path = npc.path;
old.level = npc.level > old.level ? npc.level : old.level;
if (old.environment) {
old.environment.item_changed(old, true);
}
for (var i = 0; i < me.follower.length; i++) {
if (me.follower[i].id == old.id) {
me.follower[i].path = npc.path;
break;
}
}
}
FOLLOWER.GET = function (me, par) {
var id = me.id + "_" + par.id;
return FOLLOWER.STORES.get(id);
}
FOLLOWER.CREATE = function (me, par, callback) {
if (!par || !par.path) return;
var id = me.id + "_" + par.id;
var npc = FOLLOWER.STORES.get(id);
if (npc) return callback(npc);
}
var SAVE_NUMPROP = ["str", "con", "dex", "int", "gender", "max_mp", "limit_mp", "exp", "pot", "kar", "per"
, "hp", "mp", "max_item_count", "money", 'level'];
var SAVE_STRPROP = ["id", "name", "title", "desc"];
FOLLOWER.prototype.save = function (me) {
var str = ["prop:["];
for (var i = 0; i < SAVE_NUMPROP.length; i++) {
str.push(this[SAVE_NUMPROP[i]] || 0);
str.push(",");
}
str.push("0],");
var items = this.items || [];
str.push("items:[");
if (items) {
for (var i = 0; i < items.length; i++) {
if (i > 0) str.push(",");
items[i].save_db(str);
}
}
str.push("]");
if (this.skills) {
str.push(",skills:");
str.push(JSON.stringify(this.skills));
}
if (this.temp) {
str.push(",temp:", this.format_temp(this.temp));
}
if (this.settings) {
str.push(",settings:");
str.push(JSON.stringify(this.settings));
}
if (this.equipment) {
str.push(",eq:[");
for (var i = 0; i < this.equipment.length; i++) {
if (i > 0) str.push(",");
if (this.equipment[i]) this.equipment[i].save_db(str);
else str.push("null");
}
str.push("]");
}
return str.join("");
}
FOLLOWER.SAVE = function (me) {
if (!me.follower) return "[]";
var str = ["["];
for (var i = 0; i < me.follower.length; i++) {
var item = me.follower[i];
if (str.length > 1) str.push(",");
str.push('{path:"');
str.push(me.follower[i].path);
str.push('",id:"');
str.push(me.follower[i].id);
str.push('"');
var npc = FOLLOWER.STORES.get(me.id + "_" + item.id);
if (npc) {
str.push(",");
str.push(npc.save(me));
}
str.push('}');
}
str.push("]");
return str.join("");
}
var DIE_MSG = ["\n$N扑在地上挣扎了几下腿一伸口中喷出几口<HIR>鲜血</HIR>,死了!\n",
"\n$N大叫一声倒在地上挣扎了几下<HIR>死了</HIR>\n",
"\n$N口中喷出几口<HIR>鲜血</HIR>,倒在地上,死了!\n"];
FOLLOWER.prototype.die = function (killer) {
if (!this.environment) return;
if (this.on_die && this.on_die(killer) == false) {
this.hp = 1;
return false;
}
this.clear_status();
this.send_room(DIE_MSG.random());
var corpse = new CORPSE();
corpse.init(this, false);
this.environment.item_changed(corpse, true);
this.environment.item_changed(this, false);
this.environment = null;
}
FOLLOWER.prototype.heart_beat = function (dt) {
if (!this.hp) return;
// this.add_exp(this.grow_level, this.grow_level);
if (!this.fight_type) {
if (this.hp < this.max_hp) {
this.add_hp(parseInt(this.max_hp / 3));
}
if (this.mp < this.max_mp) {
this.add_mp(parseInt(this.max_mp / 3));
}
if (this.chat_msg) {
var r = this.random(10);
if (r > 7)
this.send_message(this.chat_msg.random());
}
}
if (this.state && (!this.fight_type || this.state.allow_fight)) {
this.state.heat_count += 1;
if (this.state.heat_count >= this.state.rate) {
this.state.heat_count = 0;
if (this.state.on_enter(this) === false) {
this.set_state(null, true);
}
}
}
}
FOLLOWER.prototype.set_state = function (state, isauto) {
if (this.state && !state) {
if (this.state.on_stop) {
if (this.state.on_stop(this, isauto) == false) {
return;
}
}
}
this.state = state;
if (state) {
state.rate = state.rate || 1;
state.heat_count = 0;
state.start_time = Date.now();
}
this.color_name = null;
this.environment && this.environment.item_changed(this, true);
this.master_json = null;
}
FOLLOWER.prototype.query_mastercommands = function () {
if (this.master_json) return this.master_json;
var json = {};
json.type = "item";
json.desc = this.desc;
json.id = this.id;
json.name = this.name;
json.commands = [];
json.commands.push({
cmd: "look " + this.id,
name: "查看"
});
json.commands.push({
cmd: "fight " + this.id,
name: "比试"
});
json.commands.push({
cmd: "score " + this.id,
name: "属性"
});
json.commands.push({
cmd: "pack " + this.id,
name: "背包"
});
json.commands.push({
cmd: "cha " + this.id,
name: "技能"
});
json.commands.push({
cmd: "team with " + this.id,
name: "组队"
});
json.commands.push({
cmd: "trade " + this.id,
name: "给" + this.call3() + "东西"
});
if (this.state) {
json.commands.push({
cmd: "dc " + this.id + " stopstate",
name: "停止" + this.state.title.replace('中', "")
});
}
if (this.actions) {
for (var i = 0; i < this.actions.length; i++) {
json.commands.push({
cmd: this.actions[i].cmd,
name: this.actions[i].name
});
}
}
this.master_json = JSON.stringify(json)
return this.master_json;
}
FOLLOWER.prototype.query_commands = function (player) {
if (player.id == this.master) {
return this.query_mastercommands(player);
}
if (this.json) return this.json;
var json = {};
json.type = "item";
json.desc = this.desc;
json.id = this.id;
json.follower = true;
json.commands = [];
json.commands.push({
cmd: "look " + this.id,
name: "查看"
});
if (!this.no_fight)
json.commands.push({
cmd: "fight " + this.id,
name: "比试"
});
json.commands.push({
cmd: "kill " + this.id,
name: "击杀"
});
json.commands.push({
cmd: "ask " + this.id + " about 主人",
name: "询问主人"
});
this.json = JSON.stringify(json)
return this.json;
}
FOLLOWER.prototype.on_ask = function (me, par) {
switch (par) {
case "主人":
me.notify(this.name + "说道:我的主人就是" + this.master_name + "呀。");
break;
}
}
FOLLOWER.prototype.on_teamin = function (me) {
if (!this.team) return;
for (var i = 0; i < this.team.length; i++) {
var tm = this.team[i];
if (this.master == tm.id) {
this.do_follow(tm);
}
}
}
FOLLOWER.prototype.on_teamout = function (me) {
if (!this.team) return;
for (var i = 0; i < this.team.length; i++) {
var tm = this.team[i];
if (this.master == tm.id) {
this.do_follow(null);
if (this.environment && this.environment.is_fb()) {
this.environment.item_changed(this, false, this.name + "离开了。");
}
}
}
}
FOLLOWER.prototype.long_name = function () {
if (this.color_name) return this.color_name;
var str = [];
if (this.title) {
str.push(this.title);
str.push(" "); this.name;
}
str.push(this.name);
if (this.state) {
str.push("<hig>&lt;" + this.state.title + "&gt;</hig>");
}
return this.color_name = str.join("");
}
FOLLOWER.prototype.on_enter = function (me) {
if (me.id == this.master) {
this.on_master_enter && this.on_master_enter(me);
}
}
FOLLOWER.prototype.on_master_leave = function (me, nextrm) {
if (this.state || !this.team || this.team != me.team) return false;
if (this.hp <= 0) return false;
if (me.environment === this.environment) return false;
//如果去副本或者家里就跟
if (nextrm.is_fb() || nextrm.parent.id == "home") {
return true;
}
//如果当前是副本就自己回去
//if (this.environment.is_fb())
// this.environment.item_changed(this, false, this.name + "离开了。");
return false;
}

114
os/char/monster.js Normal file
View File

@@ -0,0 +1,114 @@
require("../util/util.js");
require("./character");
MONSTER = function () {
this.hp = this.max_hp = 100;
this.mp = this.max_mp = 100; this.auto_pfm = true;
this.family = FAMILIES.MONSTER;
this.str = this.con = this.dex = this.int = 20;
}
MONSTER.inherits(CHARACTER);
MONSTER.prototype.can_speek = false;
MONSTER.prototype.init_skill = function () {
this.attack_skill = this.query_used_skill(BASE_SKILLS.BITE);
this.dodge_skill = this.query_used_skill(BASE_SKILLS.DODGE);
this.parry_skill = this.query_used_skill(BASE_SKILLS.PARRY);
this.force_skill = this.query_used_skill(BASE_SKILLS.FORCE);
this.noweapon_skill = this.attack_skill;
}
MONSTER.prototype.query_desc = function (me) {
return this.query_commands(me);
}
MONSTER.prototype.query_commands = function (player) {
if (this.json) return this.json;
var json = {};
json.type = "item";
json.desc = this.name + "\n" + this.desc;
json.id = this.id;
json.commands = [];
json.commands.push({
cmd: "kill " + this.id,
name: "击杀"
});
if (this.actions) {
for (var cmd in this.actions) {
if (!this.actions[cmd].name) continue;
json.commands.push({
cmd: cmd + " " + this.id,
name: this.actions[cmd].name
});
}
}
this.json = JSON.stringify(json)
return this.json;
}
MONSTER.prototype.call3 = function () {
return "它";
}
MONSTER.prototype.destroy = function (msg) {
if (this.environment) {
this.environment.item_changed(this, false, msg);
}
}
MONSTER.prototype.die = function (killer) {
if (!this.environment) return;
if (this.on_die && this.on_die(killer) === false) {
this.hp = 1;
return false;
}
this.hp = 0;
this.clear_status();
this.send_message(this.name + "惨嚎一声,死了!");
var corpse = new CORPSE();
var isinfb = this.environment.is_fb();
corpse.init(this, isinfb);
this.die_room = this.environment;
this.environment.item_changed(corpse, true);
this.environment.item_changed(this, false);
if (isinfb && this.score && killer) {
//副本分数
killer.add_fbscore(this.score);
}
this.on_died && this.on_died(killer, corpse);
WORLD.auto_get(killer, corpse, this);
if (killer && killer.attack_skill && killer.attack_skill.on_enemy_die) {
killer.attack_skill.on_enemy_die(killer, this);
}
}
MONSTER.prototype.query_part = function () {
return MONSTER_PARTS.random();
}
MONSTER.prototype.heart_beat = function (dt) {
if (!this.fight_type && this.hp > 0) {
if (this.hp < this.max_hp) {
this.add_hp(parseInt(this.max_hp / 2));
}
if (this.mp < this.max_mp) {
this.add_mp(parseInt(this.max_mp / 2));
}
if (this.chat_msg) {
var r = this.random(10);
if (r > 5)
this.send_message(this.chat_msg.random());
}
}
}
var MONSTER_PARTS = [
{ name: "左爪", hert: 0.8, crit: 0 },
{ name: "右爪", hert: 0.8, crit: 0 },
{ name: "后腿", hert: 0.85, crit: 0 },
{ name: "小腹", hert: 0.91, crit: 3 },
{ name: "胸前", hert: 0.95, crit: 4 },
{ name: "背部", hert: 0.97, crit: 4 },
{ name: "头部", hert: 1.2, crit: 10 },
{ name: "颈部", hert: 1.1, crit: 5 },
{ name: "前肢", hert: 0.85, crit: 1 },
{ name: "腰间", hert: 0.99, crit: 5 },
];

227
os/char/npc.js Normal file
View File

@@ -0,0 +1,227 @@
NPC = function () {
this.hp = this.max_hp = 100;
this.mp = this.max_mp = 100;
this.str = this.con = this.dex = this.int = this.per = this.age = 20;
this.family = FAMILIES.NONE;
this.auto_pfm = true;
}
NPC.inherits(CHARACTER);
NPC.prototype.set_chat_msg = function (items, chance) {
if (items) {
this.chat_msg = items;
//this.chat_chance = chance || 10;
}
}
NPC.prototype.do_chat_msg = function () {
if (!this.is_fighting() && this.is_living && this.chat_msg) {
this.do_say(this.chat_msg.random());
}
}
NPC.prototype.format_equipments = function (call3, str, eqcmd) {
if (this.equipment && this.equipment.length) {
var eqstr = [];
for (var i = 0; i < this.equipment.length; i++) {
var item = this.equipment[i];
if (!item) continue;
eqstr.push("<span cmd='", eqcmd || "look", " ", (i),
" of ", this.id, "'>◆", item.color_name, "</span>\n");
}
if (eqstr.length) {
return str.push(call3, "装备着:\n", eqstr.join(""));
}
}
str.push(call3, "穿着一件<wht>布衣</wht>。\n");
}
NPC.prototype.set_goods = function () {
if (!arguments.length) return;
this.sell_list = [];
for (var i = 0; i < arguments.length; i++) {
var item = arguments[i];
var obj = OBJ.CREATE(item);
if (!obj) continue;
obj.count = -1;
this.sell_list.push(obj);
}
}
NPC.prototype.query_commands = function (player) {
if (this.json) return this.json;
this.json = this.query_commands_json(player, false);
return this.json;
}
NPC.prototype.query_commands_json = function (player, isyb) {
var json = {};
json.type = "item";
json.desc = this.desc;
json.id = this.id;
json.name = this.name;
json.commands = [];
json.commands.push({
cmd: "look " + this.id,
name: "查看"
});
if (!this.no_fight)
json.commands.push({
cmd: "fight " + this.id,
name: "比试"
});
json.commands.push({
cmd: "kill " + this.id,
name: "击杀"
});
if (this.on_master) {
json.commands.push({
cmd: "bai " + this.id,
name: "拜师"
});
}
if (this.on_checkskill || this.on_master) {
json.commands.push({
cmd: "cha " + this.id,
name: "学习"
});
json.master = true;
}
if (this.sell_list) {
json.commands.push({
cmd: "list " + this.id,
name: "购买"
});
json.trader = true;
}
if (this.question) {
for (var cmd in this.question) {
json.commands.push({
cmd: "ask " + this.id + " about " + cmd,
name: "询问" + cmd
});
}
}
if (this.actions) {
for (var cmd in this.actions) {
if (!this.actions[cmd].name) continue;
json.commands.push({
cmd: cmd + " " + this.id,
name: this.actions[cmd].name
});
}
}
return JSON.stringify(json);
}
NPC.prototype.update_action = function (acts) {
this.json = null;
this.actions = acts;
}
var DIE_MSG = ["\n$N扑在地上挣扎了几下腿一伸口中喷出几口<HIR>鲜血</HIR>,死了!\n",
"\n$N大叫一声倒在地上挣扎了几下<HIR>死了</HIR>\n",
"\n$N口中喷出几口<HIR>鲜血</HIR>,倒在地上,死了!\n"];
NPC.prototype.die = function (killer) {
if (!this.environment) return;
if (this.on_die && this.on_die(killer) == false) {
this.hp = 1;
return false;
}
this.hp = 0;
this.clear_status();
this.send_room(DIE_MSG.random());
this.clear_follow();
var corpse = new CORPSE();
var isinfb = this.environment.is_fb();
corpse.init(this, isinfb);
this.die_room = this.environment;
this.environment.item_changed(corpse, true);
this.environment.item_changed(this, false);
if (isinfb && this.score && killer && killer.add_fbscore) {
//副本分数
killer.add_fbscore(this.score);
}
if (!isinfb && !this.no_refresh && !this.master && !this.die_room.is_shadow) {
this.call_out(this.relive, this.on_master ? 60000 : 300000);
}
this.on_died && this.on_died(killer, corpse);
WORLD.auto_get(killer, corpse, this);
if (killer && killer.attack_skill && killer.attack_skill.on_enemy_die) {
killer.attack_skill.on_enemy_die(killer, this);
}
}
NPC.prototype.relive = function () {
if (!this.die_room) return;
var room = ROOM.Get(this.die_room.path);
var obj = room.find_obj_bypath(this.path);
if (obj) return;
obj = NPC.CLONE(this.path);
room.item_changed(obj, true);
this.die_room = null;
this.equipment = null;
this.items = null;
this.skills = null;
}
NPC.prototype.destroy = function (msg) {
if (this.environment) {
this.environment.item_changed(this, false, msg);
}
this.clear_follow();
}
NPC.prototype.heart_beat = function (dt) {
if (!this.fight_type) {
if (this.hp < this.max_hp) {
this.add_hp(parseInt(this.max_hp / 2));
}
if (this.mp < this.max_mp) {
this.add_mp(parseInt(this.max_mp / 2));
}
if (this.chat_msg) {
if (this.random(10) > 8)
this.send_message(this.chat_msg.random());
}
this.on_heart_beat && this.on_heart_beat(dt);
} else if (this.hp <= 0) {
var eny = this.query_enemy();
if (!eny) {
this.hp = 1;
this.fight_type = 0;
}
}
}
NPC.CREATE = function (path, env, oncreate, count) {
if (!path || !env) return;
if (env.environment) env = env.environment;
count = count || 1;
let obj = null;
for (let i = 0; i < count; i++) {
obj = NPC.CLONE(path);
env.item_changed(obj, true);
if (oncreate) oncreate(obj);
}
return obj;
}
NPC.CLONE = function (path) {
let base = NPC.GET(path);
let item = Object.create(base);
item.clone();
return item;
}
NPC.GET = function (path) {
let base = WORLD.NPC_STROE.get(path);
if (!base) {
base = BASE.CREATE(__PATH.NPC, path);
if (!base) throw new Error('没有人物' + path + "的定义。");
//这里会自己调用create方法存储到NPC_STROE记住了吗
}
return base;
}

845
os/char/user.js Normal file
View File

@@ -0,0 +1,845 @@
require("./character.js");
USER = function () {
this.socket = null;
this.family = FAMILIES.NONE;
this.max_item_count = 20;
this.max_store_count = 20;
this.money = 0;
this.request_count = 0;
this.cash_money = 0;
this.score = 0;
this.follower = null;
this.password = "";
this.loginTime = 0;
this.id_address = null;
this.user_level = 0;
this.eq_group = 0;
};
USER.inherits(CHARACTER);
USER.prototype.is_player = true;
USER.prototype.notify = function (text) {
//玩家不能接收消息的状态 不发送
if (this.socket && !this.is_faint && text && text.length < 30240)
this.socket.send(text);
}
USER.prototype.send = function (text) {
//直接发送消息 不管玩家状态
if (this.socket && text && text.length < 30240) {
this.socket.send(text);
}
}
USER.prototype.notify_fail = function (text) {
if (this.socket && !this.is_faint)
this.socket.send(text);
return false;
}
USER.prototype.send_warn = function (content, cmds, time) {
var str = ["{type:\"warn\",content:\""];
str.push(content);
str.push("\"");
if (time) {
str.push(",time:");
str.push(time);
}
str.push(",cmds:[");
for (var i = 0; i < cmds.length; i += 2) {
if (i > 0) str.push(",");
str.push("{cmd:\"");
str.push(cmds[i]);
str.push("\",name:\"");
str.push(cmds[i + 1]);
str.push("\"}");
}
str.push("]}");
this.send(str.join(""));
}
USER.prototype.send_commands = function () {
var str = ["{type:\"cmds\",items:["];
for (var i = 0; i < arguments.length; i += 2) {
if (i > 0) str.push(",");
str.push("{cmd:\"");
str.push(arguments[i]);
str.push("\",name:\"");
str.push(arguments[i + 1]);
str.push("\"}");
}
str.push("]}");
this.send(str.join(""));
}
USER.prototype.is_connect = function () {
return this.socket !== null;
}
USER.prototype.send_loginmessage = function () {
if (!this.login_message) {
var str = ['{type:"login"'];
if (this.settings) {
str.push(",setting:")
str.push(JSON.stringify(this.settings));
this.no_message = this.settings['no_message'] == 1;
}
str.push(",id:\"");
str.push(this.id, '",level:', this.level);
str.push("}");
this.login_message = str.join("");
}
this.send(this.login_message)
}
USER.prototype.relogin = function (newUser) {
if (!newUser.socket) return;
newUser.socket.user = null;
this.socket = newUser.socket;
newUser.socket = null;
this.socket.user = this;
this.send_loginmessage();
if (!this.environment) {
var rm = ROOM.Get(this.quit_room);
if (!rm) {
return this.send("出现错误请联系管理员报告BUG谢谢");
}
this.environment = rm;
}
this.send(this.environment.to_json());
this.environment.send_exits(this);
this.send(this.environment.items_to_json());
// if (!WORLD.is_end_cross(this)) {
this.send_room(this.name + "重新连线。");
if (this.environment.on_relogin) {
this.environment.on_relogin(this);
}
// }
this.disconnect_time = 0;
this.check_state();
this.on_skillchanged();
}
USER.prototype.ip = function () {
return this.socket.remoteAddress;
}
USER.prototype.port = function () {
return this.socket.remotePort;
}
USER.prototype.quit = function () {
var rm = this.environment;
if (this.environment) {
this.team_out("离开了游戏,自动退出队伍");
this.environment.item_changed(this, false, this.name + "离开了游戏。");
this.environment = rm;
this.clear_follow();
this.environment.clear_copy(this);
this.environment.parent.on_leaved(this);
}
this.environment = null;
this.clear_status();
this.environment = rm;
WORLD.login_out(this);
this.environment = null;
this.clear_home();
if (this.socket) {
this.socket.user = null;
this.socket = null;
}
}
USER.prototype.in_world = function () {
//判断是否连线进入过游戏,
return !!this.environment && !!this.socket;
}
USER.prototype.disconnect = function (isreplace) {
if (this.environment && this.socket) {
// this.send_message(this.name + "断线了。");
if (isreplace)
this.send("<RED>有人使用你的角色从别的地址登陆游戏,请重新登陆</RED>");
}
this.disconnect_time = Date.now();
if (this.socket) {
let socket = this.socket;
this.socket = null;
socket.user = null;
socket.end();
//if (!socket.destroyed)
// socket.destroy();
}
}
USER.prototype.loadData = function (role) {
this.id = role.id;
this.name = role.name;
this.level = role.level;
//this.title = role.title;de
//role.data = role.data.toString();
var data = JSON.toObject(role.data);
for (var i = 0; i < SAVE_NUMPROP.length; i++) {
this[SAVE_NUMPROP[i]] = data.prop[i] || 0;
}
this.quit_room = data.quit_room;
this.items = this.read_items(data.items);
this.stores = this.read_items(data.stores);
this.books = data.books ?? [];
this.equipment = this.read_items(data.eq);
this.settings = data.settings;
this.skills = data.skills ?? {};
this.eq_groups = data.eq_groups;
this.sk_groups = data.sk_groups ?? [null, [], []];
this.temp = data.temp;
this.read_titles(data.titles);
if (data.follower) {
this.follower = [];
FOLLOWER.INIT_FROM_USER(this, data.follower);
for (var i = 0; i < data.follower.length; i++) {
this.follower.push({
id: data.follower[i].id,
path: data.follower[i].path
});
}
}
var fam = this.query_temp("family");
if (fam) {
this.family = FAMILIES[fam] || FAMILIES.NONE;
}
this.user_level = role.user_level;
}
USER.prototype.read_titles = function (titles) {
this.titles = [];
if (!titles) return;
for (let item of titles) {
this.titles.push({
title: item[0], type: item[1],
use: item[2] === 1
});
if (item[2]) {
this.title = item[0];
}
}
}
USER.prototype.read_items = function (items) {
var objs = [];
if (!items) return objs;
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item) {
objs.push(null);
continue;
}
var obj = OBJ.CREATE(item[0]);
if (obj) {
obj.load_db(item);
obj.on_load(this);
objs.push(obj);
}
}
return objs;
}
USER.prototype.do_login = function () {
this.init();
this.recount();
this.long_name();
WORLD.STATS.checkStats(this);
this.send_loginmessage();
if (this.family) this.family.on_login(this);
var rm = ROOM.Get(this.query_temp("new") ? "new/new1" : this.quit_room);
if (!rm || rm.is_fb()) rm = ROOM.Get(DEFAULT_ROOM);
if (rm.is_copy()) {
var copy_room = rm.query_copy2(this);
if (copy_room) {
this.moveto(copy_room, null, this.name + "连线进入这个世界。");
} else {
if (this.query_temp("new")) {
//还没完成新手教程的,从头开始
this.set_temp("new", 1);
this.items = [];//清理掉之前的物品
this.exp = this.pot = this.money = 0;
}
copy_room = rm.create_copy2(this);
this.moveto(copy_room);
}
//如果在副本副本下线,不可能
} else {
this.moveto(rm, null, this.name + "连线进入这个世界。");
}
this.check_state();
}
var DEFAULT_ROOM = "yz/wumiao";
var SAVE_NUMPROP = ["str", "con", "dex", "int", "gender", "max_mp", "limit_mp", "exp", "pot", "kar", "per"
, "hp", "mp", "max_item_count", "money", "reg_time",
"max_store_count", "cash_money", 'eq_group'];
USER.prototype.getData = function () {
var str = ["{prop:["];
for (var i = 0; i < SAVE_NUMPROP.length; i++) {
str.push(this[SAVE_NUMPROP[i]]);
str.push(",");
}
str.push(0);
str.push("],quit_room:\"");
if (this.environment) {
if (this.environment.is_fb() || this.environment.no_save
|| this.environment.parent.no_save) {
str.push(this.query_temp("enter_room"));
} else {
str.push(this.environment.path);
}
} else {
str.push(this.query_temp("enter_room", DEFAULT_ROOM));
}
str.push("\"");
var items = this.items;
if (items) {
str.push(",items:[");
for (let i = 0; i < items.length; i++) {
if (i > 0) str.push(",");
items[i].save_db(str);
}
str.push("]");
}
items = this.stores;
if (items) {
str.push(",stores:[");
for (let i = 0; i < items.length; i++) {
if (i > 0) str.push(",");
items[i].save_db(str);
}
str.push("]");
}
items = this.books;
if (items && items.length > 0) {
str.push(',books:["', items.join('", "'), '"]');
}
if (this.skills) {
str.push(",skills:");
str.push(JSON.stringify(this.skills));
}
str.push(",temp:", this.format_temp(this.temp));
if (this.settings) {
str.push(",settings:");
str.push(JSON.stringify(this.settings));
}
if (this.equipment) {
str.push(",eq:[");
for (var i = 0; i < this.equipment.length; i++) {
if (i > 0) str.push(",");
if (this.equipment[i]) this.equipment[i].save_db(str);
else str.push("null");
}
str.push("]");
}
if (this.titles) {
str.push(",titles:[");
for (var i = 0; i < this.titles.length; i++) {
if (i > 0) str.push(",");
var item = this.titles[i];
str.push('["', item.title, '","', item.type, '"');
if (item.use) str.push(',1');
str.push(']');
}
str.push("]");
}
if (this.follower) {
str.push(",follower:");
str.push(FOLLOWER.SAVE(this));
}
str.push(',eq_groups:[');
for (let i = 0; i < this.eq_groups.length; i++) {
if (i > 0) str.push(',');
if (i === this.eq_group || !this.eq_groups[i]) str.push('[]');
else str.push('["', this.eq_groups[i].join('","'), '"]');
}
str.push('],sk_groups:[');
for (let i = 0; i < this.sk_groups.length; i++) {
if (i > 0) str.push(',');
if (!this.sk_groups[i]) str.push('0');
else str.push('["', this.sk_groups[i].join('","'), '"]');
}
str.push("]}");
var role = {};
role.id = this.id;
role.userid = this.userid;
role.name = this.name;
role.level = this.level;
role.title = this.title || this.get_level_desc();
role.data = str.join("");
return role;
}
USER.prototype.save = function () {
WORLD.DB.saveRole(this.getData());
}
USER.prototype.die = function (killer) {
if (this.on_die && this.on_die(killer) === false) {
this.hp = 1;
return false;
}
this.clear_status();
this.hp = 0;
this.mp = 0;
this.send_room(DIE_MSG.random());
var env = this.environment;
if (env.items.length < 10) {
var corpse = new CORPSE();
corpse.init(this);
env.item_changed(corpse, true);
}
env.item_changed(this, false);
this.environment = env;
this.check_state();
WORLD.on_user_die(this, killer);
this.on_died(killer);
}
USER.prototype.on_died = function () {
}
USER.prototype.check_state = function () {
if (this.hp <= 0) {
if (this.state) this.set_state(null);
this.send('{type:"die",commands:[{cmd:"relive",name:"去武庙复活"},{cmd:"relive locale",name:"原地复活"}]}');
} else {
if (this.state) this.set_state(this.state);
}
}
USER.prototype.query_commands = function (player) {
if (this.commands_json) return this.commands_json;
var json = {};
json.type = "item";
json.desc = this.long_name();
json.id = this.id;
json.commands = [];
json.commands.push({
cmd: "look " + this.id,
name: "查看"
});
if (player != this) {
if (!this.no_fight)
json.commands.push({
cmd: "fight " + this.id,
name: "比试"
});
json.commands.push({
cmd: "kill " + this.id,
name: "击杀"
});
json.commands.push({
cmd: "team add " + this.id,
name: "邀请组队"
});
if (this.level > 1 && !this.query_setting("ban_master") && !this.query_temp("tudi") && !this.query_temp("shifu")) {
json.commands.push({
cmd: "baishi " + this.id,
name: "拜师"
});
}
}
this.commands_json = JSON.stringify(json)
return this.commands_json;
}
USER.prototype.query_title = function (type) {
if (!this.titles) return null;
for (var i = 0; i < this.titles.length; i++) {
if (this.titles[i].type == type) {
return this.titles[i].title;
}
}
}
USER.prototype.add_title = function (title, type) {
if (!this.titles) this.titles = [];
var obj = { title: title, type: type };
for (var i = 0; i < this.titles.length; i++) {
if (this.titles[i].type == type) {
obj.use = this.titles[i].use;
this.titles.splice(i, 1);
break;
}
}
if (obj.title) {
if (!this.titles.length) obj.use = true;
this.titles.push(obj);
}
if (obj.use) {
if (!title) {
if (this.titles.length) {
this.titles[0].use = true;
title = this.titles[0].title;
}
}
this.title = title;
this.color_name = null;
if (this.environment)
this.environment.item_changed(this, true);
}
}
USER.prototype.query_setting = function (name) {
if (!this.settings) return 0;
return this.settings[name] || 0;
}
USER.prototype.set_setting = function (name, value) {
if (!this.settings) this.settings = {};
if (!value || value == "0") {
delete this.settings[name];
} else {
if (value == "1") value = 1;
this.settings[name] = value;
}
this.login_message = null;
}
USER.prototype.heart_beat = function (dt) {
this.request_count = 0;
if (this.state && (!this.fight_type || this.state.allow_fight)) {
this.state.heat_count += 1;
if (this.state.heat_count >= this.state.rate) {
this.state.heat_count = 0;
if (this.state.on_enter(this, dt) === false) {
this.set_state(null, true);
}
}
}
this.on_heart_beat && this.on_heart_beat(dt);
if (this.disconnect_time) {
//如果断线 在挂机就一天没有就5分钟下线
if (dt - this.disconnect_time > (this.state ? 86400000 : 3600000)) {
return this.quit();
}
}
}
USER.prototype.set_state = function (state, isauto) {
if (this.state && !state) {
if (this.state.on_stop) {
if (this.state.on_stop(this, isauto) == false) {
return;
}
}
this.send("{type:\"state\"}");
}
this.state = state;
if (state) {
state.rate = state.rate || 1;
state.heat_count = 0;
state.start_time = Date.now();
var msg = "{type:\"state\",state:\"你正在" + state.title + "\"";
if (state.desc) {
msg += ",desc:" + state.desc;
}
if (state.no_stop) {
msg += ",no_stop:true";
}
if (state.commands) {
msg += ",commands:" + state.commands;
}
this.send(msg + "}");
}
this.color_name = null;
if (this.environment)
this.environment.item_changed(this, true);
}
USER.prototype.get_state = function () {
var str = "";
if (!this.socket) str += "<red>&lt;断线中&gt;</red>";
if (this.state) str += ("<hig>&lt;" + this.state.title + "&gt;</hig>");
return str;
}
const LEVELS_TITLES = ["普通百姓", "武士", "武师", "宗师", "武圣", "武帝", "武神"];
USER.prototype.long_name = function () {
if (!this.color_name) {
var cc = this.get_level_color();
var str = [];
if (cc) {
str.push("<");
str.push(cc);
str.push(">");
}
if (this.title) {
str.push(this.title);
str.push(" ");
}
if (!this.title || this.level > 0) {
str.push(LEVELS_TITLES[this.level]);
str.push(" ");
}
str.push(this.name);
if (cc) {
str.push("</");
str.push(cc);
str.push(">");
}
this.color_name = str.join("");
this.commands_json = null;
}
return this.color_name + this.get_state();
}
USER.prototype.init_tasks = function () {
for (var i = 0; i < WORLD.TASKS.length; i++) {
var task = WORLD.TASKS[i];
task.on_start && task.on_start(this);
}
}
USER.prototype.query_jingli = function () {
var expend = this.query_temp("ex_jl") || 0;
return 200 - expend + (this.query_temp("add_jl") || 0);
}
const jclimits = [1000, 2000, 3000, 5000, 7000, 10000, 15000];
USER.prototype.query_jclimit = function () {
return jclimits[this.level] || 1000;
}
USER.prototype.add_obj = function (obj, count) {
if (!obj) return;
if (typeof obj == "string") {
obj = OBJ.clone_to(obj, this, count);
if (!obj) return;
} else {
obj = this.push_item(obj);
}
this.items_changed(obj);
obj.notify_action(this, true);
return obj;
}
USER.prototype.remove_obj = function (obj, count) {
if (typeof obj == "string") {
obj = this.find_obj(obj);
}
if (!obj) return;
count = count || obj.count || 1;
var newobj = this.remove_item(obj, count);
if (newobj == obj) {
obj.notify_action(this, false);
}
this.items_changed(obj, count);
return newobj;
}
USER.prototype.items_changed = function (item, drop_count) {
if (drop_count) {
this.send('{type:"dialog",dialog:"pack",id:"' + item.id + '",remove:' + drop_count + ',money:' + this.money + '}');
} else {
if (item.is_money) {
return this.send('{type:"dialog",dialog:"pack",money:' + this.money + '}');
}
var str = ['{type:"dialog",dialog:"pack",'];
str.push('name:"');
str.push(item.color_name);
str.push('",id:"');
str.push(item.id);
str.push('",count:');
str.push(item.count);
str.push(',grade:');
str.push(item.grade);
str.push(',unit:"');
str.push(item.unit);
str.push('"');
if (item.is_equipment) {
str.push(',can_eq:1');
}
if (item.on_use) {
str.push(',can_use:1');
}
if (item.on_study) {
str.push(',can_study:1');
}
if (item.on_open) {
str.push(',can_open:1');
}
if (item.combine_count) {
str.push(',can_combine:' + item.combine_count);
}
str.push(',value:');
str.push(item.transable ? item.value : 0);
str.push(",money:");
str.push(this.money);
str.push('}');
this.send(str.join(""));
}
}
//初始化人物使用的技能
USER.prototype.on_skillchanged = function () {
var str = ["{type:\"perform\",skills:["];
if (this.skills) {
var bases = ["", "force", "unarmed", "dodge", "parry", "throwing"];
var weapon = this.query_weapon_type(), base_type = null;
if (weapon != WEAPON_TYPE.NONE) bases[0] = weapon;
for (var i = 0; i < bases.length; i++) {
base_type = bases[i];
if (!base_type) continue;
var base_skill = this.skills[base_type];
if (base_skill) {
var sp_skill = SKILL.get(base_skill.enable_skill || base_type), pfmitem = null;
if (sp_skill && sp_skill.pfm) {
let sk_level = this.query_skill(base_skill.enable_skill || base_type, 0);
for (var p in sp_skill.pfm) {
pfmitem = sp_skill.pfm[p];
if (pfmitem.check && !pfmitem.check(this,
sk_level, base_type)) continue;
if (pfmitem.enable_skill && pfmitem.enable_skill != base_type) continue;
if (str.length > 1) str.push(",");
str.push("{id:\"");
str.push(base_type + "." + p);
str.push("\",name:\"");
str.push(pfmitem.query_name(this, base_type));
str.push("\"");
if (pfmitem.distime) {
str.push(",distime:");
str.push(pfmitem.query_distime(this));
}
str.push("}");
}
}
pfmitem = this.query_ref_skill(this.skills[base_skill.enable_skill]);
if (pfmitem && pfmitem.enable_skill && pfmitem.enable_skill == bases[i]) {
if (str.length > 1) str.push(",");
str.push("{id:\"");
str.push(bases[i] + ".ref");
str.push("\",name:\"");
str.push(pfmitem.query_name(this, base_type));
str.push("\"");
if (pfmitem.distime) {
str.push(",distime:");
str.push(pfmitem.query_distime(this, this.query_skill(base_skill.enable_skill), true));
}
str.push("}");
}
}
}
}
str.push("]");
str.push("}");
this.send(str.join(""));
}
USER.prototype.go_home = function () {
let my_room = this.query_home();
this.moveto(my_room, this.name + "向里面走去。");
}
USER.prototype.query_home = function (rm_name) {
let home = this.query_temp("home");
if (!home) return null;
if (!rm_name) rm_name = home == 1 ? "home/danjian" : "home/yuanzi";
let rm = ROOM.Get(rm_name);
let my_room = rm.query_copy2(this);
if (!my_room) {
my_room = rm.create_copy2(this);
}
return my_room;
}
USER.prototype.add_score = function (val) {
if (!val) return;
this.score += val;
WORLD.STATS.updateScore(this);
}
USER.prototype.add_money = function (val) {
let money = parseInt(this.money + val);
if (!(money >= 0)) return false;
this.money = money;
//this.send(`{"type":"dialog","dialog":"pack","money":${money}}`);
return true;
}
USER.prototype.add_cash = function (count, desc) {
if (!(count > 0 || count < 0)) return;
this.cash_money += count;
WORLD.log(this, count, desc);
if (count >= 0) {
this.notify("<hio>你获得了" + count + "元宝。</hio>");
}
this.send(`{"type":"dialog","dialog":"shop","money":[${this.money},${this.cash_money}]}`);
}
USER.prototype.query_cash = function (is_cash) {
return this.cash_money;
}
USER.prototype.can_follow = function (npc) {
if (!this.follower) this.follower = [];
var max = this.query_temp("max_follower") || 3;
if (this.follower.length >= max) return false;
for (var i = 0; i < this.follower.length; i++) {
if (this.follower[i].path == npc.path) {
return false;
}
}
return true;
}
USER.prototype.add_follower = function (npc) {
if (!this.can_follow(npc)) return false;
var item = {
path: npc.path,
id: npc.id
};
this.follower.push(item);
FOLLOWER.INIT(this, item);
return true;
}
USER.prototype.clear_home = function (clear_follower = true) {
var home = ROOM.Get("home/yuanzi");
if (home) {
home = home.query_copy(this.id)
if (home)
home.clear_copy(this);
}
if (clear_follower)
FOLLOWER.CLEAR(this);
else
FOLLOWER.RESET(this);
}
USER.prototype.clear_distime = function (pfmid) {
if (!this.temp) return;
if (pfmid) {
this.temp["pfm/" + pfmid] = null;
this.send('{type:"clearDistime",id:"' + pfmid + '"}');
} else {
for (var key in this.temp) {
if (key.startsWith("pfm/")) {
this.temp[key] = null;
}
}
this.send('{type:"clearDistime"}');
}
}
var DIE_MSG = ["\n$N扑在地上挣扎了几下腿一伸口中喷出几口<HIR>鲜血</HIR>,死了!\n",
"\n$N大叫一声倒在地上挣扎了几下<HIR>死了</HIR>\n",
"\n$N口中喷出几口<HIR>鲜血</HIR>,倒在地上,死了!\n"];
USER.prototype.add_combat_prop = function (name, val) {
this.add_prop(name, val);
if (!this.combat_props) this.combat_props = [];
this.combat_props.push([name, val]);
}
USER.prototype.clear_combat_prop = function (name, val) {
if (this.combat_props) {
for (let i = 0; i < this.combat_props.length; i++) {
this.add_prop(this.combat_props[i][0], -this.combat_props[i][1]);
}
this.combat_props = null;
this.recount();
this.notify_hp();
}
}

47
os/command.js Normal file
View File

@@ -0,0 +1,47 @@
/*
所有命令的基类,
自动加载所有定义在__COMMAND文件夹下的命令文件
*/
COMMAND = function () {
this.allow_fight = true;
this.allow_level = 0;
}
COMMAND.inherits(BASE);
//将命令绑定到某些对象上 调用obj.do_commandname();
COMMAND.prototype.for_item = function (item, name) {
if (this.exec) {
name = name || this.command;
item.prototype["do_" + name] = this.exec;
}
}
COMMAND.prototype.create = function (fname) {
// console.error("command %s success", fname);
if (this.command) {
var str = this.command.split(',');
for (var i = 0; i < str.length; i++) {
if (WORLD.COMMANDS[str[i]]) console.error("command %s 重复", fname);
WORLD.COMMANDS[str[i]] = this;
}
} else {
console.error("command %s not have command name", fname);
}
}
COMMAND.prototype.update = function () {
if (this.command) {
var str = this.command.split(',');
for (var i = 0; i < str.length; i++) {
WORLD.COMMANDS[str[i]] = this;
}
} else {
console.error("command %s not have command name", fname);
}
}
COMMAND.DO = function (cmd, par1, par2, par3) {
var cmd = WORLD.COMMANDS[cmd];
if (cmd) {
cmd.enter(null, par1, par2, par3);
}
}

116
os/const.js Normal file
View File

@@ -0,0 +1,116 @@
SKILL_TYPES = {
BASE: 0,
SKILL: 1,
KNOWLEDGE: 2
};
BASE_SKILLS = {
FORCE: "force",
DODGE: "dodge",
PARRY: "parry",
BITE: "bite"
};
EQUIP_TYPE = {
WEAPON: 0,
CLOTH: 1,
SHOES: 2,
HEAD: 3,
CAPE: 4,
RING: 5,
NECKLACE: 6,//项链
JEWELS: 7,//饰品
WRIST: 8,//护腕
WAIST: 9,//腰带
THROWING: 10//
}
WEAPON_TYPE = {
NONE: "unarmed",
SWORD: "sword",
BLADE: "blade",
STAFF: "staff",
CLUB: "club",
WHIP: "whip",
THROWING: "throwing"
}
PROPERTIES = {
"con1": "先天根骨",
"dex1": "先天身法",
"int1": "先天悟性",
"str1": "先天臂力",
"con": "根骨",
"dex": "身法",
"int": "悟性",
"str": "臂力",
"fy": "防御",
"per": "容貌",
"age": "年龄",
gj: "攻击",
ds: "躲闪",
zj: "招架",
mz: "命中",
bj_per: "暴击",
limit_mp: "内力上限",
gjsd: "攻击速度",
gjsd_per: "攻击速度",
mz_per: "命中",
max_hp: "气血",
max_mp: "内力",
releasetime: "绝招释放时间",
distime: "绝招冷却时间",
expend_mp: "内力消耗",
releasetime_per: "绝招释放时间",
distime_per: "绝招冷却时间",
expend_mp_per: "内力消耗",
add_sh_per: "最终伤害",
add_bjsh_per: "暴击伤害",
diff_sh_per: "伤害减免",
diff_sh: "受到的伤害减少",
diff_fy_per: "忽视对方防御",
fy_per: "防御",
zj_per: "招架",
gj_per: "攻击",
ds_per: "躲闪",
hp_per: "气血",
study_per: "学习效率",
dazuo_per: "打坐效率",
lianxi_per: "练习效率",
busy: "忙乱时间",
busy_per: "忙乱时间",
diff_busy: "忽视忙乱",
diff_busy_per: "忽视忙乱",
diff_bj: "暴击抵抗",
add_sh: "伤害增加",
diff_downside: "负面状态抵抗",
diff_downside_per: "负面状态抵抗",
dazuo: "打坐效率",
diff_sh_per2: "伤害减免",
diff_fy_per2: "伤害减免",
recover_per: "疗伤效果",
lianyao1: "炼药效率",
lianyao2: "丹药产出",
lianyao_exp_per: "炼药获得经验",
no_fy: "无法防御",
no_pfm: "禁止绝招",
kuang_exp: "挖矿经验",
kuang_pot: "挖矿潜能",
diaoyu_exp: "钓鱼经验",
diaoyu_pot: "钓鱼潜能",
diaoyu1: "钓鱼效率",
kuang1: "挖矿效率",
caiyao1: "采药效率",
caiyao_exp: "采药经验",
caiyao_pot: "采药潜能",
xiulian_exp: "闭关经验",
shuangxiu: "双修效率",
fenjie: "分解获得的玄晶",
};

123
os/data.js Normal file
View File

@@ -0,0 +1,123 @@
module.exports = {
parties: new Map(),
PAIMAI: new Map(),
temp: {},
save: function () {
let str = ["{"];
this.save_temp(str);
this.on_save(str);
str.push('}');
return WORLD.DB.saveData(str.join(""));
},
temp_replacer: function (key, value) {
if (value.e) {
}
return value;
},
save_temp: function (str) {
str.push('temp:', JSON.stringify(this.temp));
},
load: async function () {
const data = await WORLD.DB.readData(__PATH.DATA + "data.js");
this.temp = data.temp ?? {};
this.on_load(data);
},
query_temp: function (name, def) {
if (!this.temp) return;
let item = this.temp[name];
if (item && item.e) {
if (Date.now() <= item.e) {
return item.v;
}
delete this.temp[name];
return def;
}
return item || def;
},
set_temp: function (name, value, time) {
if (!this.temp) this.temp = {};
if (time) {
this.temp[name] = {
v: value,
e: Date.now() + time
};
} else {
this.temp[name] = value;
}
},
remove_temp: function (name) {
if (!this.temp) return;
this.temp[name] = null;
},
add_temp: function (name, value, time) {
if (!this.temp) this.temp = {};
let old = this.temp[name];
if (time) {
if (old && old.e) {
time = Date.now() + time;
if (old.e < Date.now()) {
old.e = time;
old.v = value;
} else {
if (old.e < time) old.e = time;
old.v += value;
}
return old.v;
} else {
let v = value + (old || 0);
this.temp[name] = {
v: v,
e: Date.now() + time
};
return v;
}
} else {
let v = value + (old || 0);
this.temp[name] = v;
return v;
}
},
temp_data: {},
clear_data: function () {
this.temp_data = {};
}
,
add_data: function (key, user, val) {
if (!val) return;
let data = this.temp_data[key];
if (!data) data = this.temp_data[key] = {};
let user_data = data[user.id];
if (!user_data) user_data = data[user.id] = { name: user.name, value: 0 };
user_data.value += val;
}, query_max_data: function (key) {
let data = this.temp_data[key];
if (!data) return;
let userData = null;
for (let key in data) {
let item = data[key];
if (!userData) userData = item;
else if (item.value > userData.value) {
userData = item;
}
}
return userData;
}, query_min_data: function (key) {
let data = this.temp_data[key];
if (!data) return;
let userData = null;
for (let key in data) {
let item = data[key];
if (!userData) userData = item;
else if (item.value < userData.value) {
userData = item;
}
}
return userData;
}
};

250
os/item.js Normal file
View File

@@ -0,0 +1,250 @@
require("./util/util");
ITEM = function () {
}
ITEM.inherits(BASE);
ITEM.prototype.heart_beat = function (dt) {
}
ITEM.prototype.init = function () {
}
//物件对命令的反应有几种方式实现
//1. 直接实现ON_XXXXcmd里定义的有直接对象的命令推荐这种比如on_accept是当别人给你东西时候on_checkskill当别人学你技能时适合目标明确的操作
//2. add_action方式房间里的指令推荐用这种虽然也可以实现1但是因为调用add_action时候还没ID适合房间的自定义命令和已定义的有目标的命令
//3. on(xxx)方式,对房间里的命令的反映,适合回应目标不是当前对象,或没有目标的命令
//actions里定义的动作会作为对象的可用操作发送出去
//添加物件可以接收的命令,目标是当前对象的
ITEM.prototype.add_action = function (cmd, name, func) {
if (!cmd) return;
if (!this.actions) this.actions = {};
var act = this.actions[cmd];
if (act) {
if (!name) {
act.action = func;
} else {
act.name = name;
}
if (!func) {
act.name = name;
} else {
act.action = func;
}
} else {
act = {
name: name,
action: func
};
this.actions[cmd] = act;
}
this.json = null;
return act;
}
//移除物件可以接收的命令
ITEM.prototype.remove_action = function (name, func) {
if (!this.actions) this.actions = {};
if (typeof (name) === "string") {
delete this.actions[name];
} else {
for (var i = 0; i < name.length; i++) {
delete this.actions[name[i]];
}
}
this.json = null;
}
//执行命令 返回true表示已经完成命令后续不需要执行
ITEM.prototype.exec = function (cmdName, pars) {
if (this.actions) {
var cmd = this.actions[cmdName];
if (cmd && cmd.action) {
return cmd.action.apply(this, pars);
}
}
return false;
}
ITEM.prototype.max_item_count = 10;
ITEM.prototype.item_count = function () {
return this.items ? this.items.length : 0;
}
ITEM.prototype.is_full = function (val) {
if (!this.items) return false;
if (val) return this.items.length + val > this.max_item_count;
return this.items.length >= this.max_item_count;
}
ITEM.prototype.find_obj = function (id) {
return null;
}
ITEM.prototype.find_obj_bypath = function (path, parent) {
parent = parent || this;
if (!parent.items) return;
for (var i = 0; i < parent.items.length; i++) {
if (parent.items[i].path === path) {
return parent.items[i];
}
}
}
ITEM.prototype.each_item = function (func, parent) {
if (!func) return;
parent = parent || this;
var l = parent.items.length;
for (var i = 0; i < l; i++) {
var item = parent.items[i];
if (!item) continue;
if (func(item) === false) return;
}
};
ITEM.prototype.is = function (obj) {
if (!obj) return false;
if (typeof obj === "string")
return this.path === obj;
return this.path === obj.path;
}
ITEM.prototype.remove_item_byid = function (obj, count = 0) {
if (!obj || !this.items) return;
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (item.id === obj) {
if (item.combined && count > 0) {
var subitem = item.uncombine(count);
if (!subitem) return;
if (subitem === item) {
this.items.splice(i, 1);
}
return subitem;
} else {
this.items.splice(i, 1);
return item;
}
}
}
return null;
}
ITEM.prototype.remove_item = function (obj, count) {
if (!obj || !this.items) return;
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (item === obj) {
if (item.combined) {
var subitem = item.uncombine(count);
if (!subitem) return;
if (subitem === item) {
this.items.splice(i, 1);
}
return subitem;
} else {
this.items.splice(i, 1);
return item;
}
}
}
return null;
}
ITEM.prototype.move_item_to = function (obj, count, target) {
if (!obj || !this.items) return;
var moved_obj = this.remove_item(obj, count);
if (!target) return;
return target.push_item(moved_obj);
}
ITEM.prototype.push_item = function (moved_obj) {
if (!moved_obj) return;
if (!this.items) this.items = [];
if (moved_obj.is_money && this.money !== undefined) {
this.money += moved_obj.value * moved_obj.count;
} else if (moved_obj.combined) {
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].is(moved_obj)) {
this.items[i].combine(moved_obj);
return this.items[i];
}
}
this.items.push(moved_obj);
} else {
this.items.push(moved_obj);
}
return moved_obj;
}
ITEM.prototype.create_id = function () {
this.id = UTIL.create_id();
}
ITEM.prototype.refresh = function () {
this.json = null;
}
ITEM.prototype.is_hidden = function () {
return false;
}
ITEM.prototype.query_create_time = function () {
var id = this.id;
if (!id) return;
var time = parseInt(id.substr(4), 16);
return new Date(time * 1000 + UTIL.begin);
}
ITEM.prototype.find_obj_byid = function (items, oid) {
if (!items) return;
for (var i = 0; i < items.length; i++) {
if (items[i].id === oid) {
return items[i];
}
}
}
ITEM.prototype.long_name = function () {
return this.name;
}
ITEM.prototype.create = function (file, ctor) {
this.uid = this.create_uid();
}
ITEM.prototype.destroy = function () {
}
ITEM.prototype.format_temp = function (temp, timeout = 120000) {
if (!temp) return "{}";
var dt = Date.now() + timeout;
var tmp = ["{"];
for (var key in temp) {
var v = temp[key];
if (!v) continue;
if (v.e) {
if (dt > v.e || !v.v) continue;
if (tmp.length > 1) tmp.push(",");
tmp.push("\"");
tmp.push(key);
tmp.push("\":{e:");
tmp.push(v.e);
tmp.push(",v:");
if (typeof v.v == "string") {
tmp.push("\"");
tmp.push(v.v);
tmp.push("\"");
} else {
tmp.push(v.v);
}
tmp.push("}");
} else {
if (tmp.length > 1) tmp.push(",");
tmp.push("\"");
tmp.push(key);
tmp.push("\":");
if (typeof v == "string") {
tmp.push("\"");
tmp.push(v);
tmp.push("\"");
} else {
tmp.push(v);
}
}
}
tmp.push("}");
return tmp.join("");
}

69
os/item/container.js Normal file
View File

@@ -0,0 +1,69 @@
require("./obj.js");
CONTAINER = function () {
this.count = 1;
this.combined = false;
}
CONTAINER.inherits(OBJ);
CONTAINER.prototype.is_container = true;
CONTAINER.prototype.on_get = function () {
return false;
}
CONTAINER.prototype.set_items = function () {
for (var i = 0; i < arguments.length; i++) {
var item = arguments[i];
if (item) {
if (typeof item == "string") {
OBJ.clone_to(item, this);
} else if (item.length) {
OBJ.clone_to(item[0], this,item[1]);
}
}
}
}
CONTAINER.prototype.query_items = function () {
return this.items;
}
//这个单纯的字符串描述
CONTAINER.prototype.get_desc = function (me) {
var str = [this.color_name, this.desc];
var items = this.query_items(me);
if (items && items.length) {
str.push("它里面有:");
for (var i = 0; i <items.length; i++) {
var item = items[i];
str.push("\t" + UTIL.to_c(item.count) + item.unit + item.color_name);
}
} else {
str.push("它里面什么都没有。");
}
return str.join("\n");
}
CONTAINER.prototype.clear_items = function (me) {
this.items.length = 0;
}
//这个提供给LOOK SELECT命令的
CONTAINER.prototype.query_desc = function (me) {
if (this.json) return this.json;
var obj = {};
obj.type = "item";
obj.id = this.id;
obj.desc = this.get_desc(me);
obj.commands = [];
obj.commands.push({
cmd: "get all from " + this.id,
name: "全部拾取"
});
this.json = JSON.stringify(obj)
return this.json;
}
CONTAINER.CREATE = function (name,desc,lv,odds) {
var obj = OBJ.CREATE("sp/box#lv");
obj.items = OBJ.create_by_odds(odds);
obj.name = name;
obj.desc = desc || obj.desc;
obj.grade = lv;
obj.create();
return obj;
}

71
os/item/corpse.js Normal file
View File

@@ -0,0 +1,71 @@
require("../item/obj.js");
CORPSE = function () {
this.unit = "具";
this.count = 1;
this.no_alloc = false;
}
CORPSE.inherits(CONTAINER);
CORPSE.prototype.on_get = function (player) {
return false;
}
CORPSE.prototype.init = function (player, iskeep) {
this.create_id();
this.fromid = player.id;
this.name = player.name + "的尸体";
this.color_name = "<wht>" + this.name + "</wht>";
this.environment = player.environment;
this.desc = "然而" + player.call3() + "已经死了,只剩下一具尸体静静地躺在这里。";
this.items = player.query_drop();
if (!iskeep) this.call_out(this.disappear, 60000);
}
CORPSE.prototype.query_items = function (player) {
if (this.environment.is_fb() && player.team) {
if (!this.no_drops) {
this.no_drops = [];
for (var i = 0; i < player.team.length; i++) {
if (!this.environment.query_temp(player, player.team[i].id)) {
this.no_drops.push(player.team[i].id);
}
}
this.on_getitem = this.check_get;
}
}
return this.items;
}
CORPSE.prototype.clear_items = function (me, noget) {
this.items.length = 0;
if (noget && noget.length) this.items = noget;
}
CORPSE.prototype.check_get = function (player, item) {
if (!this.no_drops) return true;
if (this.no_drops.indexOf(player.id) == -1) return true;
player.notify('你不可以拾取' + item.color_name + "。");
return false;
}
CORPSE.prototype.disappear = function () {
if (this.items) this.items.length = 0;
if (this.environment) {
this.environment.notify("一阵风吹去," + this.name + "已经不见了。");
this.environment.item_changed(this, false);
}
}
CORPSE.prototype.query_desc = function (me) {
if (this.json) return this.json;
var obj = {};
obj.type = "item";
obj.desc = this.get_desc(me);
obj.id = this.id;
obj.commands = [];
obj.commands.push({
cmd: "get all from " + this.id,
name: "全部拾取"
});
if (this.no_alloc) {
return JSON.stringify(obj);
}
this.json = JSON.stringify(obj);
return this.json;
}

450
os/item/equipment.js Normal file
View File

@@ -0,0 +1,450 @@
require("../util/util.js");
EQUIPMENT = function () {
this.eq_type = EQUIP_TYPE.WEAPON;
this.level = 0;
this.exp = 0;
this.grade = 0;
this.count = 1;
this.combined = false;
this.showAction = true;
this.allow_fight = true;
this.otype = 4;
}
EQUIPMENT.inherits(OBJ);
EQUIPMENT.prototype.is_equipment = true;
EQUIPMENT.prototype.transable = true;
//EQUIPMENT.prototype.eq_msg = "$N装备上$n。";
//EQUIPMENT.prototype.uneq_msg = "$N脱下$n。";
EQUIPMENT.prototype.change_prop = function (me, is_attach) {
me.change_prop(this.prop, is_attach);
if (this.st_prop) {
for (var i = 0; i < this.st_prop.length; i++) {
me.change_prop(this.st_prop[i].prop, is_attach);
}
}
}
EQUIPMENT.prototype.notify_action = function (me, isadd) {
if (!this.on_use) return;
isadd = me.equipment[this.eq_type] == this;
if (isadd)
me.send("{type:'addAction',id:'" + this.id + "',name:'" + this.name + "',distime:" + (this.distime || 0) + "}");
else
me.send("{type:'removeAction',id:'" + this.id + "'}");
}
EQUIPMENT.prototype.check = function (me) {
if (!this.condition) return true;
for (var key in this.condition) {
var val = this.condition[key];
switch (key) {
case "skill":
for (var sk in val) {
if (me.query_skill(sk, 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[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 ? "男性" : "女性") + ",无法装备" + 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;
}
}
return true;
}
EQUIPMENT.prototype.eq = function (me, notsend) {
if (this.check(me) == false) {
return false;
}
if (this.on_eq && this.on_eq(me) == false) {
return false;
}
this.change_prop(me, true);
this.check_group(me, true);
//me.add_score(this.query_score());
if (!notsend) {
if (this.eq_msg)
me.send_room(this.eq_msg, this);
else {
var msg;
switch (this.eq_type) {
case EQUIP_TYPE.WEAPON:
msg = "$N抽出一" + this.unit + this.color_name + "拿在手上。";
break;
case EQUIP_TYPE.CLOTH:
case EQUIP_TYPE.SHOES:
case EQUIP_TYPE.PANTS:
msg = "$N穿上一" + this.unit + this.color_name + "。";
break;
case EQUIP_TYPE.RING:
msg = "$N拿出一" + this.unit + this.color_name + "戴在手上。";
break;
case EQUIP_TYPE.NECKLACE:
case EQUIP_TYPE.JEWELS:
case EQUIP_TYPE.WRIST:
msg = "$N戴上一" + this.unit + this.color_name + "。";
break;
default:
msg = "$N装备上一" + this.unit + this.color_name + "。";
break;
}
me.send_room(msg, this);
}
}
me.send('{type:"dialog",dialog:"pack",id:"' + this.id + '",eq:' + this.eq_type + '}');
}
EQUIPMENT.prototype.uneq = function (me, notsend) {
this.on_uneq && this.on_uneq(me);
this.change_prop(me, false);
this.check_group(me, false);
//me.add_score(-this.query_score());
if (!notsend) {
if (this.uneq_msg)
me.send_room(this.uneq_msg, this);
else {
var msg;
switch (this.eq_type) {
case EQUIP_TYPE.WEAPON:
msg = "$N收回手中的" + this.color_name + "。";
break;
case EQUIP_TYPE.CLOTH:
case EQUIP_TYPE.SHOES:
case EQUIP_TYPE.PANTS:
case EQUIP_TYPE.WRIST:
msg = "$N将" + this.color_name + "脱了下来。";
break;
case EQUIP_TYPE.RING:
case EQUIP_TYPE.NECKLACE:
case EQUIP_TYPE.JEWELS:
msg = "$N将" + this.color_name + "取了下来。";
break;
default:
msg = "$N脱下一" + this.unit + this.color_name + "。";
break;
}
me.send_room(msg, this);
}
}
me.send('{type:"dialog",dialog:"pack",id:"' + this.id + '",uneq:' + this.eq_type + '}');
}
EQUIPMENT.prototype.condition_tostring = function (str) {
if (!this.condition) return;
for (var key in this.condition) {
var val = this.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(desc);
break;
case "gender":
str.push("性别要求:" + (val == 1 ? "男" : "女"));
break;
default:
str.push(PROPERTIES[key] + "要求:" + val);
break;
}
str.push("\n");
}
}
EQUIPMENT.prototype.parts = ['武器', '衣服', '鞋', '头部', '披风', '戒指', '项链', '饰品', '护腕', '腰带', '暗器'];
EQUIPMENT.prototype.qualities = ["普通", "精良", "高级", "稀有", "绝世", "传说", "神器"];
EQUIPMENT.prototype.get_desc = function (me) {
var str = [this.color_name];
str.push("\n");
str.push(this.parts[this.eq_type]);
//str.push("\n");
//str.push(this.query_quality());
str.push("\n");
this.condition_tostring(str);
if (this.desc) str.push(this.desc);
str.push("\n");
if (this.prop) {
str.push("<");
str.push(this.query_grade_color());
str.push(">");
str.push(UTIL.prop_toString(this.prop));
str.push("</");
str.push(this.query_grade_color());
str.push(">\n");
}
if (this.st_prop) {
for (var i = 0; i < this.st_prop.length; i++) {
str.push(this.st_prop[i].name);
str.push("\n");
}
}
if (this.hole_count) {
for (var i = 0; i < this.hole_count; i++) {
str.push("◇");
}
}
this.query_group_desc(me, str);
return str.join("");
}
EQUIPMENT.prototype.query_quality = function () {
return this.qualities[this.grade];
}
const level_desc = ["", "☆", "★", "★☆", "★★", "★★☆", "★★★",
"★★★☆", "★★★★", "★★★★☆", "★★★★★", "★★★★★☆", "★★★★★★"];
EQUIPMENT.prototype.level_up = function (lev) {
var cc = this.query_grade_color();
this.prop = {};
this.level = lev;
this.levelchange_prop();
this.color_name = "<" + cc + ">" + level_desc[this.level] + this.name + "</" + cc + ">";
this.json = null;
}
EQUIPMENT.prototype.levelData = [
0, 10, 20, 40, 70, 110, 160, 220, 290, 370, 460, 560, 670
];
EQUIPMENT.prototype.levelchange_prop = function () {
if (!(this.level >= 0 && this.level < 13)) return;
const base_props = this.original_prop ?? Object.getPrototypeOf(this).prop;
var val = this.levelData[this.level];
for (var key in base_props) {
var value = base_props[key];
switch (key) {
case "desc":
case "str1":
case "con1":
case "dex1":
case "int1":
case "per":
case "kar":
case "skill":
this.prop[key] = value;
break;
case "fy_per":
case "zj_per":
case "mz_per":
case "hp_per":
case "ds_per":
case "gj_per":
case "diff_busy":
case "busy_per":
case "caiyao1":
case "diaoyu1":
case "kuang1":
case "lianyao1":
case "diff_sh":
case "expend_mp":
this.prop[key] = value + parseInt(value * val / 1000);
break;
case "diff_downside_per":
case "gjsd":
case "releasetime":
case "distime":
case "diff_downside":
case "distime_per":
case "releasetime_per":
case "gjsd_per":
case "bj_per":
case "diff_bj":
case "add_bjsh_per":
case "add_sh_per":
case "diff_busy_per":
case "diff_sh_per":
case "diff_fy_per":
case "expend_mp_per":
case "busy":
this.prop[key] = value;
break;
default:
if (PROPERTIES[key])
this.prop[key] = value + parseInt(value * val / 100);
else
this.prop[key] = value;
break;
}
}
}
EQUIPMENT.prototype.clear_stone = function () {
if (!this.st_prop) return;
this.hole_count += (this.st_prop.length);
this.st_prop.length = 0;
}
EQUIPMENT.prototype.push_stone = function (stone) {
if (!stone || !stone.prop) return false;
if (!this.hole_count) return false;
if (!this.st_prop) this.st_prop = [];
this.hole_count--;
var cc = stone.query_grade_color();
var str = ["<", cc, ">◆", stone.name, " "];
str.push(UTIL.prop_toString(stone.prop, " "));
str.push("</");
str.push(cc);
str.push(">");
this.json = null;
this.st_prop.push({
id: stone.id,
path: stone.path,
name: str.join(""),
prop: stone.prop,
grade: stone.grade
});
}
EQUIPMENT.prototype.clone = function (me) {
var obj = OBJ.CREATE(this.path);
if (this.temp) {
obj.temp = {};
for (var key in this.temp) {
obj.temp[key] = this.temp[key];
}
}
obj.on_reload && obj.on_reload(me);
obj.level_up(this.level);
obj.st_prop = this.st_prop;
return obj;
}
EQUIPMENT.prototype.save_db = function (str) {
str.push('["', this.path, '","', this.id, '",', this.level);
if (this.st_prop && this.st_prop.length) {
str.push(",[");
for (var i = 0; i < this.st_prop.length; i++) {
if (i > 0) str.push(",");
str.push('"', this.st_prop[i].path, '"');
}
str.push("]");
}
// else {
// str.push(',[]');
// }
if (this.is_locked)
str.push(',1');
if (this.temp)
str.push(",", this.format_temp(this.temp));
str.push("]");
}
EQUIPMENT.prototype.load_db = function (data) {
//const [path, id, level, sts, locked,temp] = data;
this.id = data[1];
if (data[2] > 0) {
this.level = data[2];
}
for (let i = 3; i < data.length; i++) {
let value = data[i];
if (value === 1) this.is_locked = true;
else if (Array.isArray(value)) {
for (var j = 0; j < value.length; j++) {
var st_item = OBJ.CREATE(value[j]);
if (st_item) {
this.push_stone(st_item);
}
}
} else if (typeof value === 'object') {
this.temp = value;
}
}
}
EQUIPMENT.prototype.on_load = function (me) {
this.on_reload && this.on_reload(me);
if (this.level > 0) {
this.level_up(this.level);
}
}
EQUIPMENT.prototype.VALUES = [100, 1000, 2000, 10000, 100000, 1000000, 100000000];
EQUIPMENT.prototype.on_create = function (path, par) {
this.value = this.VALUES[this.grade];
}
EQUIPMENT.prototype.query_group_desc = function (me, str) {
if (!this.group_prop || !this.group_name) return;
var count = 0;
if (me && me.equipment) {
for (var i = 0; i < me.equipment.length; i++) {
if (me.equipment[i] && me.equipment[i].group_name == this.group_name) {
count++;
}
}
}
for (var i = 2; i < 8; i++) {
var prop = this.group_prop(i);
if (prop) {
var cc = i <= count ? this.query_grade_color() : "blk";
str.push("<");
str.push(cc);
str.push(">");
str.push("\n");
str.push(UTIL.to_c(i));
str.push("件套:");
str.push(UTIL.prop_toString(prop, " "));
str.push("</");
str.push(cc);
str.push(">");
}
}
}
EQUIPMENT.prototype.check_group = function (me, isadd) {
if (!this.group_prop || !this.group_name) return;
var count = isadd ? 1 : 0;
for (var i = 0; i < me.equipment.length; i++) {
if (me.equipment[i] && me.equipment[i].group_name == this.group_name) {
count++;
}
}
var prop = this.group_prop(count);
if (prop) {
me.change_prop(prop, isadd);
}
}

25
os/item/money.js Normal file
View File

@@ -0,0 +1,25 @@
require("../item/obj.js");
MONEY = function () {
this.is_cash = false;
this.combined = true;
this.count = 1;
}
MONEY.inherits(OBJ);
MONEY.prototype.is_money = true;
MONEY.prototype.transable = true;
MONEY.prototype.create = function () {
this.create_id();
if (this.is_cash) {
this.color_name = "<hio>" + this.name + "</hio>";
} else {
if (this.value == 1)
this.color_name = "<yel>" + this.name + "</yel>";
else if (this.value == 100)
this.color_name = "<hiw>" + this.name + "</hiw>";
else
this.color_name = "<hiy>" + this.name + "</hiy>";
}
}

228
os/item/obj.js Normal file
View File

@@ -0,0 +1,228 @@
OBJ = function () {
this.unit = "个";
this.path = null;
this.count = 1;
this.combined = true;
this.grade = 0;
this.otype = 0;
}
OBJ.inherits(ITEM);
OBJ.prototype.transable = false;
OBJ.prototype.init = function (me) {
this.on_init && this.on_init(me);
}
OBJ.prototype.long_name = function () {
if (this.combined) return UTIL.to_c(this.count) + this.unit + this.color_name;
return this.color_name;
}
OBJ.prototype.unit_name = function (count) {
return UTIL.to_c(count || this.count) + this.unit + this.color_name;
}
OBJ.prototype.item_to_json = function () {
return `["${this.name}","${this.id}",${this.count},${this.grade},"${this.unit}",${parseInt(this.value / 10)},${this.is_equipment ? 1 : 0},${this.on_use ? 1 : 0},${this.on_study ? 1 : 0},${this.on_open ? 1 : 0},${this.combine_count > 0 ? this.combine_count : 0}]`;
}
OBJ.prototype.query_commands = function (me) {
return this.query_desc(me);
}
OBJ.prototype.query_desc = function (me) {
if (this.json) return this.json;
var obj = {};
obj.type = "item";
obj.id = this.id;
obj.desc = this.get_desc(me);
obj.commands = [];
obj.commands.push({
cmd: "get " + this.id,
name: "捡起"
});
this.json = JSON.stringify(obj)
return this.json;
}
OBJ.prototype.get_desc = function () {
return this.color_name + "\n" + this.desc;
}
OBJ.prototype.uncombine = function (spcount) {
if (!spcount || spcount === this.count) return this;
if (spcount < this.count) {
var item = this.clone();
item.count = spcount;
this.count -= spcount;
return item;
}
}
OBJ.prototype.clone = function () {
var item = OBJ.CREATE(this.path);
if (this.temp) {
item.temp = Object.assign({}, this.temp);
}
return item;
}
OBJ.prototype.combineTemp = function (target, source) {
if (!source) return target;
if (!target) return source;
for (let key in source) {
let val = source[key];
if (val && typeof val === "number") {
let thisVal = target[key];
if (!thisVal) {
target[key] = val;
} else if (typeof thisVal === "number") {
target[key] = thisVal > val ? thisVal : val;
}
}
}
return target;
}
OBJ.prototype.combine = function (obj) {
if (this.is(obj)) {
if (obj.temp || this.temp) {
this.temp = this.combineTemp(this.temp, obj.temp);
}
this.count += (obj.count || 0);
}
}
OBJ.clone_to = function (otype, to, count) {
if (!otype || !to) return;
var item = OBJ.CREATE(otype);
if (!item) return;
count = count || 1;
if (item.is_money && to.money != undefined) {
item.count = count;
to.money += item.value * item.count;
return item;
}
if (!to.items) to.items = [];
if (item.combined) {
for (var i = 0; i < to.items.length; i++) {
if (to.items[i].is(item)) {
to.items[i].count += count;
return to.items[i];
}
}
item.count = count;
to.items.push(item);
} else {
to.items.push(item);
for (var i = 0; i < count - 1; i++) {
item = OBJ.CREATE(otype, 1);
to.items.push(item);
}
}
return item;
}
OBJ.prototype.save_db = function (str) {
str.push('["', this.path, '","', this.id, '",', this.count);
if (this.is_locked) {
str.push(',1');
}
if (this.temp)
str.push(",", this.format_temp(this.temp));
str.push(']');
}
OBJ.prototype.load_db = function (data) {
//path, id, count, temp or lock
this.id = data[1];
if (data[2] > 1) this.count = data[2];
if (data[3]) {
if (data[3] === 1) {
this.is_locked = true;
} else if (typeof data[3] === 'object') {
this.temp = data[3];
return;
}
}
if (data[4]) this.temp = data[4];
}
OBJ.prototype.on_load = function (me) {
this.on_reload && this.on_reload(me);
}
OBJ.prototype.on_clone = function () {
}
OBJ.CREATE = function (otype, count) {
let base = WORLD.OBJ_STROE.get(otype);
if (!base) {
base = BASE.CREATE(__PATH.OBJ, otype);
if (!base) throw new Error('没有物品' + otype + "的定义。");
//这里会自己调用create方法存储到OBJ_STROE记住了吗
}
// var item = BASE.CREATE(__PATH.OBJ, otype);
// if (!item) return;
let item = Object.create(base);
item.create_id();
item.on_clone();
if (count > 1)
item.count = count;
return item;
}
var grade_color = ["wht", "hig", "hic", "hiy", "HIZ", "hio", "ord"];
OBJ.prototype.create = function (path, par) {
if (par) this.path = path + par;
this.create_id();
this.on_create && this.on_create(path, par);
var cc = grade_color[this.grade];
this.color_name = "<" + cc + ">" + this.name + "</" + cc + ">";
WORLD.OBJ_STROE.set(this.path, this);
}
OBJ.prototype.update = function (path, par) {
this.create(path, par);
}
OBJ.prototype.query_grade_color = function () {
return grade_color[this.grade];
}
OBJ.prototype.notify_action = function (me, isadd) {
if (!this.on_use) return;
if (!this.showAction) return;
if (isadd)
me.send("{type:'addAction',id:'" + this.id + "',name:'" + this.name + "',distime:" + (this.distime || 0) + "}");
else
me.send("{type:'removeAction',id:'" + this.id + "'}");
}
OBJ.prototype.query_temp = CHARACTER.prototype.query_temp;
OBJ.prototype.set_temp = CHARACTER.prototype.set_temp;
OBJ.prototype.remove_temp = CHARACTER.prototype.remove_temp;
OBJ.prototype.add_temp = CHARACTER.prototype.add_temp;
OBJ.create_by_odds = function (args) {
var items = [];
if (!args) return items;
let drop = null, per = null,
obj = null;
for (var i = 0; i < args.length; i++) {
drop = args[i];
if (!drop) continue;
per = Math.random() * 10000;
obj = (drop.odds || 10000) > per ? drop.obj : drop.fall_obj;
if (obj) {
if (drop.min_count) hit_count = 0;
var count = drop.count || 1;
if (drop.min && drop.max) {
count = Math.floor(Math.random() * (drop.max - drop.min + 1)) + drop.min;
}
if (count > 0) {
if (obj instanceof Array) obj = obj.random();
items.push(OBJ.CREATE(obj, count));
}
}
}
return items;
}

38
os/login.js Normal file
View File

@@ -0,0 +1,38 @@
var crypto = require('crypto');
module.exports = {
max_idcount: 10,
max_ipcount: 12,
login_error: function (user, msg, close = true) {
user.send(`{type:'loginerror',msg:'${msg}'}`);
if (close)
user.socket?.end();
return false;
},
encryptUser: function (key, session) {
if (!key || !session) return null;
if (key.length >= 16) key = key.substr(0, 16);
try {
key = Buffer.from(key, 'utf8');
var decipher = crypto.createDecipheriv('aes-128-cbc', key, __CONFIG.DESIV);
var txt = decipher.update(session, 'base64', 'utf8');
txt += decipher.final('utf8');
var str = txt.split("%");
if (str.length !== 5) return null;
var id = parseInt(str[0]);
if (id > 0)
return {
id: id,
name: str[1],
pwd: str[2],
loginTime: parseInt(str[3]),
level: parseInt(str[4])
};
} catch (e) {
return null;
}
}
}

340
os/net-ws.js Normal file
View File

@@ -0,0 +1,340 @@
"use strict";
var crypto = require('crypto');
var fs = require("fs");
function wsServer(options) {
var evt = ["Close", "Error", "SocketIn", "Connect", "Receive",
"ClientError", "ClientClose", "ClientTimeout"];
for (var i = 0; i < evt.length; i++) {
this["on" + evt[i]] = function () {
}
}
this.options = options.SSL ? {
key: fs.readFileSync(options.KEY),
cert: fs.readFileSync(options.CERT),
requestCert: true,
rejectUnauthorized: true,
passphrase: options.PASSWORD,
ca: [fs.readFileSync(options.CERT)]
} : null;
this.ssl = options.SSL;
}
wsServer.prototype.listen = function (port, func) {
var net = require(this.ssl ? 'tls' : 'net');
var tcpserver = net.createServer(this.options, onClientConnect.bind(this));
tcpserver.listen(port, func);
tcpserver.on('close', this.onClose.bind(this));
tcpserver.on('error', this.onError.bind(this));
this.tcpServer = tcpserver;
}
wsServer.prototype.send = function (msg, socket) {
socket.send(msg);
}
wsServer.prototype.close = function () {
return new Promise((resolve) => {
this.tcpServer.close(resolve);
});
}
module.exports = wsServer;
function onClientConnect(socket) {
socket.send = function (msg) {
if (msg)
this.protocol.sendData(msg, socket);
}
socket.on('close', this.onClientClose.bind(this, socket));
socket.on('error', this.onClientError.bind(this, socket));
var $this = this;
socket.setTimeout(3000);
socket.on('timeout', this.onClientTimeout.bind(this, socket));
$this.onSocketIn(socket);
socket.on('data', function (data) {
if (socket.protocol) {
socket.protocol.readData(data, socket, $this);
} else {
var header = readHeader(data);
socket.requestHeader = header;
if (header["Sec-WebSocket-Key"]) {
socket.protocol = protocols.var1;
} else if (header["Sec-WebSocket-Key1"]) {
socket.protocol = protocols.var2;
} else {
socket.protocol = protocols.tcp;
socket.protocol.readData(data, socket, $this);
return;
}
socket.protocol.handShake(header, socket, data);
$this.onConnect(socket);
}
});
}
var protocols = {
var1: {
handShake: function (header, socket) {
var hasher = crypto.createHash("sha1");
hasher.update(header["Sec-WebSocket-Key"] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
var hashmsg = hasher.digest().toString('base64');
var origin = header.Origin;
var protocol = header['sec-websocket-protocol'];
if (protocol)
protocol.split(/, */);
var respon = ["HTTP/1.1 101 Switching Protocols",
"Connection: Upgrade",
"Upgrade: WebSocket",
`Sec-WebSocket-Accept:${hashmsg}`,
`Sec-WebSocket-Origin:${origin}`];
if (protocol) respon.push(`Sec-WebSocket-Protocol: ${protocol}`);
//var extens = header["Sec-Websocket-Extensions"];
//if (extens) {
// respon.push(`Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits`);
//}
respon.push("\r\n");
socket.write(respon.join("\r\n"));
},
readData: function (data, socket, server) {
var start = 0;
while (start < data.length) {
var iseof = (data[start] >> 7) > 0;
var frameType = data[start++] & 0xF;
var hasMask = (data[start] >> 7) > 0;
var length = (data[start++] & 0x7F);
if (length == 126) {
length = data.readUInt16BE(start);
start = start + 2;
}
else if (length == 127) {
length = data.readUInt32BE(start);
start = start + 4;
return;
}
var markIndex = start;
start = start + 4;
for (let i = 0; i < length; i++) {
data[start] = data[start] ^ data[markIndex + (i % 4)];
start++;
}
switch (frameType) {
case FrameTypes.Close:
socket.end();
// server.onClientClose(socket);
break;
case FrameTypes.Binary:
break;
case FrameTypes.Ping:
break;
case FrameTypes.Pong:
break;
case FrameTypes.Text:
var msg = data.toString("utf8", markIndex + 4, markIndex + 4 + length);
server.onReceive(msg, socket);
break;
default:
break;
}
}
},
sendData: function (text, socket) {
var textBuffer = Buffer.from(text);
var length = textBuffer.length;
var data;
if (length < 126) {
data = Buffer.alloc(length + 2);
data[0] = 129;
data.writeUInt8(length, 1);
textBuffer.copy(data, 2);
}
else if (length >= 126 && length < 65536) {
data = Buffer.alloc(length + 4);
data[0] = 129;
data.writeUInt8(126, 1);
data.writeUInt16BE(length, 2);
textBuffer.copy(data, 4);
} else {
data = Buffer.alloc(length + 10);
data[0] = 0x81;
data[1] = 127;
data.writeUInt32BE(0, 2);
data.writeUInt32BE(length, 6);
textBuffer.copy(data, 10);
}
socket.write(data);
}
},
var2: {
handShake: function (header, socket, buffer) {
var key1 = header["Sec-WebSocket-Key1"];
var key2 = header["Sec-WebSocket-Key2"];
var origin = header["Origin"];
var n1 = getNumber(key1);
n1 = parseInt(n1);
n1 = n1 / getSpace(key1);
var n2 = getNumber(key2);
n2 = parseInt(n2);
n2 = n2 / getSpace(key2);
var buf = Buffer.alloc(16);
buf.writeIntBE(n1, 0, 4, true);
buf.writeIntBE(n2, 4, 4, true);
buffer.copy(buf, 8, buffer.length - 8, buffer.length);
var hasherbs = crypto.createHash("md5");
hasherbs = hasherbs.update(buf);
hasherbs = hasherbs.digest();
var host = "ws://" + header["Host"] + "/";
var headers = [
"HTTP/1.1 101 WebSocket Protocol Handshake",
"Upgrade: WebSocket",
"Connection: Upgrade",
"Sec-WebSocket-Origin:" + origin,
"Sec-WebSocket-Location:" + host
, "\r\n"
];
socket.write(headers.join("\r\n"));
socket.write(hasherbs);
},
buffer: null
,
readData: function (data, socket, server) {
var start = 0;
while (start < data.length) {
if (data[start] != 0) {
break;//error
}
var end = start + 1;
while (data[end] != 255 && end < data.length) {
end++;
}
var msg = data.toString("utf8", start + 1, end);
server.onReceive(msg, socket);
start = end + 1;
}
},
sendData: function (text, socket) {
var textBuffer = Buffer.from(text, "utf-8");
var length = textBuffer.length;
var wrappedBytes = Buffer.alloc(length + 2);
wrappedBytes[0] = 0;
textBuffer.copy(wrappedBytes, 1);
wrappedBytes[wrappedBytes.length - 1] = 255;
socket.write(wrappedBytes);
}
},
tcp: {
readData: function (data, socket, server) {
var start = 0;
if (socket.unread_data) {
data = Buffer.concat([socket.unread_data, data], socket.unread_data.length + data.length);
socket.unread_data = null;
}
let isread = false;
while (start < data.length) {
let length = data.readUInt8(start);
let index = start + 1;
if (length === 254) {
//不够读长度咋办
length = data.readUInt16BE(index);
index += 2;
} else if (length === 255) {
length = data.readUInt32BE(index);
index += 4;
}
if (data.length < index + length) {
socket.unread_data = isread ? data.slice(start) : data;
return;
}
let msg = data.toString("utf8", index, index + length);
isread = true;
server.onTcpReceive(msg, socket);
start = index + length;
}
},
sendData: function (text, socket) {
var textBuffer = Buffer.from(text);
var length = textBuffer.length;
var data;
if (length < 254) {
data = Buffer.alloc(length + 1);
data.writeUInt8(length);
textBuffer.copy(data, 1);
}
else if (length >= 254 && length < 65536) {
data = Buffer.alloc(length + 3);
data.writeUInt8(254);
data.writeUInt16BE(length, 1);
textBuffer.copy(data, 3);
} else {
data = Buffer.alloc(length + 5);
data.writeUInt8(255);
data.writeUInt32BE(length, 1);
textBuffer.copy(data, 5);
}
socket.write(data);
}
}
};
function getNumber(str) {
return str.replace(/\D/g, "");
}
function getSpace(str) {
return str.replace(/\S/g, "").length;
}
function readHeader(data) {
var header = {}, key, flag = 0;
for (var i = 0; i < data.length; i++) {
switch (data[i]) {
case 0x0D://\r
key && (header[key] = data.toString("utf8", flag, i));
break;
case 0x0A://\n
key = null;
flag = i + 1;
break;
case 0x3A://:
if (!key) {
key = data.toString("utf8", flag, i);
data[i + 1] == 0x20 ? flag = i + 2 : flag = i + 1;
}
break;
}
}
if (flag < data.length) {
header.CONTENT = data.toString("utf8", flag);
}
return header;
}
var FrameTypes =
{
Continuation: 0,
Text: 1,
Binary: 2,
Close: 8,
Ping: 9,
Pong: 10,
};
//%x0 代表一个继续帧
//%x1 代表一个文本帧
//%x2 代表一个二进制帧
//%x3-7 保留用于未来的非控制帧
//%x8 代表连接关闭
//%x9 代表ping
//%xA 代表pong
//%xB-F 保留用于未来的控制帧

135
os/room/area.js Normal file
View File

@@ -0,0 +1,135 @@
AREA = function () {
this.rooms = [];
this.map = [];
this.name = "";
this.is_area = false;
this.first = null;
this.is_show = true;
this.is_copy = false;
this.expend = 10;
this.is_multi = false;
this.index = 0;
this.exp = 1000;
this.pot = 1000;
}
AREA.inherits(BASE);
AREA.prototype.create = function (path) {
WORLD.AREAS.push(this);
if (this.family) {
FAMILIES[this.family].area = this;
}
}
AREA.Get = function (id) {
if (!WORLD.AREAS) return;
for (var i = 0; i < WORLD.AREAS.length; i++) {
if (WORLD.AREAS[i].id == id) return WORLD.AREAS[i];
}
}
AREA.prototype.on_leaved = function (me) {
//离开后
}
AREA.prototype.on_leave = function (me) {
//进入前
return true;
}
AREA.prototype.on_enterd = function (me) {
//进入后
}
AREA.prototype.on_enter = function (me) {
//进入前
return true;
}
AREA.prototype.find_area = function (path) {
}
AREA.prototype.is_record = function (diff) {
return this["record_" + diff];
}
AREA.prototype.query_exp = function () {
var lv = this.fb_index || 0;
return 1000 + lv * 100;
}
AREA.prototype.query_desc = function () {
return this.desc;
}
AREA.prototype.clear = function () {
this.json = null;
this.drop_list = null;
this.diff_drop_list = null;
}
AREA.prototype.query_drops = function (isdiff) {
if (isdiff) return this.query_diff_drops();
if (this.drop_list) return this.drop_list;
var items = [];
for (var i = 0; i < this.rooms.length; i++) {
var rm = this.rooms[i];
for (var j = 0; j < rm.items.length; j++) {
if (rm.items[j].drop_list) {
items.push(rm.items[j].drop_list);
}
}
}
this.query_npc_drops(this.drop_npcs0, items);
this.drop_list = items;
return this.drop_list;
}
AREA.prototype.query_npc_drops = function (npcs, items) {
if (!npcs || !npcs.length) return;
for (var i = 0; i < npcs.length; i++) {
var npc = NPC.GET(npcs[i]);
if (!npc || !npc.drop_list) continue;
items.push(npc.drop_list)
}
}
AREA.prototype.query_diff_drops = function (isdiff) {
if (this.diff_drop_list) return this.diff_drop_list;
var items = [];
for (var i = 0; i < this.rooms.length; i++) {
var rm = this.rooms[i];
for (var j = 0; j < rm.items.length; j++) {
if (rm.items[j].drop_list) {
items.push(rm.items[j].drop_list);
}
}
}
this.query_npc_drops(this.drop_npcs1, items);
this.diff_drop_list = items;
return this.diff_drop_list;
}
AREA.prototype.update = function (path) {
WORLD.COMMANDS["jh"].map_json = null;
for (var i = 0; i < WORLD.AREAS.length; i++) {
if (WORLD.AREAS[i].path == path) {
var old_area = WORLD.AREAS[i];
WORLD.AREAS[i] = this;
this.rooms = old_area.rooms;
if (this.rooms) {
for (let room of this.rooms) {
room.parent = this;
}
}
old_area.rooms = null;
if (this.family) {
FAMILIES[this.family].area = this;
}
return;
}
}
this.create(path);
}
AREA.Get = function (id) {
for (var i = 0; i < WORLD.AREAS.length; i++) {
if (WORLD.AREAS[i].id == id) {
return WORLD.AREAS[i];
}
}
}
AREA.prototype.query_drop_items = function () {
return this.drop_items;
}
AREA.prototype.query_actions = function () {
return this.actions;
}

5
os/room/fam_area.js Normal file
View File

@@ -0,0 +1,5 @@
FAMILY_AREA = function () {
}
FAMILY_AREA.inherits(AREA);

692
os/room/room.js Normal file
View File

@@ -0,0 +1,692 @@
ROOM = function () {
this.name = "房间";
this.desc = "";
this.items = [];
this.parent = null;
}
ROOM.inherits(ITEM);
ROOM.prototype.max_item_count = 50;
ROOM.prototype.do_leave = function (obj, dir, leave_msg) {
if (this.on_leave && this.on_leave(obj, dir) == false) {
return false;
}
if (this.item_changed(obj, false, leave_msg, dir) == false) {
return false;
}
}
ROOM.prototype.do_enter = function (obj, isshow, in_msg) {
this.on_before_enter && this.on_before_enter(obj);
if (obj.is_player) {
obj.send(this.to_json());
this.send_exits(obj);
}
this.item_changed(obj, true, in_msg);
this.on_enter && this.on_enter(obj);
}
ROOM.prototype.item_changed = function (obj, isin, changed_msg, dir) {
if (!obj) return;
var msg;
var obj_index = -1, isshow = !obj.query_temp('hidden');
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (item == obj) {
obj_index = i;
continue;
}
if (item.is_player && isshow) {
if (!msg) msg = this.item_json(obj, isin);
item.send(msg);
if (changed_msg && item != obj && !item.query_setting("off_move")) {
item.send(changed_msg);
}
} else {
if (obj.hp) {
if (isin && item.on_enter) {
item.on_enter(obj);
} else if (!isin && item.on_leave) {
if (item.on_leave(obj, dir) == false) return false;
}
}
}
}
if (isin) {
obj.environment = this;
if (obj_index == -1) {
this.items.push(obj);
if (obj.is_player) obj.send(this.items_to_json());
} else if (obj.is_player) {
if (!msg) msg = this.item_json(obj, isin);
obj.send(msg);
}
} else if (obj_index > -1) {
this.items.splice(obj_index, 1);
obj.environment = null;
}
}
ROOM.prototype.item_json = function (item, isin) {
if (!item) return "";
var str = [];
if (isin) {
str.push('{"type":"itemadd",');
str.push("id:\"");
str.push(item.id);
str.push("\",name:\"");
str.push(item.long_name());
str.push("\"");
if (item.is_player) {
str.push(",p:1");
}
if (item.appdend_status) {
str.push(",mp:");
str.push(item.mp);
str.push(",hp:");
str.push(item.hp);
str.push(",max_mp:");
str.push(item.max_mp);
str.push(",max_hp:");
str.push(item.max_hp);
item.appdend_status(str);
}
str.push("}");
} else {
str.push('{"type":"itemremove",id:"');
str.push(item.id);
str.push('"}');
}
return str.join("");
}
ROOM.prototype.items_to_json = function () {
var str = ['{"type":"items","items":['];
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
if (!item.is_hidden()) {
str.push("{id:\"");
str.push(item.id);
str.push("\",name:\"");
str.push(item.long_name());
str.push("\"");
if (item.is_player) {
str.push(",p:1");
} else {
if (!item.item_types) {
item.item_types = item.hp > 0 ? `,m:${(item.on_checkskill || item.on_master) ? 1 : 0},l:${item.sell_list ? 1 : 0},f:${item.master ? 1 : 0}` : ",o:1";
}
str.push(item.item_types);
}
if (item.appdend_status) {
str.push(",mp:");
str.push(item.mp);
str.push(",hp:");
str.push(item.hp);
str.push(",max_mp:");
str.push(item.max_mp);
str.push(",max_hp:");
str.push(item.max_hp);
item.appdend_status(str);
}
str.push("},");
}
}
str.push("0]}");
return str.join("");
}
ROOM.prototype.set_npc = function () {
for (var i = 0; i < arguments.length; i++) {
var name = arguments[i];
if (typeof name == "string") name = [name, 1];
var obj_path = name[0];
if (!obj_path) continue;
for (var j = 0; j < name[1]; j++) {
var obj = NPC.CLONE(obj_path);
if (obj) {
this.items.push(obj);
obj.environment = this;
}
}
}
}
ROOM.prototype.set_obj = function (names) {
for (var i = 0; i < arguments.length; i++) {
var name = arguments[i];
if (typeof name == "string") name = [name, 1];
var obj = OBJ.CREATE(name[0], name[1]);
if (obj) {
this.items.push(obj);
}
}
}
ROOM.prototype.set_item = function (id, name, desc, commands) {
this.hidden_items = this.hidden_items || [];
if (commands && typeof commands[0] == "string") {
commands = [commands];
}
let hidden_item = {
id: id,
name: name,
desc: desc,
commands: commands,
query_desc: on_look_hidden_item,
environment: this
};
this.hidden_items.push(hidden_item);
if (commands) {
for (var j = 0; j < commands.length; j++) {
this.add_action(commands[j][0], null, commands[j][2]);
}
//添加隐藏物品的命令到房间的actions命令不能重复
}
return hidden_item;
}
function on_look_hidden_item(player) {
if (this.json) return this.json;
var json = {};
json.type = "item";
json.desc = this.desc;
if (this.commands) {
json.commands = [];
for (var i = 0; i < this.commands.length; i++) {
if (this.commands[i][1])
json.commands.push({
cmd: this.commands[i][0] + " " + this.id,
name: this.commands[i][1]
});
}
}
this.json = JSON.stringify(json)
return this.json;
}
ROOM.prototype.find_obj = function (oid) {
var items = this.items;
if (!items) return;
var item = this.find_obj_byid(items, oid);
if (item) return item;
if (!this.hidden_items) return;
for (var i = 0; i < this.hidden_items.length; i++) {
if (this.hidden_items[i].id == oid) return this.hidden_items[i];
}
}
ROOM.prototype.find_by_path = function (path) {
var items = this.items;
if (!items) return;
for (var i = 0; i < items.length; i++) {
if (items[i].path == path) {
return items[i];
}
}
}
ROOM.prototype.is_here = function (path) {
var items = this.items;
if (!items) return;
for (var i = 0; i < items.length; i++) {
if (items[i].path == path) {
return true;
}
}
}
ROOM.prototype.notify = function (msg) {
if (!this.items) return;
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].is_player) {
this.items[i].notify(msg);
}
}
}
ROOM.prototype.query_exits = function (dir) {
if (this.exits && this.exits[dir]) return true;
return false;
}
ROOM.prototype.add_exit = function (dir, rm) {
this.exits = this.exits || {};
this.exits[dir] = rm;
this.exits_changed();
}
ROOM.prototype.remove_exit = function (dir) {
this.exits = this.exits || {};
delete this.exits[dir];
this.exits_changed();
}
ROOM.prototype.exits_changed = function () {
this.room_exits_json = null;
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].is_player)
this.send_exits(this.items[i]);
}
}
ROOM.prototype.send_exits = function (player) {
player.send(this.exitsto_roomjson());
}
ROOM.prototype.exitsto_roomjson = function () {
if (this.room_exits_json) return this.room_exits_json;
var obj = {};
obj.type = "exits";
obj.items = {};
if (this.exits) {
for (var dir in this.exits) {
if (!this.exits[dir]) continue;
var rm = ROOM.Get(this.exits[dir]);
if (!rm) continue;
obj.items[dir] = rm.name;
}
}
this.room_exits_json = JSON.stringify(obj);
return this.room_exits_json;
}
ROOM.prototype.to_json = function () {
if (this.json) return this.json;
var obj = {};
obj.type = "room";
obj.path = this.path;
obj.name = this.long_name;
obj.desc = this.desc;
obj.commands = [];
if (this.actions) {
for (var cmd in this.actions) {
var name = this.actions[cmd].name;
if (name)
obj.commands.push({
cmd: cmd,
name: name
});
}
}
if (this.is_copy_room && !this.parent.not_fb) {
obj.commands.push({
cmd: "cr",
name: "完成副本"
});
}
this.json = JSON.stringify(obj);
return this.json;
}
ROOM.prototype.query_commands = function () {
if (this.commands_json) return this.commands_json;
var json = {};
json.type = "command";
json.commands = [];
if (this.actions) {
for (var cmd in this.actions) {
json.commands.push({
name: this.actions[cmd].name,
cmd: cmd
});
}
}
this.commands_json = JSON.stringify(json)
return this.commands_json;
}
ROOM.prototype.refresh = function (obj) {
this.json = null;
this.commands_json = null;
this.room_exits_json = null;
var rmname = this.parent.name + "-" + this.name;
if (this.parent.not_fb || !this.parent.is_copy) {
this.long_name = rmname;
} else {
this.long_name = rmname + "(副本区域)";
}
if (obj) {
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].is_player) {
this.items[i].send(this.to_json());
this.send_exits(this.items[i]);
this.items[i].send(this.items_to_json());
}
}
// this.send_exits(obj);
}
}
ROOM.prototype.get_path = function () {
if (this.path) return this.path;
var str = this.name;
var area = this.area;
while (area) {
str = area.name + "-" + str;
area = area.parent;
}
this.path = str;
return this.path;
}
ROOM.prototype.query_recover_room = function () {
var area = this.parent;
while (area) {
if (area.recover_room) {
return area.recover_room;
}
area = area.parent;
}
return "yz/wumiao";
}
ROOM.prototype.create = function (file) {
var base_room = WORLD.ROOMS[file];
if (base_room) {
this.parent = base_room.parent;
if (this.parent.is_copy) {
//副本区域
if (this.parent.not_fb) {
this.long_name = base_room.long_name;
} else {
this.long_name = base_room.long_name + "(副本区域)";
}
this.create_time = Date.now();
this.is_copy_room = true;
} else {
//投影区域 房间人满了后
this.is_shadow = true;
this.long_name = base_room.long_name;//+ "(" + UTIL.to_c(base_room.shadow_rooms.length + 1) + "号)";
}
WORLD.RUN_ROOMS.push(this);
} else {
this.initBaseRoom(file);
}
this.on_create && this.on_create();
}
ROOM.prototype.initBaseRoom = function (file) {
WORLD.ROOMS[file] = this;
var area = getAreaByPath(this.path);
this.parent = area;
if (!area || !area.is_copy) {
//如果是副本,第一个被创建的不放在运行的房间
WORLD.RUN_ROOMS.push(this);
}
if (area) {
if (!area.rooms) area.rooms = [];
for (var i = 0; i < area.rooms.length; i++) {
if (area.rooms[i].path == this.path) {
area.rooms.splice(i, 1);
break;
}
}
area.rooms.push(this);
this.long_name = area.name + "-" + this.name;
} else {
this.long_name = this.name;
}
}
ROOM.prototype.update = function (file) {
this.on_create && this.on_create();
var oldroom = WORLD.ROOMS[file];
this.initBaseRoom(file);
if (!oldroom) return;
if (oldroom.copy_rooms) {
this.copy_rooms = {};
for (var key in oldroom.copy_rooms) {
var rm = oldroom.copy_rooms[key];
var newRm = BASE.CREATE(__PATH.MAP, this.path);
this.replaceRoom(rm, newRm);
newRm.owner = key;
this.copy_rooms[key] = newRm;
}
oldroom.copy_rooms = null;
} else {
if (ROOM.public_rooms) {
for (var i = 0; i < ROOM.public_rooms.length; i++) {
if (ROOM.public_rooms[i] == oldroom) {
ROOM.public_rooms[i] = this;
break;
}
}
}
}
}
ROOM.prototype.replaceRoom = function (oldroom, newRoom) {
var items = oldroom.items;
for (var i = 0; i < items.length; i++) {
if (items[i].is_player || items[i].master) {
newRoom.items.push(items[i]);
items[i].environment = newRoom;
}
}
oldroom.destroy();
}
ROOM.prototype.destroy = function () {
this.items.length = 0;
this.owner = null;
}
ROOM.prototype.heart_beat = function (dt) {
for (var i = 0; i < this.items.length; i++) {
if (!this.items[i].is_player)
this.items[i].heart_beat(dt);
}
this.on_heart_beat && this.on_heart_beat(dt);
}
ROOM.prototype.is_copy = function () {
if (!this.parent) return false;
return this.parent.is_copy;
}
ROOM.prototype.is_fb = function () {
if (!this.parent) return false;
return this.parent.is_copy && !this.parent.not_fb;
}
ROOM.prototype.is_enter = function () {
if (!this.parent) return false;
return this.parent.first == this.path;
}
ROOM.prototype.query_fb_first = function (id) {
//查询副本入口
if (!this.parent || !this.parent.is_copy || !this.parent.rooms) return;
return this.parent.rooms[0].query_copy(id);
}
ROOM.prototype.query_copy = function (id) {
if (!this.copy_rooms) this.copy_rooms = {};
return this.copy_rooms[id];
}
ROOM.prototype.query_copy2 = function (user) {
var id = this.parent.query_owner(user);
if (!id) return this;
return this.query_copy(id);
}
ROOM.prototype.clear_copy = function (me) {
//清除复制的副本,四个位置,换地图,完成副本,复活,彻底掉线
//但是又不能清除不是自己的副本,比如帮派
if (!this.owner) return;
let id = this.parent.query_owner(me);
if (id !== this.owner) return;//只能清除自己或队伍创建的副本
var name = "fb/";
for (var key in me.temp) {
if (key.startsWith(name)) {
me.temp[key] = null;
}
}
if (me.team) {
for (var i = 0; i < me.team.length; i++) {
let tm = me.team[i];
if (tm !== me && tm.environment && tm.environment.parent === this.parent
&& tm.environment.owner == this.owner) {
return;
}
}
}
this.clear_by_area(this.parent, this.owner);
}
ROOM.prototype.create_copy2 = function (me, diff_type) {
var id = this.parent.query_owner(me);
if (!id) return;
return this.create_copy(id, diff_type || 0);
}
ROOM.prototype.create_copy = function (id, diff_type) {
//第一次创建副本,从入口开始创建,把所在区域所有房间都创建一遍
if (!this.parent) return;
// var rooms = [];
this.create_by_area(this.parent, id, diff_type);
//this.add_fbroom(rooms);
return this.query_copy(id);
}
ROOM.prototype.create_by_area = function (area, id, diff_type) {
if (area.rooms) {
for (var i = 0; i < area.rooms.length; i++) {
var base_room = area.rooms[i];
var copy_room = BASE.CREATE(__PATH.MAP, base_room.path);
if (!copy_room) continue;
copy_room.set_difficulty(diff_type);
if (!base_room.copy_rooms) base_room.copy_rooms = {};
base_room.copy_rooms[id] = copy_room;
copy_room.owner = id;
}
}
if (area.areas) {
for (var i = 0; i < area.areas.length; i++) {
this.create_by_area(area.areas[i], id);
}
}
}
ROOM.prototype.create_shadow = function () {
//当房间人满了后 进入房间就另外创建一个房间投影,这种类型的房间最好不要放NPC,物品
//这种房间创建了不销毁,重复使用
if (this.is_copy_room || this.no_shadow) return;
if (!this.shadow_rooms) this.shadow_rooms = [];
for (var i = 0; i < this.shadow_rooms.length; i++) {
if (!this.shadow_rooms[i].is_full()) {
return this.shadow_rooms[i];
}
}
var shadow = BASE.CREATE(__PATH.MAP, this.path);
if (shadow) {
this.shadow_rooms.push(shadow);
}
return shadow;
}
ROOM.prototype.clear_by_area = function (area, id) {
if (area.rooms) {
for (var i = 0; i < area.rooms.length; i++) {
var base_room = area.rooms[i];
if (base_room.copy_rooms) {
var rm = base_room.copy_rooms[id];
if (rm) {
WORLD.RUN_ROOMS.remove(rm);
rm.destroy();
delete base_room.copy_rooms[id];
}
}
}
}
if (area.areas) {
for (var i = 0; i < area.areas.length; i++) {
this.clear_by_area(area.areas[i], id);
}
}
}
function getAreaByPath(path) {
var index = path.lastIndexOf("/");
path = path.substr(0, index + 1);
var items = WORLD.AREAS;
for (var i = 0; i < items.length; i++) {
if (items[i].room_path == path) return items[i];
}
}
ROOM.Get = function (path) {
var rm = WORLD.ROOMS[path];
if (!rm) return console.log("room %s is not exist", path);
// if (!rm) throw new Error(path + "is not exist");
return rm;
}
//房间存储数据,存在当前用户或队伍的房间区域的第一个房间里面,在副本销毁时候会释放
ROOM.prototype.query_temp = function (me, name, def) {
var first = this.query_fb_first(me.query_teamid());
if (!first) return;
if (!first.temp) return;
var item = first.temp[name];
if (item && item.e) {
if (Date.now() <= item.e) {
return item.v;
}
first.temp[name] = null;
return def;
}
return item || def;
}
ROOM.prototype.set_temp = function (me, name, value, time) {
var first = this.query_fb_first(me.query_teamid());
if (!first) return;
if (!first.temp) first.temp = {};
if (time) {
first.temp[name] = {
v: value,
e: Date.now() + time
};
} else {
first.temp[name] = value;
}
}
ROOM.prototype.add_temp = function (me, name, value, time) {
let val = this.query_temp(me, name, 0) + value;
this.set_temp(me, name, val, time);
return val;
}
ROOM.RANDOM = function () {
if (!this.public_rooms) {
this.public_rooms = [];
for (var i = 0; i < WORLD.AREAS.length; i++) {
if (WORLD.AREAS[i].is_area && !WORLD.AREAS[i].is_public) {
var rms = WORLD.AREAS[i].rooms;
for (var j = 0; j < rms.length; j++) {
if (rms[j].max_item_count > 1)
this.public_rooms.push(rms[j]);
}
}
}
}
return this.public_rooms.random();
}
ROOM.prototype.set_difficulty = function (type) {
this.on_set_difficulty && this.on_set_difficulty(type);
}
ROOM.prototype.send = function (msg) {
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].is_player) {
this.items[i].send(msg);
}
}
}
ROOM.prototype.find_me = function () {
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].is_player) {
return this.items[i];
}
}
}
ROOM.prototype.query = function (id) {
let room = ROOM.Get(id);
if (!room) return null;
if (this.owner) {
return room.query_copy(this.owner);
}
return room;
}

94
os/skill/family.js Normal file
View File

@@ -0,0 +1,94 @@
FAMILY = function () {
this.titles = [];
this.npcs = [];
this.battle_family = null;
//this.scores = new Map();
this.battle_score = 0;
this.battle_gift = 0;
this.can_battle = false;
}
FAMILY.inherits(BASE);
FAMILIES = {};
FAMILY.prototype.set_titles = function () {
for (var i = 0; i < arguments.length; i++) {
this.titles.push(arguments[i]);
}
}
FAMILY.prototype.create = function (path) {
FAMILIES[this.id] = this;
}
FAMILY.prototype.update = function (path) {
FAMILIES[this.id] = this;
}
FAMILY.prototype.query_title = function (level) {
return this.titles[level];
}
FAMILY.prototype.query_temp = CHARACTER.prototype.query_temp;
FAMILY.prototype.set_temp = CHARACTER.prototype.set_temp;
FAMILY.prototype.remove_temp = CHARACTER.prototype.remove_temp;
FAMILY.prototype.add_temp = CHARACTER.prototype.add_temp;
FAMILY.prototype.send = function (str) {
for (var i = 0; i < WORLD.USERS.length; i++) {
if (WORLD.USERS[i].family == this) {
WORLD.USERS[i].send(str);
}
}
}
FAMILY.prototype.is_battle = function (fam) {
return this.battle_family == fam.id;
}
FAMILY.prototype.add_score = function (me, sc) {
this.battle_score += sc;
}
CHARACTER.prototype.send_fam = function (str) {
this.family.send(str);
}
FAMILY.prototype.create_name = function () {
return UTIL.random_name(this.gender);
}
FAMILY.prototype.query_skill = function (grade) {
if (!this.skill_levels) {
this.skill_levels = [];
for (var i = 0; i < this.skills.length; i++) {
if (!this.skill_levels[this.skills[i].grade]) {
this.skill_levels[this.skills[i].grade] = [];
}
this.skill_levels[this.skills[i].grade].push(this.skills[i]);
}
}
if (grade >= this.skill_levels.length) grade = this.skill_levels.length - 1;
return this.skill_levels[grade].random();
}
FAMILY.prototype.query_skills = function (grade) {
if (!this.skill_levels) {
this.skill_levels = [];
for (var i = 0; i < this.skills.length; i++) {
if (!this.skill_levels[this.skills[i].grade]) {
this.skill_levels[this.skills[i].grade] = [];
}
this.skill_levels[this.skills[i].grade].push(this.skills[i]);
}
}
if (grade >= this.skill_levels.length) grade = this.skill_levels.length - 1;
return this.skill_levels[grade];
}
FAMILY.prototype.add_gongji = function (me, count) {
if (!count) return;
me.add_temp("gongji", count);
if (count < 0) return;
if (!this.tops) this.tops = {};
var old = this.tops[me.id];
if (!old) this.tops[me.id] = { name: me.name, score: count };
else {
old.score += count;
}
}

648
os/skill/skill.js Normal file
View File

@@ -0,0 +1,648 @@
/*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;
}
}
}
}

49
os/task/events.js Normal file
View File

@@ -0,0 +1,49 @@
EVENTS = function () {
}
EVENTS.inherits(BASE);
EVENTS.add = function (item) {
if (!item || !item.id) return;
Object.setPrototypeOf(item, EVENT_BASE);
const items = WORLD.USER_EVENTS;
for (var i = 0; i < items.length; i++) {
if (items[i].id == item.id) {
items[i] = item;
return this.notify(item, 1);
}
}
items.push(item);
this.notify(item, 0);
}
EVENTS.ACTIONS = ['add', 'update', 'finish'];
EVENTS.notify = function (item, act) {
let users = WORLD.USERS;
let msg = `{type: "dialog", dialog: "events",${EVENTS.ACTIONS[act]}:1}`;
for (let user of users) {
if (!user.socket) continue;
if (item.check && !item.check(user))
continue;
user.send(msg);
}
}
EVENTS.remove = function (id) {
if (!id) return;
const items = WORLD.USER_EVENTS;
for (var i = 0; i < items.length; i++) {
if (items[i].id == id) {
EVENTS.notify(items[i], 2);
return items.splice(i, 1);
}
}
}
const EVENT_BASE = {
query_desc: function () {
return this.desc;
},
query_grade: function () {
return this.grade;
}
};

48
os/task/playertask.js Normal file
View File

@@ -0,0 +1,48 @@
require("../util/util");
USERTASK = function () {
}
USERTASK.inherits(BASE);
USERTASK.prototype.create = function (path) {
WORLD.TASKS.push(this);
this.on_create && this.on_create();
}
USERTASK.prototype.update = function (path) {
this.on_create && this.on_create();
for (var i = 0; i < WORLD.TASKS.length; i++) {
if (WORLD.TASKS[i].path == path) {
WORLD.TASKS[i] = this;
return;
}
}
WORLD.TASKS.push(this);
}
USERTASK.prototype.query_title = function () {
}
USERTASK.prototype.start = function () {
}
USERTASK.prototype.query_desc = function () {
}
USERTASK.prototype.query_state = function () {
//0 不显示 1进行中2.可领取 3.已完成
}
USERTASK.RUN = function (id, player) {
for (var i = 0; i < WORLD.TASKS.length; i++) {
if (WORLD.TASKS[i].id == id) {
return WORLD.TASKS[i].start(player);
}
}
return false;
}
USERTASK.GET = function (id) {
for (var i = 0; i < WORLD.TASKS.length; i++) {
if (WORLD.TASKS[i].id == id) {
return WORLD.TASKS[i];
}
}
}

36
os/task/task.js Normal file
View File

@@ -0,0 +1,36 @@
TASK = function () {
}
TASK.inherits(BASE);
TASK.prototype.create = function () {
WORLD.SYSTEMTASKS.push(this);
this.startup();
}
TASK.GET = function (id) {
for (var i = 0; i < WORLD.SYSTEMTASKS.length; i++) {
if (WORLD.SYSTEMTASKS[i].id == id) {
return WORLD.SYSTEMTASKS[i];
}
}
}
TASK.prototype.update = function (path) {
var oldtask = null;
for (var i = 0; i < WORLD.SYSTEMTASKS.length; i++) {
if (WORLD.SYSTEMTASKS[i].path == path) {
WORLD.SYSTEMTASKS[i].stop();
oldtask = WORLD.SYSTEMTASKS[i];
WORLD.SYSTEMTASKS[i] = this;
}
}
if (!oldtask) {
WORLD.SYSTEMTASKS.push(this);
}
this.startup(oldtask);
}
TASK.prototype.startup = function () {
}
TASK.prototype.stop = function () {
}

154
os/util/data.js Normal file
View File

@@ -0,0 +1,154 @@
const fs_sync = require("fs");
const fs = fs_sync.promises;
const DB = __CONFIG.DB;
module.exports = {
close: function () {
return DB.close();
},
getRoles: function (userid, server) {
return DB.getRoles(userid, server);
},
addRole: async function (role) {
return await DB.addRole(role);
},
deleteRole: function (userid, roleid) {
return DB.deleteRole(userid, roleid);
},
saveRole: function (role) {
return DB.saveRole(role);
},
saveRoles: async function (roles) {
const dt = new Date();
const path = __PATH.DATA + "bak/data" + dt.getFullYear() + "-" + (dt.getMonth() + 1) + "-" + dt.getDate() + "-" + dt.getHours() + ".js";
const stream = fs_sync.createWriteStream(path, { flags: 'a' });
try {
stream.write('[');
for (let role of roles) {
await DB.saveRole(role);
this.localBak(stream, role);
}
stream.write('0]');
} catch (error) {
console.error('备份数据失败:', error);
} finally {
stream.end();
}
},
localBak: function (stream, role) {
stream.write('{id:"');
stream.write(role.id);
stream.write('",name:"');
stream.write(role.name);
stream.write('",userid:');
stream.write(role.userid.toString());
stream.write(',title:"');
stream.write(role.title);
stream.write('",level:');
stream.write(role.level.toString());
stream.write(',data:');
stream.write(role.data);
stream.write('},');
},
saveRequest: function (recs) {
var dt = new Date();
var f = dt.getFullYear() + "-" + (dt.getMonth() + 1) + "-" + dt.getDate();
var path = __PATH.DATA + "req/request" + f + ".txt";
var ary = [];
for (var i = 0; i < recs.length; i++) {
var r = recs[i];
ary.push(r.time);
ary.push(" ");
ary.push(r.user);
ary.push(" ");
ary.push(r.cmd);
ary.push("\r\n");
}
return fs.appendFile(path, ary.join(""));
},
saveLogs: function (logs) {
var dt = new Date();
var f = dt.getFullYear() + "-" + (dt.getMonth() + 1) + "-" + dt.getDate();
var path = __PATH.DATA + "log/log" + f + ".txt";
var ary = [];
for (var i = 0; i < logs.length; i++) {
var r = logs[i];
ary.push(r.time);
ary.push(" ");
ary.push(r.user);
ary.push(" ");
ary.push(r.cmd);
ary.push(" ");
ary.push(r.msg);
ary.push("\r\n");
}
return fs.appendFile(path, ary.join(""));
},
saveData: async function (content) {
let path = __PATH.DATA + "data.js";
let dt = new Date();
var f = dt.getFullYear() + "-" + (dt.getMonth() + 1) + "-" + dt.getDate();
let _dst = __PATH.DATA + "/temp/temp" + f + ".js";
await fs.copyFile(path, _dst);
return fs.writeFile(path, content);
}, readData: async function () {
let path = __PATH.DATA + "data.js";
try {
const data = await fs.readFile(path);
return JSON.toObject(data);
} catch (error) {
console.error('数据读取失败', path, error);
}
},
getRoleData: function (userid, id) {
return DB.getData(userid, id);
},
change_name: function (id, name) {
return DB.updateRoleName(id, name);
},
change_userid: function (id, fromuserid, touserid) {
return DB.updateUserid(id, fromuserid, touserid);
},
check_file: async function (path) {
try {
await fs.access(path)
return true;
} catch (error) {
return false;
}
},
initDataDir: async function () {
__PATH.BASE_DATA = __PATH.DATA;
__PATH.DATA = __PATH.DATA + WORLD.SERVERID + "/";
if (!await this.check_file(__PATH.DATA)) {
console.log('创建备份文件夹....');
await fs.mkdir(__PATH.DATA);
}
var paths = await fs.readdir(__PATH.DEF_DATA);
for (var i = 0; i < paths.length; i++) {
var _src = __PATH.DEF_DATA + paths[i];
var _dst = __PATH.DATA + paths[i];
if (!await this.check_file(_dst)) {
var stat = await fs.stat(_src);
if (stat.isFile()) {
await fs.copyFile(_src, _dst);
console.log('创建备份文件 ', _dst, "....");
} else {
await fs.mkdir(_dst);
console.log('创建备份文件夹 ', _dst, "....");
}
}
}
},
getServers: function () {
return DB.getServers();
}
};

406
os/util/util.js Normal file
View File

@@ -0,0 +1,406 @@
const util = require('util');
const JSON5 = require('json5');
//nodejs自带的原型继承方法
Function.prototype.inherits = function (superCtor) {
util.inherits(this, superCtor);
}
Array.prototype.remove = function (item) {
for (var i = 0; i < this.length; i++) {
if (this[i] == item) {
this.splice(i, 1);
return true;
}
}
}
Array.prototype.contain = function (item) {
for (var i = 0; i < this.length; i++) {
if (this[i] === item) {
return true;
}
}
return false;
}
Array.prototype.random = function (index) {
index = index || this.length;
return this[Math.floor(Math.random() * this.length)];
}
JSON.toObject = function (str) {
return JSON5.parse(str);
}
UTIL = {
empty: function () {
},
require: function (str) {
return require(str);
},
is_gift: function () {
return false;
},
wrandom: function (me, max, rate = 86400000) {
let key = parseInt(me.id[0], 36);
if (!(key >= 0)) key = 1;
let time = Math.floor(Date.now() / rate) + key;
return time % max;
},
moneyToStr: function (value) {
if (!value) return "";
var str = [];
if (value >= 10000) {
str.push(parseInt(value / 10000) + "两<hiy>黄金</hiy>");
value = value % 10000;
}
if (value > 100) {
str.push(parseInt(value / 100) + "两<wht>白银</wht>");
value = value % 100;
}
if (value > 0) {
str.push(value + "个<yel>铜板</yel>");
}
return str.join("");
},
timeSpan: function (time) {
let str = [];
if (time > 86400000) {
str.push(Math.floor(time / 86400000), '天');
time = time % 86400000;
}
if (time > 3600000) {
str.push(Math.floor(time / 3600000), '小时');
time = time % 3600000;
}
if (time > 60000) {
str.push(Math.floor(time / 60000), '分钟');
time = time % 60000;
}
str.push(Math.floor(time / 1000), '秒');
return str.join("");
},
C_STR: "零一二三四五六七八九",
C_STR2: ["", "十", "百", "千", "万", "亿"],
C_STR3: ["", "万", "亿"],
to_c: function (num) {
if (!num) return "";
var str = "";
var count = 0;
var add = 0;//0=0,1=数 2=百十千 3=万亿
while (num) {
var d = num % 10;
if (count) {
if (count % 4 == 0 && add != 3) {
str = UTIL.C_STR3[count / 4] + str;
add = 3;
} else if (d && add != 2) {
str = UTIL.C_STR2[count % 4] + str;
add = 2;
}
}
if (d) {
if (d != 1 || num > 10 || count % 4 != 1)
str = UTIL.C_STR[d] + str;
add = 1;
} else if (add == 1) {
str = UTIL.C_STR[d] + str;
add = 0;
}
num = parseInt(num / 10);
count++;
}
return str;
},
htmlEncode: function (str) {
if (!str) return str;
return str.replace(/>/g, "&gt;")
.replace(/</g, "&lt;")
},
prop_toString: function (prop, sp, count) {
if (!prop) return "";
var str = [];
count = count || 1;
for (var item in prop) {
switch (item) {
case "desc":
case "desc1":
case "desc2":
case "desc3":
case "desc4":
case "desc5":
str.push(prop[item]);
break;
case "releasetime":
case "distime":
prop[item] > 0 ?
str.push(PROPERTIES[item] + "-" + (prop[item] * count / 1000) + "秒")
:
str.push(PROPERTIES[item] + "+" + (-prop[item] * count / 1000) + "秒");
break;
case "diff_busy":
case "busy":
case "gjsd":
case "diff_downside":
prop[item] > 0 ?
str.push(PROPERTIES[item] + "+" + (prop[item] * count / 1000) + "秒")
:
str.push(PROPERTIES[item] + "" + (prop[item] * count / 1000) + "秒");
break;
case "expend_mp_per":
case "distime_per":
case "releasetime_per":
prop[item] > 0 ?
str.push(PROPERTIES[item] + "-" + (prop[item] * count) + "%")
:
str.push(PROPERTIES[item] + "+" + (prop[item] * -count) + "%");
break;
case "lianxi_per":
case "dazuo_per":
case "study_per":
case "add_sh_per":
case "add_bjsh_per":
case "busy_per":
case "gjsd_per":
case "bj_per":
case "zj_per":
case "ds_per":
case "fy_per":
case "diff_sh_per":
case "diff_sh_per2":
case "diff_fy_per":
case "diff_fy_per2":
case "diff_busy_per":
case "mz_per":
case "gj_per":
case "diff_bj":
case "hp_per":
case "money_per":
case "diff_downside_per":
case "recover_per":
prop[item] > 0 ?
str.push(PROPERTIES[item] + "+" + (prop[item] * count) + "%")
:
str.push(PROPERTIES[item] + "" + (prop[item] * count) + "%");
break;
case "skill":
var skills = prop[item];
for (var sk in skills) {
var sk_base = SKILL.get(sk);
if (sk_base)
str.push(sk_base.name + "+" + skills[sk] + "级");
}
break;
case "age":
prop[item] > 0 ?
str.push(PROPERTIES[item] + "-" + prop[item] * count + "岁")
:
str.push(PROPERTIES[item] + "+" + (-prop[item]) * count + "岁");
break;
case "expend_mp":
prop[item] > 0 ?
str.push(PROPERTIES[item] + "-" + prop[item] * count)
:
str.push(PROPERTIES[item] + "+" + (-prop[item]) * count);
break;
default:
let p = PROPERTIES[item];
if (p) {
prop[item] > 0 ?
str.push(PROPERTIES[item] + "+" + prop[item] * count)
:
str.push(PROPERTIES[item] + "" + prop[item] * count);
} else {
p = SKILL.SLOTS[item];
if (p) {
str.push(p.format(prop[item] * count));
}
}
break;
}
}
return str.join(sp || "\n");
},
diff_time: function (next_hour) {
//到清理临时temp( 明天5点)的时间
next_hour = next_hour || 5;
var dt = new Date();
var hour = dt.getHours();
var day = dt.getDate();
if (hour >= next_hour) day = day + 1;
dt = new Date(dt.getFullYear(), dt.getMonth(), day, next_hour);
return dt - Date.now();
},
diff_week_time: function (next_hour) {
//到清理临时temp( 每周一5点)的时间
next_hour = next_hour || 5;
var date = new Date();
var week = date.getDay();
week = week == 0 ? 1 : 8 - week;
if (week == 7 && date.getHours() < next_hour) {
week = 0;
}
var next_date = new Date(date.getFullYear(), date.getMonth(), date.getDate() + week, next_hour);
return next_date - Date.now();
}, diff_month_time: function (next_hour) {
//到清理临时temp( 每周一5点)的时间
next_hour = next_hour || 5;
var date = new Date();
if (date.getDate() > 1 || date.getHours() >= next_hour) {
date = new Date(date.setMonth(date.getMonth() + 1, 1));
}
var next_date = new Date(date.getFullYear(), date.getMonth(), 1, next_hour);
// me.send(next_date.getFullYear() + "年" + (date.getMonth() + 1)+ "月" + 1 + "日" + next_hour);
return next_date - Date.now();
},
logs: [],
log: function (msg) {
this.logs.push({
dt: Date.now,
content: msg
});
},
saveLog: function () {
if (!this.logs) return;
var fs = require("fs");
var dt = new Date();
var path = __PATH.DATA + "log/";
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
}
var file = path + (dt.getMonth() + 1) + "-" + dt.getDate() + "-" + dt.getHours() + ".log";
fs.writeFileSync(path, JSON.stringify(this.logs));
},
idstr: "0123456789abcdefghijklmnopqrstuvwxwz",
begin: 1490276099978,
create_id: function () {
var str = [];
for (var i = 0; i < 4; i++) {
str.push(this.idstr[parseInt(Math.random() * this.idstr.length)]);
}
str.push(parseInt((Date.now() - this.begin) / 1000).toString(16));
return str.join("");
},
random_name: function (s, t) {
t = t || (parseInt(Math.random() * 2) + 1);
var str = [];
if (t == 2) {
var key = parseInt(Math.random() * this.name0.length);
if (key % 2 == 1) key -= 1;
str.push(this.name0[key++]);
str.push(this.name0[key]);
} else {
str.push(this.name1[parseInt(Math.random() * this.name1.length)]);
}
if (s == 0) {
str.push(this.name2[parseInt(Math.random() * this.name2.length)]);
} else {
str.push(this.name3[parseInt(Math.random() * this.name3.length)]);
}
if (parseInt(Math.random() * 4) > 1) {
if (s == 0) {
str.push(this.name2[parseInt(Math.random() * this.name2.length)]);
} else {
str.push(this.name3[parseInt(Math.random() * this.name3.length)]);
}
}
return str.join("");
},
name0: "万俟司马上官欧阳夏侯诸葛闻人东方赫连皇甫尉迟公羊澹台公冶宗政濮阳淳于单于太叔申屠公孙仲孙轩辕令狐锺离宇文长孙慕容鲜于闾丘司徒司空丌官司寇子车颛孙端木巫马公西乐正公良拓拔夹谷谷梁梁丘左丘东门西门",
name1: "赵钱孙李周吴郑王冯陈楮卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎",
name2: "世舜丞主产仁仇仓仕仞任伋众伸佐佺侃侪促俟信俣修倝倡倧偿储僖僧僳儒俊伟列则刚创前剑助劭势勘参叔吏嗣士壮孺守宽宾宋宗宙宣实宰尊峙峻崇崈川州巡帅庚战才承拯操斋昌晁暠曹曾珺玮珹琒琛琩琮琸瑎玚璟璥瑜生畴矗矢石磊砂碫示社祖祚祥禅稹穆竣竦综缜绪舱舷船蚩襦轼辑轩子杰榜碧葆莱蒲天乐东钢铎铖铠铸铿锋镇键镰馗旭骏骢骥驹驾骄诚诤赐慕端征坚建弓强彦御悍擎攀旷昂晷健冀凯劻啸柴木林森朴骞寒函高魁魏鲛鲲鹰丕乒候冕勰备宪宾密封山峰弼彪彭旁日明昪昴胜汉涵汗浩涛淏清澜浦澉澎澔瀚瀛灏沧虚豪豹辅辈迈邶合部阔雄霆震韩俯颁颇频颔风飒飙飚马亮仑仝代儋利力劼勒卓哲喆展帝弛弢弩彰征律德志忠思振挺掣旲旻昊昮晋晟晸朕朗段殿泰滕炅炜煜煊炎选玄勇君稼黎利贤谊金鑫辉墨欧有友闻问",
name3: "筠柔竹霭凝晓欢霄枫芸菲寒伊亚宜姬舒影荔枝思丽秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊勤珍贞莉兰凤洁梅琳素云莲真环雪荣妹霞香月莺媛艳瑞凡佳嘉琼桂娣叶璧璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦岚苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希宁欣飘育滢馥",
getLunar: function (date) {
date = date || new Date();
//农历函数开始
var lunarInfo = new Array(0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, //1990
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, 0x14b63);
function lYearDays(y) {
var i, sum = 348;
for (i = 0x8000; i > 0x8; i >>= 1) sum += (lunarInfo[y - 1900] & i) ? 1 : 0;
return (sum + leapDays(y));
}
function leapDays(y) {
if (leapMonth(y)) return ((lunarInfo[y - 1900] & 0x10000) ? 30 : 29);
else return (0);
}
function leapMonth(y) {
return (lunarInfo[y - 1900] & 0xf);
}
function monthDays(y, m) {
return ((lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29);
}
function Lunar(y, m, d) {
var i, leap = 0,
temp = 0;
var offset = (Date.UTC(y, m, d) - Date.UTC(1900, 0, 31)) / 86400000;
for (i = 1900; i < 2050 && offset > 0; i++) {
temp = lYearDays(i);
offset -= temp;
}
if (offset < 0) {
offset += temp;
i--;
}
this.year = i;
leap = leapMonth(i);
this.isLeap = false;
for (i = 1; i < 13 && offset > 0; i++) {
if (leap > 0 && i == (leap + 1) && this.isLeap == false) {
--i;
this.isLeap = true;
temp = leapDays(this.year);
} else {
temp = monthDays(this.year, i);
}
if (this.isLeap == true && i == (leap + 1)) this.isLeap = false;
offset -= temp;
}
if (offset == 0 && leap > 0 && i == leap + 1) if (this.isLeap) {
this.isLeap = false;
} else {
this.isLeap = true; --i;
}
if (offset < 0) {
offset += temp; --i;
}
this.month = i;
this.day = offset + 1;
}
return new Lunar(date.getFullYear(), date.getMonth(), date.getDate());
},
isLunar15: function (dt) {
let issuc = WORLD.DATA.query_temp('lunar15', 0);
if (issuc === 0) {
let now = dt || new Date();
let hour = now.getHours();
var lunar = UTIL.getLunar(now);
if ((lunar.day === 14 && hour >= 5) || lunar.day === 15
|| (lunar.day === 16 && hour < 5)) {
WORLD.DATA.set_temp('lunar15', 1, UTIL.diff_time());
issuc = 1;
} else {
WORLD.DATA.set_temp('lunar15', -1, UTIL.diff_time());
issuc = -1;
}
}
return issuc > 0;
}
};

341
os/world.js Normal file
View File

@@ -0,0 +1,341 @@
require("./util/util");
const db = require("./util/data");
WORLD = {
USERS: [],
COMMANDS: {},
SKILLS: {},
ROOMS: {},
RUN_ROOMS: [],
DEFAULT_SKILLS: {},
AREAS: [],
TASKS: [],
SYSTEMTASKS: [],
USER_EVENTS: [],
OBJ_STROE: new Map(),
NPC_STROE: new Map(),
HEARTBEATCOUNT: 0,
RECEIVED: [],
LOGS: [],
SERVERID: 0,
SERVERS: [],
CONNECT_COUNT: 0,
DATA: require('./data'),
USERLOGIN: require('./login'),
DB: db,
SocketCount: 0,
LISTENER: require("./ws"),
max_connect_count: 1100,
max_user_count: 5100,
MESSAGE: {
stores: new Map(),
NOTICES: [],
},
STATS: {
TOPS: [],
EXP: [],
SCORE: [],
WEAPON: [],
},
status: -1,//-1关闭 0正常 >1 用户等级>连接
SocketIn: function () {
this.SocketCount++;
},
connect: function (socket) {
if (WORLD.status < 0)
return socket.end();
if (!WORLD.check_connect(socket))
return socket.end();
socket.user = new USER();
socket.user.socket = socket;
socket.user.wait_input = this.USERLOGIN.check_session.bind(this.USERLOGIN);
socket.setTimeout(60000);
},
check_connect: function (socket) {
return true;
},
before_login: function (user) {
if (this.status < 0) return false;
if (this.status === 0) return true;
return this.status <= user.user_level;
},
disconnect: function (socket) {
if (socket.user) {
socket.user.socket = null;
socket.user.disconnect();
}
this.SocketCount--;
if (socket.oserver) {
socket.oserver.disconnect();
socket.oserver = null;
}
}, request: function (request, socket) {
if (!request) return;
var user = socket.user;
if (!user) {
return;
}
if (user.request_count > 20) {
return user.send("不要急,慢慢来。");
}
user.request_count = user.request_count + 1;
var time = Date.now();
try {
user.command(request);
} catch (e) {
console.log(user.name, "命令错误:", request, e.message, e.stack);
WORLD.log(user, request, e.message + e.stack);
}
WORLD.RECEIVED.push({
time: time,
cmd: request + " " + (Date.now() - time).toString(),
user: user.id
});
if (WORLD.RECEIVED.length > 1000) {
WORLD.saveRequest();
}
}, saveRequest: function () {
db.saveRequest(WORLD.RECEIVED);
WORLD.RECEIVED.length = 0;
},
startup: async function (sid) {
if (sid) {
sid = parseInt(sid);
this.SERVERS = await db.getServers();
this.SERVER = this.getServer(sid);
} else {
this.SERVER = __CONFIG.def_server;
}
if (!this.SERVER) throw "服务器设置错误,无法启动";
this.SERVERID = this.SERVER.id;
await db.initDataDir();
loadResource();
await this.DATA.load();
await this.LISTENER.start(this.SERVER.port);
console.log("服务", this.SERVER.name, "(" + this.SERVERID + ")启动");
console.log("ws://" + this.SERVER.ip + ":" + this.SERVER.port);
this.heart_beat_service = setInterval(WORLD.heart_beat, __CONFIG.HEARTBEAT);
this.status = __CONFIG.CONNECT_LEVEL ?? 0;
this.on_startup();
if (this.status > 0)
console.log('当前允许级别' + this.status + "账号登陆");
},
sendAll: function (msg) {
for (var i = 0; i < WORLD.USERS.length; i++) {
WORLD.USERS[i].send(msg);
}
},
getUser: function (id) {
if (!id) return;
for (var i = 0; i < WORLD.USERS.length; i++) {
if (WORLD.USERS[i].id == id) return WORLD.USERS[i];
}
}, find_user: function (name) {
if (!name) return;
for (var i = 0; i < WORLD.USERS.length; i++) {
if (WORLD.USERS[i].name == name) return WORLD.USERS[i];
}
},
on_user_login: function (user) {
},
on_user_cross_login: function (user) {
},
on_startup: function () {
},
on_user_quit: function (user) {
},
on_user_relogin: function (user) {
}
,
on_heart_beat: function (user) {
},
heart_beat: function () {
var avtived_obj = null;
try {
const dt = Date.now();
WORLD.CONNECT_COUNT = 0;
for (let i = 0; i < WORLD.USERS.length; i++) {
avtived_obj = WORLD.USERS[i];
if (avtived_obj.socket) WORLD.CONNECT_COUNT++;
avtived_obj.heart_beat(dt);
}
WORLD.on_heart_beat(dt);
for (let i = 0; i < WORLD.RUN_ROOMS.length; i++) {
avtived_obj = WORLD.RUN_ROOMS[i];
avtived_obj.heart_beat(dt);
}
WORLD.HEARTBEATCOUNT++;
if (WORLD.HEARTBEATCOUNT > 720) {
WORLD.HEARTBEATCOUNT = 0;
WORLD.save();
console.log("数据已备份%d", Date.now() - dt);
}
} catch (e) {
console.log(avtived_obj ? (avtived_obj.path ?? avtived_obj.name) : "", "心跳错误:", e, e.stack);
WORLD.log(null, e.message, e.stack);
}
},
login_out: function (user) {
this.on_user_quit(user);
if (user.serverid === WORLD.SERVERID) {
user.save();
}
WORLD.USERS.remove(user);
if (user.socket) {
try {
user.socket.end();
user.socket.destroy();
} catch (e) {
console.log(e.message, e.stack);
}
}
}, send: function (text) {
for (var i = 0; i < this.USERS.length; i++) {
this.USERS[i].send(text);
}
}, log: function (user, cmd, msg) {
WORLD.LOGS.push({
time: Date.now(),
cmd: cmd,
user: user ? user.name : "",
msg: msg
});
if (WORLD.LOGS.length > 500) {
db.saveLogs(WORLD.LOGS);
WORLD.LOGS.length = 0;
}
}, saveLog: function () {
db.saveLogs(WORLD.LOGS);
WORLD.LOGS.length = 0;
}
,
is_server: function (user) {
return user.serverid == WORLD.SERVERID;
},
save: async function () {
var roles = [];
for (var i = 0; i < WORLD.USERS.length; i++) {
if (WORLD.USERS[i].serverid != WORLD.SERVERID) continue;
roles.push(WORLD.USERS[i].getData());
}
try {
console.time('saved');
await db.saveRoles(roles);
console.log('玩家数据已保存');
await this.DATA.save();
console.log('全局数据已经保存');
await this.saveLog();
await this.saveRequest();
console.log('日志数据已经保存');
console.timeEnd('saved');
return true;
} catch (error) {
console.error('玩家数据保存失败', error.message);
return false;
}
},
writeHeapSnapshot: function () {
let v8 = UTIL.require('v8');
let dt = new Date();
let fname = __PATH.DATA + "/" + dt.getFullYear() + "_" + dt.getMonth() + "_"
+ dt.getDate() + "_" + dt.getHours() + "_" + dt.getMinutes() + ".heapsnapshot";
v8.writeHeapSnapshot(fname);
console.log('快照保存到', fname);
}
,
loadLocalData: function () {
let data = db.getLocalRoles();
if (!data || !data.length) return;
console.log("加载上次未保存的本地用户%d", data.length);
for (let i = 0; i < data.length; i++) {
let user = new USER();
user.loadData(data[i]);
this.USERS.push(user);
}
db.deleteLocalRoles();
},
on_cross_response: function (id, sid) {
//允许跨服
},
can_cross: function (id) {
//允许跨服
}, on_user_die: function (me, killer, corpse) {
}, on_resource_loaded: function () {
}
};
function loadResource() {
let fs = require("fs");
function readdir(basePath, path) {
path = path || basePath;
let files = fs.readdirSync(path);
let count = 0;
for (let i = 0; i < files.length; i++) {
let sub_path = path + files[i];
let stat = fs.statSync(sub_path);
if (stat.isDirectory()) {
count += readdir(basePath, sub_path + "/");
} else {
let fname = sub_path.replace(basePath, "").replace(".js", "");
BASE.CREATE(basePath, fname);
count++;
}
}
return count;
}
try {
let sum = 0;
let count = readdir(__PATH.EXTENDS);
console.log("%s %d ", __PATH.EXTENDS, count);
sum += count;
count = readdir(__PATH.COMMAND);
console.log("%s%d ", __PATH.COMMAND, count);
sum += count;
count = readdir(__PATH.FAMILY);
console.log("%s %d ", __PATH.FAMILY, count);
sum += count;
count = readdir(__PATH.OBJ);
console.log("%s %d ", __PATH.OBJ, count);
sum += count;
count = readdir(__PATH.AREA);
console.log("%s %d ", __PATH.AREA, count);
sum += count;
count = readdir(__PATH.SKILL);
console.log("%s %d ", __PATH.SKILL, count);
sum += count;
count = readdir(__PATH.MAP);
console.log("%s %d ", __PATH.MAP, count);
sum += count;
count = readdir(__PATH.TASK);
console.log("%s %d ", __PATH.TASK, count);
sum += count;
console.log('资源脚本加载%d', sum);
WORLD.on_resource_loaded();
} catch (e) {
console.log("error: ", e, e.stack);
}
}

49
os/ws.js Normal file
View File

@@ -0,0 +1,49 @@
const ws = require("./net-ws");
const server = new ws({
SSL: false,
KEY: "",
CERT: "",
PASSWORD: ""
});
server.start = async function (port) {
return new Promise((resolve) => {
this.listen(port, resolve);
});
}
// WORLD.LISTENER = server;
server.onConnect = function (socket) {
WORLD.connect(socket);
}
server.onSocketIn = function (socket) {
WORLD.SocketIn(socket);
}
server.onReceive = function (msg, socket) {
WORLD.request(msg, socket);
}
server.onClose = function (msg, socket) {
console.log("server closed");
}
server.onClientClose = function (socket, e) {
WORLD.disconnect(socket);
if (!socket.destroyed) {
socket.destroy();
}
}
server.onClientTimeout = function (socket, e) {
if (socket && (!socket.user || !socket.user.id)) {
socket.end();
}
}
server.onClientError = function (socket, e) {
if (socket && socket.user) {
if (!socket.destroyed) {
socket.destroy();
}
}
}
module.exports = server;