這些裝備被運用的方式不外乎是
1. wield/wear/unwield/remove 時變更 NPC 的能力
2. 戰鬥時發生磨損, 間接降低 NPC 的能力
3. 透過 init() 或 heart_beat() 提供其他功能
除非特例, 一般來說 NPC 從生成到死亡只會經歷一場戰鬥,
裝備的磨損量非常之小, 基本上可以忽略 (不要讓 NPC 的裝備發生磨損)
而 wield/wear/unwield/remove 等指令主要透過 set_temp() 將裝備與 NPC 設定一連結
連結建立後, 其實那件裝備就只是躺在 NPC 的 all_inventory() 裡面, 不會有任何功能
結論:
只要能夠無視磨損, 不使用 init() 或 heart_beat() 的裝備
其實只需要一個物件就足夠 (全系統只需 clone 一份, 而且永遠不會呼叫 destruct())
在每個 NPC 都放上一份, 是非常非常浪費系統資源的
(這個機制的效果非常顯著, CW 因此減少了約 15% 的物件數量)
實作範例:
1. 因為是共享式的物件, 這些裝備並不適合放置於某一隻 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;
}
2. 裝備繼承檔的修改
修改 /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");
}
...略
}
3. 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()); // 武器
}
}
}
4. 撰寫 npc 時的改變
原來的寫法
代碼: 選擇全部
void create() {
...略
carry_object("/obj/area/obj/cloth")->wear();
carry_object(__DIR__"obj/cane")->wield();
}
代碼: 選擇全部
void create() {
...略
// 不使用 carry_object(), 減少物件數量
apply_shared_equip({
"/obj/area/obj/cloth",
__DIR__"obj/cane",
});
}
5. 指令 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;
}
...略
6. 其他注意事項:
a. 如果要限制武器的數量, 請勿使用此機制
如果要無限量供應武器, 請記得修改 combined 的 set_amount() 控制共享裝備的數量
b. 請於裝備磨損機制中加上檢查 is_shared(), 共享裝備理應不會有任何磨損
c. 如果有卸除/破壞武器的技能, 須針對共享裝備特別處理, 以 unquip() 取代 destruct()
d. 原來的 npc 裝備機制仍然可以正常運行, 不適合共享的裝備, 可繼續使用原來的寫法
e. 如果一件裝備, 只有一隻 NPC 在用, 改用共享裝備仍然是有效益的, 至少可以免去物件 create/destruct 的迴圈