本文转载自小猫的世界MUD,原文链接:https://catworld.muds.me/forum/viewtopic.php?f=23&t=740
一般 NPC 都會攜帶並使用一把武器與多件防具 這些裝備被運用的方式不外乎是
- wield/wear/unwield/remove 時變更 NPC 的能力
- 戰鬥時發生磨損, 間接降低 NPC 的能力
- 透過 init() 或 heart_beat() 提供其他功能
除非特例, 一般來說 NPC 從生成到死亡只會經歷一場戰鬥, 裝備的磨損量非常之小, 基本上可以忽略 (不要讓 NPC 的裝備發生磨損)
而 wield/wear/unwield/remove 等指令主要透過 set_temp() 將裝備與 NPC 設定一連結 連結建立後, 其實那件裝備就只是躺在 NPC 的 all_inventory() 裡面, 不會有任何功能
結論: 只要能夠無視磨損, 不使用 init() 或 heart_beat() 的裝備 其實只需要一個物件就足夠 (全系統只需 clone 一份, 而且永遠不會呼叫 destruct()) 在每個 NPC 都放上一份, 是非常非常浪費系統資源的
(這個機制的效果非常顯著, CW 因此減少了約 15% 的物件數量)
實作範例:
建立共享装备
因為是共享式的物件, 這些裝備並不適合放置於某一隻 NPC 身上,所以要先建立一個 "容器" 來放置這些 "共享裝備",而建立這些物件的時後, 也需要一些額外的特殊手續,綜合上述, room 或 deamon 都可符合要求,在設計上需盡可能的極簡, 有放置物品的容器不可 update/dest, 否則東西就都不見了 XD
建立 /adm/daemons/shared.c
// 修改這個檔案一定要 reboot
// 絕對不能使用 update, 否則會導致共享物件全部消失
int query_max_encumbrance() {
// 最大化容器容量, 如果是 64bit 系統, 這個數字就可以再大些
return 2147483647;
}
// 建立共享裝備
object load_shared_equip(string file) {
object source, equip;
// 載入欲建立共享裝備的正本物件, 就是 clonep() 會回傳 0 的那一種
source = load_object(file);
if (source) {
// 嘗試從正本物件中取出之前建立的副本
equip = source->query_temp("shared_object");
if (!equip && inherits(F_EQUIP, source)) {
equip = new(file);
// 有數量限制的特殊處理
if (inherits(THROWING, equip)) {
equip->set_amount(1000);
}
// 標記為共享物件, 呼叫 is_shared() 時會回傳 1
equip->set_shared();
// 此 daemon 就是共享裝備的 environment(), 可防止物件被 clean_up() 回收
equip->move(this_object());
// 共享裝備只需要一份, 之後再建立的都是直接回傳這一份副本
source->set_temp("shared_object", equip);
}
}
return equip;
}
裝備繼承檔的修改
修改 /feature/equip.c
// 新增成員變數/getter/setter
nosave int shared;
void set_shared() {
if (previous_object() && file_name(previous_object()) == SHARE_D) {
shared = 1;
set("no_drop", 1); // 不能交易
set("no_get", 1); // 不能拾取
}
}
int is_shared() {
return shared;
}
// 增加 function 參數 owner
// 一般裝備是以 environment() 作為 owner, 呼叫時不會傳遞參數
// 共享裝備會傳入 owner, owner 為裝備的使用者
varargs int wear(object owner) {
// ...略
if (!shared) {
owner = environment();
}
// ...略
// 一般裝備才需要檢查是否為使用中
if (!shared && query("equipped")) return 1;
// ...略
}
varargs int wield(object owner) {
// ...略
if (!shared) {
owner = environment();
}
// ...略
// 一般裝備才需要檢查是否為使用中
if (!shared && query("equipped")) return 1;
// ...略
}
varargs int unequip(object owner) {
// ...略
if (!shared) {
owner = environment();
}
// ...略
// 一般裝備需要移除使用中的標記
if (!shared) {
delete("equipped");
}
// ...略
}
NPC 標準物件的修改
修改 /std/char/npc.c
// NPC 設定共享裝備的 function
void apply_shared_equip(string *files) {
object equip;
foreach (string file in files) {
equip = SHARE_D->load_shared_equip(file);
if (equip->query("armor_prop")) {
equip->wear(this_object()); // 防具
} else {
equip->wield(this_object()); // 武器
}
}
}
撰寫 npc 時的改變
原來的寫法
void create() {
// ...略
carry_object("/obj/area/obj/cloth")->wear();
carry_object(__DIR__"obj/cane")->wield();
}
共享裝備的寫法
// ...略
// 調整 look npc 時的裝備資料來源
// 從 all_inventory() 裡找出已裝備的物件
// 改成
// 使用 query_temp() 找出已裝備的物件
mixed equip = obj->query_temp("armor"); // 防具
inv = mapp(equip) ? values(equip) : ({});
// 副手武器
if (equip = obj->query_temp("secondary_weapon")) {
inv = ({ equip }) + inv;
}
// 主手武器
if (equip = obj->query_temp("weapon")) {
inv = ({ equip }) + inv;
}
// ...略
指令 look 的修改
// ...略
// 調整 look npc 時的裝備資料來源
// 從 all_inventory() 裡找出已裝備的物件
// 改成
// 使用 query_temp() 找出已裝備的物件
mixed equip = obj->query_temp("armor"); // 防具
inv = mapp(equip) ? values(equip) : ({});
// 副手武器
if (equip = obj->query_temp("secondary_weapon")) {
inv = ({ equip }) + inv;
}
// 主手武器
if (equip = obj->query_temp("weapon")) {
inv = ({ equip }) + inv;
}
// ...略
其他注意事項:
- 如果要限制武器的數量, 請勿使用此機制,如果要無***供應武器, 請記得修改 combined 的 set_amount() 控制共享裝備的數量
- 請於裝備磨損機制中加上檢查 is_shared(), 共享裝備理應不會有任何磨損
- 如果有卸除/破壞武器的技能, 須針對共享裝備特別處理, 以 unquip() 取代 destruct()
- 原來的 npc 裝備機制仍然可以正常運行, 不適合共享的裝備, 可繼續使用原來的寫法
- 如果一件裝備, 只有一隻 NPC 在用, 改用共享裝備仍然是有效益的, 至少可以免去物件 create/destruct 的迴圈