Tổng hợp các chức năng mod đơn giản trong NRO

Tổng hợp các chức năng mod đơn giản trong NRO

Tổng hợp các chức năng mod đơn giản trong NRO

Đây sẽ là bài viết tổng hợp lại các mod những chức năng đơn giản, cơ bản trong NRO. Các chức năng này có thể tạo ra chỉ với vài dòng code đơn giản. Mình sẽ update liên tục nên thiếu chức năng hoặc có sai sót nào các bạn có thể comment lại mình sẽ bổ sung.

Mình sử dụng Visual Studio để mod nên ai muốn dùng cách khác mà code lỗi thì tự tìm cách sửa lỗi nhé.

Các đoạn code chỉnh sửa trong game mình sẽ trình bày dưới dạng sau:

// <Tên class> <Thư viện khai báo thêm nếu có> ... (Mô tả doạn code nếu có) <Hàm nhận dạng> { ... <Câu lệnh nhận dạng> (nếu không có câu lệnh này và dòng ... phía trên tức là chỉnh sửa ở đầu hàm) /*Ghi chú thao tác với code (nếu có)*/ // Giải thích code (nếu có) <Code viết thêm> ... } ...

Các thẻ <Nội dung mô tả> sẽ bị thay thể bởi một thứ gì đó theo nội dung mô tả trong thẻ.

Mình sẽ sử dụng kỹ thuật xử lý lệnh chat mod NRO các bạn hãy xem trước tại: https://phucprotein.blogspot.com/2021/05/ki-thuat-xu-ly-lenh-chat-mod-nro.html


Mod đổi khu bằng lệnh chat

Để đối khu ta sử dụng lệnh:

Service.gI().requestChangeZone(zone, -1);

zone là khu ta muốn đổi sang, -1 là cái số viết cho có (admin code game làm thừa)

Mod đổi khu bằng lệnh chat

// Service ... public void chat(string text) { // Đổi khu theo lệnh chat kzX if (IsGetInfoChat<int>(text, "kz")) { int zone = GetInfoChat<int>(text, "kz"); // Lệnh đổi khu requestChangeZone(zone, -1); } ... } ...

Mod gọi NPC nhanh

Để gọi nhanh NPC trong map phù hợp dùng lệnh:

Service.gI().openMenu(idNpc);

idNpc: là id của NPC cần gọi mỗi NPC có một id riêng (đi hỏi người khác hoặc tự tìm)

Một số NPC có thể gọi bất cứ lúc nào.

Ví dụ mod lệnh gọi npc và lệnh gọi Ca Lích để tới tương lai.

// Service ... public void chat(string text) { if (text.Equals("tl")) { // id NPC Ca Lích là 38 openMenu(38); } else if (IsGetInfoChat<int>(text, "npc")) { int idNpc = GetInfoChat<int>(text, "npc"); openMenu(idNpc); } ... } ...

Mod tốc độ game và hiển thị thông báo

Cài đặt tốc độ game

Ta cần khai báo thư viện UnityEngine và sử dụng lệnh sau để thay đổi tốc độ game:

Time.timeScale = speed;

speed là tốc độ game có kiểu dữ liệu float giá trị càng lớn tốc độ game càng nhanh.

Cài đặt tốc độ game mặc định khi khởi động là 2:

// Main using UnityEngine; /*Nếu đã có không cần khai báo lại*/ ... private void Start() { Time.timeScale = 2f; ... } ...

Hiển thị thông báo

Sử dụng lệnh sau để hiển thị thông báo:

GameScr.info1.addInfo(<Nội dung thông báo>, 0);

Thêm lệnh chat cài đặt tốc độ game và thông báo tốc độ game hiện tại:

// Service using UnityEngine; ... public void chat(string text) { // Cài đặt tốc độ theo lệnh chat spdX if (IsGetInfoChat<float>(text, "spd")) { float speed = GetInfoChat<float>(text, "spd"); // Lệnh chỉnh tốc độ game Time.timeScale = speed; // Lệnh gọi thông báo GameScr.info1.addInfo("Tốc độ game: " + speed, 0); } ... } ...

Mod tốc độ chạy

Để chỉnh tốc độ chạy ta sẽ dùng lệnh sau:
Char.myCharz().cspeed = X;
X là tốc độ chạy nhân vật có kiểu dữ liệu int mặc định ban đầu là 4, giá trị càng lớn tốc độ chạy càng nhanh.

Do admin game đã fix nếu di chuyển nhanh tốc độ chạy sẽ bị hạ về 1 rồi trở về bình thường nên ta cần chỉnh sửa tốc độ chạy liên tục bằng cách bỏ lệnh trên vào một hàm update nào đó (thường là void GameScr.update())

Mod lệnh chat chỉnh tốc độ chạy:

// GameScr ... public override void update() { Char.myCharz().cspeed = tdc; ... } // Tạo một biến lưu giá trị tốc độ chạy public static int tdc = 8; ...
// Service ... public void chat(string text) { if (IsGetInfoChat<int>(text, "tdc")) { GameScr.tdc = GetInfoChat<int>(text, "tdc"); GameScr.info1.addInfo("Tốc độ chạy: " + GameScr.tdc, 0); } ... } ...

Mod hiển thị ID NPC cùng với tên NPC

Thay tất cả (g, this.template.name thành (g, this.template.name + "(" + this.template.npcTemplateId + ")" trong void paintName(mGraphics g) của class Npc.

Mình không muốn vì muốn ngắn mà lại chơi chiêu đổi tên NPC, sau này lỡ muốn dùng tên NPC thì mệt lắm. Các bạn thì có thể tự tìm và sử dụng cách đổi tên NPC

// Npc ... public new void paintName(mGraphics g) { ... if (this.template == null) { return; } /*Từ đây đến cuối hàm thay lại bằng code phía dưới*/ if (this.template.npcTemplateId == 3) { if (global::Char.myCharz().npcFocus != null && global::Char.myCharz().npcFocus.Equals(this)) { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - mFont.tahoma_7.getHeight() - 5, mFont.CENTER, mFont.tahoma_7_grey); } else { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - 3 - mFont.tahoma_7.getHeight(), mFont.CENTER, mFont.tahoma_7_grey); } this.dyEff = 60; } else if (this.template.npcTemplateId != 4) { if (this.template.npcTemplateId == 50 || this.template.npcTemplateId == 51) { if (this.duahau != null) { int num = 0; if (SmallImage.imgNew[this.duahau[this.duaHauIndex]] != null && SmallImage.imgNew[this.duahau[this.duaHauIndex]].img != null) { num = mGraphics.getImageHeight(SmallImage.imgNew[this.duahau[this.duaHauIndex]].img); } if (global::Char.myCharz().npcFocus != null && global::Char.myCharz().npcFocus.Equals(this)) { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - mFont.tahoma_7.getHeight() - num, mFont.CENTER, mFont.tahoma_7_grey); } else { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - 8 - mFont.tahoma_7.getHeight() - num + 16, mFont.CENTER, mFont.tahoma_7_grey); } } } else if (this.template.npcTemplateId == 6) { if (global::Char.myCharz().npcFocus != null && global::Char.myCharz().npcFocus.Equals(this)) { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - mFont.tahoma_7.getHeight() - 16, mFont.CENTER, mFont.tahoma_7_grey); } else { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - 8 - mFont.tahoma_7.getHeight(), mFont.CENTER, mFont.tahoma_7_grey); } } else { if (TileMap.mapID != 51) { int num2 = 15; if (this.template.npcTemplateId == 47) { num2 = 47; } if (global::Char.myCharz().npcFocus != null && global::Char.myCharz().npcFocus.Equals(this)) { if (TileMap.mapID != 113) { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - mFont.tahoma_7.getHeight() - num2, mFont.CENTER, mFont.tahoma_7_grey); } } else { num2 = 8; if (this.template.npcTemplateId == 47) { num2 = 40; } if (TileMap.mapID != 113) { mFont.tahoma_7_yellow.drawString(g, this.template.name + "(" + this.template.npcTemplateId + ")", this.cx, this.cy - this.ch - num2 - mFont.tahoma_7.getHeight(), mFont.CENTER, mFont.tahoma_7_grey); } } } this.dyEff = 65; } } } ...

Mod kích thước khi khởi động

Trong hàm void Start() của class Main ta chú ý các dòng sau:

// Main ... private void Start() { ... if (Main.isPC) { this.level = Rms.loadRMSInt("levelScreenKN"); if (this.level == 1) { // Cài đặt kích thước 720x320 nếu cấu hình màn hình nhỏ (this.level == 1) Screen.SetResolution(720, 320, false); } else { // Cài đặt kích thước 1024x600 cho cấu hình màn hình lớn Screen.SetResolution(1024, 600, false); } } } ...
  • Screen.SetResolution(width, height, false);: lệnh cài đặt kích thước cho cửa sổ.
    • width: chiều rộng.
    • height: chiều cao.
    • false viết theo người ta chứ mình cũng không biết.

Mod cố định kích thước màn hình khi khởi động là 240x320 (không bị ảnh hưởng bởi cấu hình màn hình lớn hay nhỏ):

// Main ... private void Start() { ... if (Main.isPC) { /* Xoá tất cả những thứ này this.level = Rms.loadRMSInt("levelScreenKN"); if (this.level == 1) { Screen.SetResolution(720, 320, false); } else { Screen.SetResolution(1024, 600, false); } */ Screen.SetResolution(240, 320, false); } } ...

Mod cấu hình game khi khởi động

Tìm đến class MotherCanvas hàm void checkZoomLevel(int w, int h) thay đổi giá trị biến mGraphics.zoomLevel trong này.

mGraphics.zoomLevel là cấu hình game mang giá trị 1 tương ứng với màn hình nhỏ (cấu hình thấp), từ 2 đến 4 tương ứng màn hình lớn (cấu hình cao, càng lớn ảnh sẽ càng rõ và đẹp).

Kinh nghiệm từng nghe của mình (chưa kiểm tra): Cấu hình cao treo sẽ đỡ lag hơn cấu hình thấp 🤣🤣 (2, 3, 4 như nhau nha không phải càng cao càng tốt).

Ví dụ mod cố định cấu hình game khi khởi động là cấu hình thấp (không phụ thuộc vào kích thước game):

// MotherCanvas ... public void checkZoomLevel(int w, int h) { /* Xoá tất cả code trước đó của hàm này if (Main.isWindowsPhone) { mGraphics.zoomLevel = 2; if (w * h >= 2073600) { mGraphics.zoomLevel = 4; } else if (w * h > 384000) { mGraphics.zoomLevel = 3; } } else if (!Main.isPC) { if (Main.isIpod) { mGraphics.zoomLevel = 2; } else if (w * h >= 2073600) { mGraphics.zoomLevel = 4; } else if (w * h >= 691200) { mGraphics.zoomLevel = 3; } else if (w * h > 153600) { mGraphics.zoomLevel = 2; } } else { mGraphics.zoomLevel = 2; if (w * h < 480000) { mGraphics.zoomLevel = 1; } }*/ mGraphics.zoomLevel = 1; } ...

Mod xoá tàu vũ trụ

Các bạn thêm đoạn code sau vào hàm void update() của class Teleport. Không giải thích thêm.

// Teleport ... public void update() { if (isMe) { Char.myCharz().isTeleport = false; if (type == 0) { Controller.isStopReadMessage = false; Char.ischangingMap = true; } Teleport.vTeleport.removeElement(this); return; } ... } ...

Sử dụng capsual bay

Để sử dụng capsual bay ta dùng lệnh:

// 193 là id capsual thường, 194 là id capsual đặc biệt Service.gI().useItem(0, 1, -1, 193); // Capsual thường /*Hoặc*/ Service.gI().useItem(0, 1, -1, 194); // Capsual vip

Để chọn map tới ta dùng lệnh:

Service.gI().requestMapSelect(index);

index: là thứ tự map cần đến, thứ tự hiển thị map của tab capsual được mở lên bắt đầu từ 0.

Ví dụ mod sử dụng capsual và lựa chọn map đầu tiên nhanh với phím tắt C:

// GameScr ... public override void updateKey() { ... else if (GameCanvas.keyAsciiPress == 114) { ChatTextField.gI().startChat(this, string.Empty); } else if (GameCanvas.keyAsciiPress == 'c') { /*typeCapsual là loại capsual sử dụng do mình tự qui ước mang giá trị 0 là không sử dụng capsual, 1 là sử dụng capsual thường, 2 là sử dụng capsual vip.*/ int typeCapsual = 0; // Kiểm tra capsual bay trong hành trang Item[] items = Char.myCharz().arrItemBag; for (int i = 0; i < items.Length; i++) { if (items[i] != null) { if (items[i].template.id == 194) { typeCapsual = 2; break; } if (items[i].template.id == 193) { typeCapsual = 1; } } } if (typeCapsual != 0) { if (typeCapsual == 1) { Service.gI().useItem(0, 1, -1, 193); } else { Service.gI().useItem(0, 1, -1, 194); } /* Nếu chỉ muốn bật capsual không chọn map đầu thì bỏ dòng dưới*/ Service.gI().requestMapSelect(0); } } ... } ...

Gọi đệ tử khi vào game

Nghe bảo vào game muốn gọi đệ tử ra up cần nhảy nhẹ một cái 🤣🤣. Chán cho mấy ông làm mod cũng nghe theo rồi cho acc vào game nhảy một phát nhìn hài lắm, up đệ các thứ nhảy lên nó cũng rời vị trí đi mất hay.

Muốn gọi đệ tử ra khi vào game chỉ cần di chuyển nhẹ một phát thôi và với code thì ta có thể làm mà mắt thường không nhận ra sự di chuyển.

Khi mod game hãy cố gắng tìm gốc rễ vấn đề rồi làm sao cho tối ưu nhất!

Ngoài ra mình thấy lúc mới chuyển map mà chưa di chuyển nhân vật đệ tử cũng chưa xuất hiện nên làm luôn một thể.

Mình tìm ra được hàm void loadInfoMap(Message msg)class Controller sẽ được gọi khi vào game và mỗi lần chuyển map. Vì vậy mình sẽ cho nhân vật di chuyển mỗi khi gọi hàm đó là được.

Lưu ý khi chỉnh trong class Controller dnSpy không hỗ trợ chỉnh sửa hàm void onMessage(Message msg) nên nếu sử dụng dnSpy ta phải sử dụng tính năng Edit Method (C#) để chỉnh sửa hàm void loadInfoMap(Message msg). Ta cũng có thể sử dụng Visual Studio 2019 để mod game sẽ không gặp phải tình trạng này.

// Controller ... public void loadInfoMap(Message msg) { ... Cout.LogError("----------DA CHAY XONG LOAD INFO MAP"); // Di chuyển rồi về lại vị trí cũ ngay lập tức Char.myCharz().cy--; Service.gI().charMove(); Char.myCharz().cy++; ... } ...

Mình phải bỏ phần auto skill và phần khoá vị trí đệ tử ra vì nó vượt quá mức cơ bản, nó sẽ sớm xuất hiện ở bài viết khác các bạn thông cảm

Nhận xét

Bài đăng phổ biến từ blog này

Kĩ thuật xử lý lệnh chat mod NRO

Hướng dẫn xoá đồ hoạ game giảm CPU hiệu quả NRO