Saturday, May 31, 2014

Advanced Scripting (AMX Mod X) - Bài Tập Nâng Cao

Advanced Scripting (AMX Mod X)




This article will briefly summarize some of the more advanced topics of AMX Mod X Scripting.

Contents

 [hide

Tasks

Tasks là giờ mà cho phép bạn chạy mã ở một khoảng thời gian, hoặc một lần hoặc lặp đi lặp lại. Họ là hữu ích cho những thứ như chờ đợi một vài giây, thiết lập đối tượng để tiêu diệt bản thân, hoặc chỉ lặp đi lặp lại một công việc hơn và hơn.
Một tác vụ có thể được thiết lập trong một số cách khác nhau. Hàm thực tế là set_task ():
set_task(Float:time,const function[],id = 0,parameter[]="",len = 0,flags[]="", repeat = 0)
Các tham số trong Hàm
  • Float:time - Thời gian lặp lại (nhỏ nhất là   0.1 giây)
  • function[] - Một chuỗi có chứa các hàm Public để chạy trên bộ đếm thời gian
  • id - Một id duy nhất để gán cho các tác vụ
  • parameter - Một mảng chứa dữ liệu để gửi cho hàm
  • len - Kích thước của mảng để gửi cho các hàm
  • flags - Một trong những điều sau đây:
    • "a" -Lặp lại với số lần xác định
    • "b" - Lặp lại liên tục và không dừng
    • "c" - do task on time after a map timeleft
    • "d" - do task on time before a map timeleft
  • repeat - Nếu flags là "a",  thì repeat là số lần lặp lại các tác vụ
An example of a task is below. It will slap a specified player 5 times, once per second.
//the timed function receives the parameter array and its task id
public slapTask(params[], id)
{
   new player = params[0]
   user_slap(player, 5)
}
 
public start_slapping(id)
{
   new params[1]
   params[0] = id
   // chúng ta không cần một id cụ thể
   set_task(1.0, "slapTask", 0, params, 1, "a", 5)
}
Lưu ý rằng nếu bạn chỉ định 0 cho chiều dài tham số, sau đó hàm task sẽ như thế này:
public slapTask(id)

Menus

Menus là những tin nhắn HUD cung cấp cho một người chơi một sự lựa chọn các tùy chọn để lựa chọn. Nó khá lộn xộn để giải quyết nhưng có thể rất hữu ích cho những thứ như bỏ phiếu và lựa chọn lệnh.
Menus phải được đăng ký bởi hai điều - một tập hợp các "chìa khóa" mà nói như thế nào nhiều lựa chọn để đăng ký và một chuỗi trong đó xác định trình đơn như là duy nhất. Chuỗi này phải xuất hiện trong đầu của mỗi đơn bạn muốn kích hoạt hàm của bạn. Khi bạn hiển thị một menu, bạn có thể hiển thị nó cho một hoặc nhiều người chơi. Một khi họ nhấn một phím, kết quả của phím ấn của chính của họ sẽ được gửi đến các hàm mà bạn đã đăng ký trình đơn.
Ví dụ của chúng tôi, chúng tôi sẽ thực hiện một trình đơn có hiển thị một danh sách các súng: AK47, M4A1, AWP hay, đến một cầu thủ. Cho dù anh ta lựa chọn, anh ta sẽ được đưa ra.
#include <amxmodx>
#include <amxmisc>
#include <fun>
public plugin_init()
{
    register_plugin("Menu Demo", "1.0", "BAILOPAN")
    new keys = MENU_KEY_0|MENU_KEY_1|MENU_KEY_2
    register_menucmd(register_menuid("Which Weapon?"), keys, "giveWeapon")
}
Hai lệnh là rõ ràng ở đây - register_menuid và register_menucmd. register_menuid đăng ký một cụm từ ngắn mà sẽ xuất hiện ở phần đầu của menu, sau đó trả về một id. Id này là tham số đầu tiên register_menucmd. Tham số thứ hai đến register_menucmd là cấu hình chính. Menu của chúng tôi sẽ có ba lựa chọn, vì vậy chúng tôi đã thêm ba phím menu in Trong thực tế, đây là những lá cờ phép toán tổng cộng "7", nhưng điều đó không quan trọng. Tham số cuối cùng là chức năng nào đó sẽ xử lý kết quả trình đơn.
Tiếp theo, làm thế nào để chúng tôi hiển thị trình đơn? Chúng ta hãy làm một giao diện điều khiển lệnh nhanh chóng: "giveme".
public plugin_init()
{
    register_plugin("Menu Demo", "1.0", "BAILOPAN")
    new keys = MENU_KEY_0|MENU_KEY_1|MENU_KEY_2
    register_menucmd(register_menuid("Which Weapon?"), keys, "giveWeapon")
    register_clcmd("giveme", "showWeaponMenu")
}
register_clcmd tương tự như register_concmd, ngoại trừ nó chỉ có hai tham số. Nó được sử dụng đến đăng ký bất kỳ lệnh một khách hàng có thể sử dụng (trừ những người đặc biệt, như + tấn công).
// Hàm clcmd sẽ chỉ cung cấp cho chúng tôi id người chơi 
public showWeaponMenu(id)
{
    new menu[192]
    new keys = MENU_KEY_0|MENU_KEY_1|MENU_KEY_2
 
    format(menu, 191, "Which Weapon?^n^n1. AK47^n2. M4A1^n3. AWP")
    show_menu(id, keys, menu)
    return PLUGIN_HANDLED
}
 
// hàm Menu của chúng tôi sẽ nhận được id người chơi và phím họ nhấn
public giveWeapon(id, key)
{
    //key will start at zero
    if (key == 0)
    {
         give_item(id, "weapon_ak47")
    } else if (key == 1) {
         give_item(id, "weapon_m4a1")
    } else if (key == 2) {
         give_item(id, "weapon_awp")
    }
}
Và chúng tôi đang làm! Dòng format có thể là một chút bối rối. Các "^ n" có nghĩa là "dòng mới", vì vậy menu trông độc đáo định dạng. Bạn có thể sử dụng bổ khác trong VGUI2 mods, chẳng hạn như "\ w" cho văn bản trắng, "\ r" cho văn bản màu đỏ, và "\ y" cho văn bản màu vàng. Khi một người chơi loại lệnh, anh sẽ thấy menu. Khi anh ta đánh một chìa khóa, chức năng giveWeapon sẽ nhận được id của mình và số lượng phím anh ta nhấn. Sau đó, anh ta sẽ có một khẩu súng tương ứng với những gì ông đã chọn.

Events/Messages - Sự Kiện/Tin Nhắn

Cho minh họa này, chúng ta sẽ mở rộng ví dụ trên. Mỗi khi một người chơi hồi sinh, ông sẽ được hiển thị menu để lựa chọn một vũ khí (lưu ý - chúng ta đang bỏ qua những thứ khác như chặn mua và loại bỏ buyzones, đây chỉ là minh họa).
Tin nhắn là một cách để khách hàng Half-Life để nói chuyện với các máy chủ, và ngược lại. Chúng được định dạng đặc biệt danh sách các tham số. Ví dụ, thông báo "DeathMsg" (tin nhắn id 83) có ba thông số: Kẻ tấn công (byte), nạn nhân (byte), và vũ khí (string). Bạn có thể có thể nắm bắt các thông điệp hoặc gửi cho họ. Ở đây, chúng tôi sẽ làm minh họa đơn giản của cả hai. Đầu tiên chúng ta hãy làm cho nó để người dùng nhận được Menu súng của họ khi họ sinh ra.
public plugin_init()
{
    register_plugin("Menu Demo", "1.0", "BAILOPAN")
    new keys = MENU_KEY_0|MENU_KEY_1|MENU_KEY_2
    register_menucmd(register_menuid("Which Weapon?"), keys, "giveWeapon")
    // flags - b có nghĩa là "gửi đến một mục tiêu", e có nghĩa là "mục tiêu là còn sống" 
     // sự kiện này được gửi khi một người chơi hồi sinh
    register_event("ResetHUD", "hook_hud", "be")
}
 
public hook_hud(id)
{
    // kể từ khi chúng tôi xác định không có tham số cho các task, 
     // nhiệm vụ id ​​sẽ được trao cho các hàm 
     // đây là hữu ích vì chúng ta có thể tái sử dụng hàm của chúng tôi 
     // hiển thị hàm Menu cho một id
    set_task(0.2, "showWeaponMenu", id)
}
Lưu ý rằng chúng tôi đã thiết lập một sự chậm trễ nhỏ khi chúng tôi nhận được thông báo - điều này là để đảm bảo rằng người sử dụng đã có thời gian để hồi sinh. register_event có thể đưa thêm nhiều thông số để giúp hạn chế trường hợp bạn bắt - ví dụ như chỉ phù hợp với các thông số nhất định. Bạn có thể đọc thêm về điều này trong các tài liệu tham khảo hàm.
Bây giờ, hãy nói rằng chúng tôi muốn tìm ra khi một cầu thủ đã chết ...
public plugin_init()
{
    register_plugin("Message Demo", "1.0", "BAILOPAN")
    // thông báo này thông báo cho mọi người về một cái chết, vì vậy chúng tôi sử dụng
    // flag "a" - global sự kiện
    register_event("DeathMsg", "hook_death", "a")
}
 
public hook_death()
{
    new Killer = read_data(1) // lấy các tham số thông điệp đầu tiên
    new Victim = read_data(2) //lấy các tham số thông điệp đầu tiên
    new headshot = read_data(3) //đây cũng là một headshot?
    new weapon[32]
    read_data(4, weapon, 31)  / / lấy tên vũ khí
}
Hoặc, giả sử chúng ta muốn thực hiện một hàm đơn giản để tạo ra một thông điệp chết:
stock make_deathMsg(Killer, Victim, const weapon[])
{
    // message_begin bắt đầu một tin nhắn. KHÔNG BAO GIỜ bắt đầu hai thông báo cùng một lúc. 
     // MSG_ALL có nghĩa là gửi tin nhắn đến tất cả mọi người 
     // get_user_msgid trả về id của một tên tin nhắn 
     // {0,0,0} là vector nguồn gốc - không được sử dụng ở đây 
     // 0 là mục tiêu - không có mục tiêu cụ thể ở đây
    message_begin(MSG_ALL, get_user_msgid("DeathMsg"), {0,0,0}, 0) 
    write_byte(Killer)
    write_byte(Victim)
    write_string(weapon)
    message_end()
}
Để tìm hiểu thêm về thông điệp, tham khảo ý kiến ​​các HLSDK, AMX Mod X diễn đàn, các trang web lập trình HL-liên quan, hoặc những plugin khác. Để liệt kê các tin nhắn một mod đã, loại "trò chơi meta" trong giao diện điều khiển máy chủ (với metamod nạp). Bạn cũng có thể sử dụng register_message, phương pháp tin nhắn disection tiên tiến hơn được tìm thấy trong các mô-đun Engine.

Catching Log Messages

Nắm bắt tin nhắn đăng nhập không được sử dụng nhiều nữa, nhưng nó vẫn còn tốt để biết làm thế nào để làm điều đó. Như tin nhắn đăng nhập được gửi bởi các mod, AMX Mod X sẽ có thể bắt chúng và cho phép bạn treo chúng. Ví dụ, chúng ta hãy cung cấp cho tất cả mọi người $ 16,000 khi bắt đầu hiệp đấu .
Các tin nhắn đăng nhập cho trận đấu được gửi như thế này: Thế giới kích hoạt "Round_Start". AMX Mod X sẽ xem xét "World_triggered" như tham số đầu tiên và "Round_Start" như tham số thứ hai. Vì vậy:
#include <amxmodx>
 
public plugin_init()
{
    register_plugin("Log Demo", "1.0", "BAILOPAN")
    // điều này sẽ lọc cho hai tham số
    // roundstart là hàm public
    register_logevent("roundstart", 2, "0=World triggered", "1=Round_Start")
}
 
public roundstart()
{
    // thiết lập một sự chậm trễ nhỏ để đảm bảo tất cả mọi người sinh ra
    set_task(1.0, "roundDelay")
}
 
public roundDelay(taskId)
{
    new players[32], num
    get_players(players, num)
    new i
    for (i=0; i<num; i++)
    {
         cs_set_user_money(players[i], 16000)
    }
}
Bạn cũng có thể đọc các thông số ghi cụ thể với read_logdata (), mà chỉ có thể được sử dụng bên trong "plugin_log ()" forward:
//nhận được tất cả các tin nhắn đăng nhập
public plugin_log()
{
    new data[32]
    read_logdata(data, 31)
}

Multi-Lingual Support - Hỗ trợ đa ngôn ngữ

Thêm hỗ trợ đa ngôn ngữ đến một plugin có thể khó khăn, nhưng nó thường là đáng giá nếu bạn có khách hàng sẵn sàng để dịch chuỗi của bạn sang ngôn ngữ mẹ đẻ của họ.
Bước đầu tiên là xác định những gì cần phải được dịch. Giả sử bạn có một cuộc gọi như thế này:
new score = get_score()
client_print(id, print_chat, "[AMXX] Your score is %d", score)
Đây là một ứng cử viên tốt để được đa ngôn ngữ. Đầu tiên, tạo một tập tin txt. (Tốt nhất đặt theo tên plugin của bạn) và lưu nó vào addons \ amxmodx \ data \ lang \. Hãy sử dụng "myplugin.txt" cho ví dụ. Cho mỗi ngôn ngữ, thêm một mục vào tập tin. Mục được thiết lập là "chìa khóa", mà được kết hợp với 'chuỗi dịch. quan sát:
(addons\amxmodx\data\lang\myplugin.txt)

[en]
SCORE_MSG = Your score is %d

[de]
SCORE_MSG = Ihr Spielergebnis ist %d

[es]
SCORE_MSG = Su cuenta es %d

[fr]
SCORE_MSG = Votre score est %d
Sau đó, trong plugin_init (), bạn phải đăng ký các language keys:
public plugin_init()
{
    ...
    //giả định được đặt trong amxmodx\data\lang
    register_dictionary("myplugin.txt")
}
Bây giờ, ở đây có phần khó khăn. Đa ngôn ngữ API AMX Mod X được xây dựng thành các định dạng () thói quen phong cách. Đối với bất cứ điều gì giống như hoặc sử dụng các chuỗi format ()-style, bạn có thể sử dụng API ML.
    client_print(id, print_chat, "[AMXX] %L", id, "SCORE_MSG", get_score())
Hãy phá vỡ điều này. Đối với mỗi %L xuất hiện, chúng ta cần ít nhất hai tham số. Tham số đầu tiên là mục tiêu. Này phải là một id người chơi, LANG_SERVER (có nghĩa là chương trình bằng ngôn ngữ mẹ đẻ của máy chủ), hoặc LANG_PLAYER. LANG_PLAYER là một sửa đổi lần đặc biệt mà chỉ nên được sử dụng khi gửi một thông điệp tới tất cả người chơi - có nghĩa là "chương trình trong ngôn ngữ mẹ đẻ của mỗi người chơi". Tham số thứ hai là chuỗi phím để xác định các cụm từ ngôn ngữ để dịch. Cuối cùng, nếu chuỗi dịch đòi hỏi bất kỳ thông số riêng của mình (chúng ta cần% d, một số nguyên), mà phải được thêm vào như là tốt.
Bạn có thể nhận được thiết kế rất phức tạp với điều này, nhưng nó là khuyến cáo rằng bạn giữ cho mọi thứ đơn giản cho rõ ràng. Đây là một ví dụ cuối cùng sử dụng tin nhắn toàn cầu để tất cả người chơi, giả định rằng Hello chính là dịch đúng trong tất cả các ngôn ngữ có sẵn:
    client_print(0, print_chat, "[AMXX] %L", LANG_PLAYER, "HELLO")

SQL Support - SQL Hỗ trợ

Hỗ trợ SQL đã được cải thiện rất nhiều trong AMX Mod X. Có một tập hợp chung của người bản xứ làm việc với một trình điều khiển duy nhất, do đó, miễn là một (và chỉ một) mô-đun SQL được nạp, SQL (hoặc DBI) người bản địa sẽ làm việc. Đây là một mồi ngắn về cách sử dụng người bản địa DBI:
// Tạo một kết nối
 new Sql:mysql = dbi_connect("localhost", "dvander", "pass", "dbase")
 
// Nếu kết nối nhỏ hơn 1, nó là xấu
 if (mysql < SQL_OK) {
  new err[255]
  new errNum = dbi_error(mysql, err, 254)
  server_print("error1: %s|%d", err, errNum)
  return 1
 }
 
 server_print("Connection handle: %d", mysql)
//Chạy một truy vấn
 new Result:ret = dbi_query(mysql, "INSERT INTO config (keyname, val) VALUES ('amx', 'yes')")
 
// Nếu truy vấn là ít hơn RESULT_NONE, nó đã thất bại
 if (ret < RESULT_NONE) {
  new err[255]
  new errNum = dbi_error(mysql, err, 254)
  server_print("error2: %s|%d", err, errNum)
  return 1
 }
 
//Làm một truy vấn lựa chọn
 new Result:res = dbi_query(mysql, "SELECT * FROM config")
 
//Nếu truy vấn là nhỏ hơn hoặc bằng RESULT_FAILED, bạn có một kết quả không hợp lệ và không thể làm bất cứ điều gì với nó.
 if (res <= RESULT_FAILED) {
  new err[255]
  new errNum = dbi_error(mysql, err, 254)
  server_print("error3: %s|%d", err, errNum)
  return 1
 }
 
 server_print("Result handle: %d", res)
 
// Lặp qua tập hợp kết quả
 while (res && dbi_nextrow(res)>0) {
  new qry[32]
// Lấy cột/lĩnh vực được gọi là "KeyName" từ tập hợp kết quả
  dbi_result(res, "keyname", qry, 32)
  server_print("result: %s", qry)
 }
 
// Miễn phí tập hợp kết quả
 dbi_free_result(res)
 
// Đóng kết nối
 dbi_close(mysql)

Regular Expressions

Biểu thức chính quy cho phép bạn mô tả cách thức mà để phá vỡ chuỗi. Chúng cực kỳ mạnh mẽ. AMX Mod X sử dụng tương thích thư viện RE Perl, bạn có thể đọc các chi tiết cụ thể tại trang web của họ. AMX Mod X cung cấp biểu thức thông thường với các mô-đun regex_amxx. Đây là một ví dụ ngắn:
#include <amxmodx>
#include <regex>
 
public plugin_init()
{
    register_plugin("Regex", "1.0", "BAILOPAN")
    register_srvcmd("amx_regex", "cmdtest")
}
 
public cmdtest()
{
    new str[] = "It's Walky!"
// mô hình này sẽ phù hợp với bất kỳ chuỗi, trong đó có
// Hai nhóm ký tự cách nhau bởi một khoảng trống
// Hai nhóm là chuỗi con 1 và 2
    new pattern[] = "(.+) (.+)"
 
    new num, error[128]
    //str = string
    //pattern = mô hình để sử dụng 
    //num = mã trả về đặt biệt
    //error = Nếu có một lỗi nó sẽ ở đây.
    //127 - error's max length
    new Regex:re = regex_match(str, pattern, num, error, 127)
 
    server_print("Result=%d, num=%d, error=%s", re, num, error)    
 
    //REGEX_OK means there was a match
    if (re >= REGEX_OK)
    {
        new str2[64]
        new i
        // kể từ khi nó trở REGEX_OK, num có 
        // Số lượng các chuỗi con phù hợp của mô hình. 
        // chuỗi con đầu tiên (0) dường như để phù hợp với toàn bộ chuỗi.
        for (i=0; i<num; i++)
        {
            regex_substr(re, i, str2, 63)
            server_print("Substring %d: %s", i, str2)
        }
        // biểu thức khớp thường xuyên sử dụng bộ nhớ. 
         // bạn phải giải phóng nó nếu bạn nhận được REGEX_OK 
         // Điều này cũng sẽ thiết lập lại 0.
        regex_free(re)
    }
 
    // lưu ý các mẫu biểu hiện thường xuyên không hợp lệ
// Nó sẽ trả lại REGEX_PATTERN_FAIL, -1
    re = regex_match("Bruno the Bandit", ".+(]", num, error, 127)
 
    server_print("Result=%d, num=%d, error=%s", re, num, error)
}
Nếu bạn biên dịch và chạy kịch bản này (amx_regex trong máy chủ giao diện điều khiển), bạn sẽ thấy kết quả như sau:
Result=1, num=3, error=
Substring 0: It's Walky!
Substring 1: It's
Substring 2: Walky!
Result=-1, num=4, error=missing ) 
Lưu ý rằng các tham số thứ ba "regex_match ()" là đặc biệt. Đó là một trong hai số chuỗi con, một mã lỗi trận đấu, hoặc một vị trí lỗi mô hình (tùy thuộc vào giá trị trả về).

Entities - Thực Thể

Tất cả các thứ trên map bạn đang chơi như cái thúng, con tin, ô tô… và kể cả cả player bạn đang điều khiển, súng ống .. tất tần tật đều là thực thể cả.
Thực thể cơ bản bất kỳ cấu trúc năng động trong Half-Life. Người chơi, vũ khí, lựu đạn, và các đối tượng nhỏ khác đặt xung quanh là các thực thể. Họ có một duy nhất "thực thể id" mà bạn có thể sử dụng để thay đổi giá trị của họ.
Tôi sẽ không đi vào điều này quá sâu vì nó là một chủ đề phức tạp, nhưng các mô-đun cơ bản địa có tính năng cho phép bạn thay đổi các thuộc tính của các thực thể, hoặc tìm kiếm các thực thể trong trò chơi bằng class / chủ sở hữu của họ (lớp là loại thực thể, chẳng hạn như "người chơi").
Trong ví dụ này, chúng tôi sẽ làm cho một thực thể trông giống như một người chơi cầm một khẩu súng giả.
 //tạo ra một thực thể cơ bản
 entid = create_entity("info_target")
 //thiết lập className của nó
 entity_set_string(entid, EV_SZ_classname, "some_guy")
 //thiết lập model của nó
 entity_set_model(entid, "models/w_suit.mdl")
 new Float:Vec[3] = {0.0,0.0,0.0}
 //thiết lập nguồn gốc của nó
 entity_set_origin(entid, Vec)
 //thiết lập một số tính chất cơ bản
 entity_set_int(entid, EV_INT_solid, 1)
 entity_set_int(entid, EV_INT_movetype, 6)
 //tạo vũ khí - nhờ có pimp_daddy!
 entWeapon = create_entity("info_target")
 entity_set_string(entWeapon, EV_SZ_classname, weapString)
 //thiết lập để hiện theo
 entity_set_int(entWeapon, EV_INT_movetype, MOVETYPE_FOLLOW)
 entity_set_int(entWeapon, EV_INT_solid, SOLID_NOT)
 entity_set_edict(entWeapon, EV_ENT_aiment, entid)
 entity_set_model(entWeapon, "models/p_m4a1.mdl")
Bạn cũng có thể thay đổi những điều cơ bản khác, chẳng hạn như:
//cách set_user_armor () hoạt động trong mô-đun fun
stock set_armor(player, Float:value)
{
    entity_set_int(player, EV_FL_armorvalue, value)
}

FakeMeta

FakeMeta là thế hệ tiếp theo của kịch bản Half-Life . Về cơ bản nó cho phép bạn viết plugin metamod trong Pawn.Nó là vô cùng mạnh mẽ, và vì lý do này, nó sẽ không thực sự được trình bày ở đây. Đây chỉ là để cho bạn biết những gì nó có khả năng.
  •  Gọi Engine/DLL
    • Có hai loại chức năng trong không gian tên HL - hàm Engine và hàm DLL (DLL hàm là những thư viện trò chơi / mod cần export). Cả hai có thể được gọi bằng cách sử dụng mô-đun FakeMeta sử dụng dllfunc () và engfunc () người bản địa. Các thông số được truyền trực tiếp vào metamod, vì vậy hãy cẩn thận! Bạn có thể dễ dàng sụp đổ một máy chủ làm điều sai trái.
  • Engine/DLL Hooks
    • Như đã nói ở trên, HL cung cấp Engine và DLL hàm. Bạn cũng có thể hooks / supercede các cuộc gọi sử dụng register_forward. Bạn có thể supercede các cuộc gọi sử dụng fm_return () và quay trở lại PLUGIN_HANDLED. Một lần nữa, hãy chắc chắn bạn biết những gì bạn đang làm. Hooks bị thay đổi sẽ gây ra sự cố.
  • Easy entity manipulation
    • FakeMeta thay thế entity__ Engine () chức năng với một người bản địa gọi là "PEV ()" và "set_pev ()". Chúng là chút dễ dàng hơn để sử dụng. Để biết thêm thông tin xem các fakemeta include.
  • Private Offset Hacking
    • Hiệu số riêng là hiệu số thành một khối bộ nhớ được gọi là "pvPrivateData". Hiệu số bên phải thường có thể sửa đổi các tính năng trò chơi cụ thể, chẳng hạn như tiền trong Counter-Strike, hoặc tài nguyên trong tự nhiên-lựa chọn. Tuy nhiên, hiệu số sai có thể gây ra sự cố trò chơi.

0 nhận xét:

Post a Comment