III.1.3.1. Gameplay
Trong gameplay, sẽ có 2 phần: Giải đố (Puzzle Solving) và Bắn quái (Shooting)
- Puzzle
o Người chơi sẽ đi tìm các mảnh ghép để có thể đi tiếp về cốt truyện.
o Những Key Item (Vật phẩm quan trọng) sẽ rất dễ để nhận biết nhưng những Item liên quan đến cốt truyện sẽ khó tìm hơn và địi hỏi người chơi phải chịu khó backtrack (quay lại) một chút để mở khóa chúng.
o Điều này giúp người chơi bình thường khơng q khó chịu khi phải đi tìm vật phẩm để có thể tiếp tục cốt truyện của game mà vẫn cho người chơi muốn tìm hiểu sâu hơn có cơ hội biết thêm về thế giới của trị chơi.
- Bắn qi:
o Có lẽ đây là phần khó nhất của game.
o Số lượng đạn của người chơi cực kì ít nên việc sử dụng chúng hết sức tiết kiệm là một điều cần phải chú ý.
o Số lượng vũ khí có trong game có thể hơi ít do bối cảnh là một bệnh viện, không phải khu quân đội.
o Có rất nhiều loại quái vật. Từ những con Zombie đến những con quái vật có thể bay và lao vào người chơi bất cư lúc nào. Mỗi loại quái vật sẽ yêu cầu một cách đối phó khác nhau.
Người choi sẽ được chỉ những đặc điểm của chúng nhưng khơng phải cách đối phó với chúng.
III.1.3.2. Lập trình.
Hiện tại, Project có 14 plugins (10 Main Plugins và 4 Addons chưa chia Sub Plugins) bao gồm
● Main
o Zombie File – Plugins điều khiển chính của cả Mod
o Main: Script Text/Event – Điều khiển Scripted Event và các đoạn hội thoại.
o Main: Item/Key Item – Điều kiển những item được tìm thấy trên mặt đất. Bao gòm vũ khí, những item cần thiết cho Puzzle, hộp hồi máu,…
o Main: Buttons – Đa phần là để làm Puzzle. Quản lý các nút bấn cửa ở trong map. Lúc ấn thường nút bấn sẽ gửi ngay tín hiệu để thực hiện lệnh. Plugin này sẽ chặn chúng cho đến khi đủ các yêu cầu của Puzzlel sẽ mới cho tín hiệu kia chạy. o Main: NPC – Quản lý và điều khiển các Monster và NPC
khác bao gồm 6 loại Zombie khác nhau.
o Main: Primary Weapon – Quản lý vũ khí chính. o Main: Secondary Weapon – Quản lý vũ phụ.
o Main: Melee Weapon – Quản lý vũ cận chiến và các thứ liên quan đến nó.
● Addons
o CZTutorial – Một khung nhỏ xuất hiện ở góc như hướng dẫn (vốn đã có sẵn nhưng bị hơ hiệu hóa. Plugins này kích hoạt và sử dụng nó theo ý muốn.)
o Help Sprite – Một chữ E xuất hiện trên các Entity như hướng dẫn đầu game. Có thể ghép vào Item và Key Item plugins kia như 1 stock nhưng sẽ lặp lại khá nhiều nên em quyết định tách nó ra)
o Door Fix – Một số cửa bị lỗi chỉ có thể mở từ một phía. Cái này do lỗi của Engine nên phải sửa bằng cách plugin)
o Hud Hider – Quản lý sự xuất hiện hoặc biến mất của giao diện hiển thị trên màn hình.
Ngồi ra cịn có 2 Module:
● Infinity Game – Có nhiều tiện ích khá q trọng như điều khiển tốc độ, thay đổi ngoại hình, thay đổi tầm nhìn,… Đa phần có thể viết qua Plugins nhưng qua Module sẽ nhanh hơn rất nhiều.
● SwNPC – Module hỗ trợ để viết Plugin liên quan đến thuật toán di chuyển của NPC dựa trên SyPB plugins ở Metamod.
III.1.3.2.1 Giải thuật tìm kẻ địch của quái vật.
Bình thường nếu sử dụng giải tốn đơn giản: [Tìm vị trí giữ người chơi và vật thể sau đó thay đổi Velocity của vật thể và đưa nó đến gần hơn ], thì vật thể đó sẽ khơng thể đi qua được các chướng ngại vật và sẽ cứ đâm đầu vào nó cho đến khi được lệnh với.
Em đã tìm hiểu qua A* PathFinding, một giải thuật tìm kiếm đồ thị. Thuật tốn này tìm một đường đi từ một nút khởi đầu tới một nút đích cho trước (hoặc tới một nút thỏa mãn một điều kiện đích). Thuật tốn này sử dụng một "đánh giá heuristic" để xếp loại từng nút theo ước lượng về tuyến đường tốt nhất đi qua nút đó. Thuật tốn này duyệt các nút theo thứ tự của đánh giá heuristic này. Do đó, thuật tốn A* là một ví dụ của tìm kiếm theo lựa chọn tốt nhất (best-first search).
Em đã thực hiện một số thử nghiêm với giải thuật này. Ban đầu, để cho vật thể tự tạo những điểm nút mỗi khi cần di chuyển (tầm 1~5 giây mỗi lần) nhưng nó là quá sức mới engine của game nên rất dễ gây crash. Sau nhiều lần thử nghiệm, em đã mượn hệ thống tìm đường của SyPB – một trong loại BOT tốt nhất của CS 1.6 để làm căn bản. Hệ thống này thực hiện tương tự nhưng thay vì để vật thể tự tìm, tạo điểm nút phù hợp, SyPB sẽ cho phép người dùng tự tạo điểm nút – Waypoint, hướng di chuyển để BOT xử lý việc tìm điểm nút có thể thực hiện nhanh và thường xuyên hơn.
Sau khi liên lạc với người viết SyPB, em và bạn ấy đã tạo ra SwNPC, giống SyPB nhưng dành riêng cho vật thể (BOT = fake player, Entity = Monster, Object, NPC).
Hình 3.1 Waypoint
III.1.3.2.2 Các câu đố - Puzzle
Để tạo ra các Puzzle, em đã kết hợp giữa các Button của map và AMXX. Plugin Main: Buttons quản lý các nút bấn và cửa ở trong map. Lúc ấn thường nút bấn sẽ gửi ngay tín hiệu để thực hiện lệnh. Plugin này sẽ chặn chúng cho đến khi đủ các yêu cầu của Puzzlel sẽ mới cho tín hiệu kia chạy.
Ngồi ra, cịn một số vật có đặc điểm các chú ý như máy phát điện sẽ kêu khi sửa, thu hút sự chú ý của zombie/quái vật. Vật phậm (Item) và Vật Phẩm Quan Trọng (Key Item) đặt ở vị trí cố định nên việc tính tốn trước vị trí của chúng sao cho dễ nhìn, tránh việc bỏ lỡ và phải mất nhiều thời gian đi xung quanh để tìm 1 Item nào đó.
III.1.3.2.3 Vũ khí
Như đã nói ở trên, số lượng vũ khí có trong game có thể hơi ít do bối cảnh là một bệnh viện, không phải khu quân đội. Tuy nhiên, khơng phải chúng khơng có những đặc điểm riêng thú vị của chúng.
Có lẽ đặc biệt nhất là vũ khí ẩn ở gần cuối game giúp việc clear phần cịn lại dễ dàng hơn và có thể coi là một phần quà cho nỗ lực tìm kiếm Hidden trong game.
CHƯƠNG IV. KẾT LUẬN
Trên thực tế, trò chơi này và game nói chung giúp những người đã mệt mỏi với những cơng việc hằng ngày thốt khỏi nó và đi vào một thế giới khác. Người ta ngọi đó là “Giải thốt khỏi thực tại”. Dù thế giới game đó có nhiều khó khăn hay quá đơn giản, đối với những người đã quá chán nản với thế giới khơng màu của thực tại, nó tốt đẹp hơn nhiều và tạo ra động lực để họ có thể tiếp tục cố gắng trong tương lai.
Đồ án này có thể khơng được hồn thiện nhưng nó vẫn là tác phẩn đầu tiên em đặt nhiều tâm huyết của mình vào nó. Lý do em muốn làm đồ án này đơn giản chỉ là em muốn làm game. Từ một người chơi game trở thành một người làm ra chúng. Muốn nhìn người khác biểu lộ các cảm xúc của họ khi chơi, muốn nghe những lời khen, lời phê bình từ người chơi và đưa đến cho họ những cảm xúc thật nhất cùng khoảng thời gian vui vẻ và hứng thú khi chơi game sau những giờ học, giờ chơi căng thẳng.
CHƯƠNG VI. MAIN CODE TRONG GAME#include <amxmodx> #include <amxmodx> #include <amxmisc> #include <cstrike> #include <engine> #include <fakemeta> #include <hamsandwich> #include <fakemeta_util> #include <fun> // #include <xs> // #include <sqlx> #include <infinitygame> #include <zf_script> #include <zombie_file_addons> #include <offset>
#define PLUGIN "Zombie_Files" #define VERSION "1.0"
#define AUTHOR "on154288"
#define DEBUG 1
//********************************************** //* Resources. (Models, Sounds, Sprites,...) *
#define PLAYER_MODEL "soi"
new const g_Sound[][] = {
"zombie_file/sfx/hartbeat_loop.wav", "zombie_file/sfx/hartbeat_end.wav" }
new const g_StepSound[][] = { "zombie_file/run_sfx/step1.wav", "zombie_file/run_sfx/step2.wav", "zombie_file/run_sfx/step3.wav", "zombie_file/run_sfx/step4.wav" } //********************************************** //* Enum. * //**********************************************
//Unique Task Index enum(+= 100) { TASK_HARTBEAT_LOOP = 2000, TASK_HARTBEAT_FINISH, TASK_CHECKTIME, TASK_REAL_TIMER, TASK_CHECKSTAMINA }
//Running enum { NOT_RUNNING = 0, RUNNING } //********************************************** //* Var. * //**********************************************
//Global Game Info
new g_GameTime, g_MaxPlayer
public g_bGameStarted //This allow another plugins to get this
//Message
new g_MsgScreenFade, g_MsgRoundTime
//Bitvar
new g_LowHartBeat
//Running
new Float:CheckTime2[33], g_PlayerKey[33][3], g_StepCount[33]
{ {100, 140}, {120, 150}, {130, 160}, {140, 180} } //Player Data
new g_flCurrentSpd, g_iHPlvl
//Public Var
public bool:g_Running = false
//********************************************** //* Macros. *
//********************************************** #define Get_BitVar(%1,%2) (%1 & (1 << (%2 & 31))) #define Set_BitVar(%1,%2) %1 |= (1 << (%2 & 31)) #define UnSet_BitVar(%1,%2) %1 &= ~(1 << (%2 & 31))
public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)
//Event
register_event("HLTV", "event_newround", "a", "1=0", "2=0") register_logevent("logevent_round_start", 2, "1=Round_Start")
//Forward
register_forward( FM_CmdStart , "fw_CmdStart" );
//Ham
RegisterHam(Ham_Spawn, "player", "fw_Player_Spawn_Post", true) RegisterHam(Ham_Player_Jump, "player", "fw_Player_Jump_Pre") RegisterHam(Ham_Player_Jump, "player", "fw_Player_Jump", true)
//Messege
g_MsgScreenFade = get_user_msgid("ScreenFade") g_MsgRoundTime = get_user_msgid( "RoundTime" )
register_message(get_user_msgid("Health"), "fw_MsgHealth")
//Round End Block
IG_EndRound_Block(true, true) //Setup Data g_bGameStarted = 0 g_MaxPlayer = get_maxplayers() } //********************************************** //* Precache. * //********************************************** public plugin_precache()
new i;
for(i = 0; i < sizeof(g_Sound); i++) precache_sound(g_Sound[i])
for(i = 0; i < sizeof(g_StepSound); i++) precache_sound(g_StepSound[i])
new buffer[64]
formatex(buffer, charsmax(buffer), "models/player/soi/soi.mdl") engfunc(EngFunc_PrecacheModel, buffer) } //********************************************** //* Event Hook. * //********************************************** public event_newround() { g_GameTime = 0 Set_Time(g_GameTime) } public logevent_round_start() {
zf_script_print_dialog(0, "Ugh...It's hurt...", NOTICE_DIALOG, C_WHITE, 0.0)
zf_script_print_dialog(0, "There is a medkit over there^nI better patch myself up first", NOTICE_DIALOG, C_WHITE, 3.5)
zf_script_print_dialog(0, "[W] [A] [S] [D] - Moving^n[Space] - Jump^n[E] - Interact", NOTICE_TIP, C_GREEN, 3.0)
}
//********************************************** //* Forward Hook. *
//**********************************************
public fw_CmdStart( id , handle , seed ) {
static OldButton; OldButton = pev(id, pev_oldbuttons) static Buttons; Buttons = get_uc(handle, UC_Buttons)
static Float:speed, Float:new_speed
if(g_bGameStarted != 2) return
if(pev(id, pev_health) < 30.0) return
//Bug Fix: Slow Duck Speed. if(g_Running)
speed = float(g_Speed[g_iHPlvl][RUNNING]) else
speed = float(g_Speed[g_iHPlvl][NOT_RUNNING])
if(g_flCurrentSpd != floatround(new_speed)) {
if(is_user_crouching(id) && (pev(id, pev_flags) & FL_ONGROUND)) { Handle_DuckSpeed(id, 1) return } } if(g_flCurrentSpd != floatround(speed)) {
if(!is_user_crouching(id) || !(pev(id, pev_flags) & FL_ONGROUND)) { Handle_DuckSpeed(id, 0) return } }
//Function: Running (by pressing [W] + [W]). if ( Buttons & IN_FORWARD )
{
//Running Footstep
if(g_Running && (get_gametime() - 0.35 > CheckTime2[id])) {
if(!(get_uc( handle , UC_Buttons ) & (IN_JUMP | IN_DUCK))) { if(pev(id, pev_armorvalue) >= 3.0) { emit_sound(id, CHAN_BODY,
g_StepSound[g_StepCount[id]], 1.0, ATTN_NORM, 0, PITCH_NORM) g_StepCount[id] += 1
if(g_StepCount[id] == 4) g_StepCount[id] = 0
CheckTime2[id] = get_gametime() set_pev(id, pev_armorvalue, pev(id, pev_armorvalue) - 2.0)
} }
}
if(OldButton & IN_FORWARD) return //Actual Running if(!task_exists(id+TASK_CHECKTIME)) { g_PlayerKey[id][0] = 'w' remove_task(id+TASK_CHECKTIME) set_task(0.15, "Recheck_Key", id+TASK_CHECKTIME)
} else {
g_PlayerKey[id][1] = 'w' }
} else {
if(OldButton & IN_FORWARD) { Stop_Running(id) } return } if(equali(g_PlayerKey[id], "ww")) { Reset_Key(id) Start_Running(id) } return } //********************************************** //* Ham Hook. * //********************************************** public fw_Player_Spawn_Post(id) {
if(!is_user_alive(id)) return if(!g_bGameStarted) { g_bGameStarted = 1 IG_TerminateRound(WIN_DRAW, 2.0, 0)
client_print(0, print_center,"The game will start in few seconds...")
#if !defined DEBUG
set_cvar_float("mp_freezetime", 20.0) #else set_cvar_float("mp_freezetime", 3.0) #endif ScreenFadeIn(id, 5.0) return } if(g_bGameStarted == 1) {
#if !defined DEBUG
static Float:fOrigin[3]; fOrigin[0] = 3661.22 fOrigin[1] = 2303.39 fOrigin[2] = -1898.96
set_pev(id, pev_origin, fOrigin) set_pev(id, pev_gravity, 100.0) set_pev(id, pev_health, 10.0) IG_Fog(0, 0, 0, 0, 30); PlaySound(id, S_STARTMUSIC) ScreenFadeIn(id, 5.0) IG_Fog(0, 0, 0, 0, 30);
set_task(24.0, "Task_Speed", id, "1")
SetPlayerLight(0, "l") IG_ModelSet(id, PLAYER_MODEL, 1) #else static Float:fOrigin[3]; fOrigin[0] = 3661.22 fOrigin[1] = 2303.39 fOrigin[2] = -1898.96
set_pev(id, pev_origin, fOrigin) set_task(3.0, "Task_Speed", id)
//set_task(3.0, "Task_Speed", id)
//SetPlayerLight(0, "l") #endif
//set_pev(id, pev_health, 10.0) //IG_Fog(0, 0, 0, 0, 0); set_user_footsteps(id, 1) IG_SpeedSet(id, 1.0, 1) if(task_exists(id+TASK_REAL_TIMER)) remove_task(id+TASK_REAL_TIMER)
set_task(1.0, "Task_Timer", id+TASK_REAL_TIMER, _, _, "b") } } public fw_Player_Jump_Pre(id) { if(pev(id, pev_armorvalue) >= 5.0) return static iOldButtons;
iOldButtons = set_pev(id, pev_oldbuttons)
if( !( iOldButtons & IN_JUMP ) && (pev(id, pev_flags) & FL_ONGROUND))
set_pev(id, pev_oldbuttons, iOldButtons | IN_JUMP );
}
if(!(pev(id, pev_flags) & FL_ONGROUND) || pev(id, pev_armorvalue) < 5.0)
return
new buttons, old_button buttons = pev(id, pev_button)
old_button = pev(id, pev_oldbuttons)
if(buttons & IN_JUMP && old_button & IN_JUMP) return
set_pev(id, pev_armorvalue, pev(id, pev_armorvalue) - 5.0) g_StopCheck = get_gametime()
}
//********************************************** //* Messege Hook. *
//**********************************************
public fw_MsgHealth(msg_id, msg_dest, msg_entity) {
static health, Float:speed; health = get_msg_arg_int(1)
//Heart Beat SFX
if(health < 30 && !Get_BitVar(g_LowHartBeat, msg_entity)) {
if(task_exists(TASK_HARTBEAT_FINISH+msg_entity)) remove_task(TASK_HARTBEAT_FINISH+msg_entity)
set_task(0.75, "Task_HartBeat_Loop", TASK_HARTBEAT_LOOP+msg_entity, _, _, "b")
Set_BitVar(g_LowHartBeat, msg_entity) }
if(health >= 30 && Get_BitVar(g_LowHartBeat, msg_entity)) { if(task_exists(TASK_HARTBEAT_LOOP+msg_entity)) remove_task(TASK_HARTBEAT_LOOP+msg_entity) set_task(0.5, "Task_HartBeat_Finisher", TASK_HARTBEAT_FINISH+msg_entity) UnSet_BitVar(g_LowHartBeat, msg_entity) }
//Setting Fog and get Health Level (for setting up Speed)
//Because setting speed at freezetime will make player moving around sooner than it should be.
if(g_bGameStarted == 1) { IG_SpeedSet(msg_entity, 1.0, 1) IG_Fog(0, 0, 0, 0, 30); return } else if(g_bGameStarted == 2) { if(health < 30) { IG_Fog(0, 0, 0, 0, 30); g_iHPlvl = 0
}
else if(health >= 30 && health < 50) {
IG_Fog(0, 0, 0, 0, 25) g_iHPlvl = 1
}
else if(health >= 50 && health < 70) { IG_Fog(0, 0, 0, 0, 20) g_iHPlvl = 2 } else { IG_Fog(0, 0, 0, 0, 15) g_iHPlvl = 3 } } if(g_Running) { speed = float(g_Speed[g_iHPlvl][RUNNING]) } else { speed = float(g_Speed[g_iHPlvl][NOT_RUNNING]) } IG_SpeedSet(msg_entity, speed, 1)
client_print(msg_entity, print_chat, "HP Level: %i | Game Started: %i | Speed: %0.2f", g_iHPlvl, g_bGameStarted, speed)
} //********************************************** //* Function Running. * //********************************************** public Recheck_Key(id) { id -= TASK_CHECKTIME if(!is_user_connected(id)) return Reset_Key(id) } public Reset_Key(id) { g_PlayerKey[id][0] = 0 g_PlayerKey[id][1] = 0 } public Start_Running(id) { if(pev(id, pev_armorvalue) < 3.0)
{ Stop_Running(id) return } static Float:speed; speed = float(g_Speed[g_iHPlvl][RUNNING]) if(g_flCurrentSpd > g_Speed[g_iHPlvl][NOT_RUNNING]) { IG_SpeedSet(id, speed, 1) Handle_DuckSpeed(id, 1) } else IG_SpeedSet(id, speed, 1) g_Running = true } public Stop_Running(id) { static Float:speed; speed = float(g_Speed[g_iHPlvl][NOT_RUNNING]) if(g_flCurrentSpd > g_Speed[g_iHPlvl][RUNNING]) { IG_SpeedSet(id, speed, 1) Handle_DuckSpeed(id, 1) }
else IG_SpeedSet(id, speed, 1) if(g_Running) g_StopCheck = get_gametime() g_Running = false g_StepCount[id] = 0 } //********************************************** //* Bug Fix: Slow Duck Speed Fix. *
//**********************************************
public Handle_DuckSpeed(id, On) {
new Float:speed, Float:new_speed
if(g_Running) speed = float(g_Speed[g_iHPlvl][RUNNING]) else speed = float(g_Speed[g_iHPlvl][NOT_RUNNING]) new_speed = speed * 2.0 switch(On)
case 0: { IG_SpeedSet(id, speed, 1) if(speed < 250.0) { client_cmd(id, "cl_forwardspeed 250.0") client_cmd(id, "cl_backspeed 250.0") client_cmd(id, "cl_sidespeed 250.0") } else {
client_cmd(id, "cl_forwardspeed %0.1f", speed) client_cmd(id, "cl_backspeed %0.1f", speed) client_cmd(id, "cl_sidespeed %0.1f", speed) }
g_flCurrentSpd = floatround(speed)
//client_print(id, print_chat, "[DEBUG] State: Not Duck + %s | Speed: %0.1f", Get_BitVar(g_Running, id) ? "Run" : "Not Run" , speed)
} case 1: {
IG_SpeedSet(id, new_speed, 1)
client_cmd(id, "cl_forwardspeed %0.1f", new_speed) client_cmd(id, "cl_backspeed %0.1f", new_speed) client_cmd(id, "cl_sidespeed %0.1f", new_speed) g_flCurrentSpd = floatround(new_speed)
//client_print(id, print_chat, "[DEBUG] State: Not Duck + %s | Speed: %0.1f", Get_BitVar(g_Running, id) ? "Run" : "Not Run" , speed) } } } //********************************************** //* Task. * //********************************************** public Task_Speed(id) { IG_SpeedSet(id, 100.0, 1) g_bGameStarted = 2 set_pev(id, pev_gravity, 1.0) } public Task_HartBeat_Loop(taskid) {
new id; id = taskid - TASK_HARTBEAT_LOOP PlaySound(id, g_Sound[0])
}
public Task_HartBeat_Finisher(taskid) {
new id; id = taskid - TASK_HARTBEAT_FINISH PlaySound(id, g_Sound[1])
public Task_Timer(taskid) {
static id; id = taskid - TASK_REAL_TIMER g_GameTime++
Set_Time(g_GameTime)
if((pev(id, pev_armorvalue) < 100.0) && (get_gametime() - 1.5 > g_StopCheck) && !g_Running)
{
if(pev(id, pev_armorvalue) + 5.0 > 100.0) set_pev(id, pev_armorvalue, 100.0) else
set_pev(id, pev_armorvalue, pev(id, pev_armorvalue) +5.0)
}
//client_print(id, print_chat, "Armor: %i | Running: %s | g_StopCheck: %0.2f", pev(id, pev_armorvalue), g_Running ? "Yes" : "No", g_StopCheck) }
//********************************************** //* Stock. *
//**********************************************
stock FixedUnsigned16(Float:flValue, iScale) {
new iOutput;
if ( iOutput < 0 ) iOutput = 0; if ( iOutput > 0xFFFF ) iOutput = 0xFFFF; return iOutput; }
stock PlaySound(id, const sound[]) {
if(id == 0) {
if (equal(sound[strlen(sound)-4], ".mp3"))
client_cmd(0, "mp3 play ^"sound/%s^"", sound) else client_cmd(0, "spk ^"%s^"", sound) } else { if(is_user_connected(id)&& is_user_alive(id)) { if (equal(sound[strlen(sound)-4], ".mp3"))
client_cmd(id, "mp3 play ^"sound/%s^"", sound) else
client_cmd(id, "spk ^"%s^"", sound) }
stock SetPlayerLight(id, const LightStyle[]) {
if(id != 0) {
message_begin(MSG_ONE_UNRELIABLE, SVC_LIGHTSTYLE, .player = id)
write_byte(0) write_string(LightStyle) message_end() } else { message_begin(MSG_BROADCAST, SVC_LIGHTSTYLE) write_byte(0) write_string(LightStyle) message_end() } }
stock ScreenFadeIn(id, Float:duration) { message_begin(MSG_ONE_UNRELIABLE, g_MsgScreenFade, {0,0,0}, id) write_short(FixedUnsigned16(duration, 1<<12)) write_short(FixedUnsigned16(duration, 1<<12)) write_short((0x0000)) write_byte(0) write_byte(0) write_byte(0) write_byte(255)
message_end() }
stock Set_Time(const Time) {
message_begin( MSG_ALL, g_MsgRoundTime ); write_short( Time );
message_end(); }
stock is_user_crouching(ent,ignoreplayer=0) {
if(!is_user_alive(ent) && !ignoreplayer) return 0; new Float:minsize[3] pev(ent,pev_mins,minsize) if(minsize[2]==-18.0) return 1; return 0; }
TÀI LIỆU THAM KHẢO
Tiếng Anh
1. Metamod (http://metamod.org/about.html) 2. HamSandwich General Usage
(wiki.alliedmods.net/HamSandwich_General_Usage_(AMX_Mod_X)
)
3. AMXX API, fakemeta_const, Used with dll_func() (https://www.amxmodx.org/api/fakemeta_const)
Tiếng Việt
4. Hàm Ảo (vi.wikipedia.org/wiki/Hàm_ảo)