Vendo que há um grande número de postos de trabalho na linha de Twilight Suzuka Como: Faça um NPC perfeito . . Decidi tentar e fazer o meu próprio NPC usando esse tutorial, mas acrescentou recursos extras
Algumas delas são:
Bata animações
Animações de morte
Animações ociosas
Sangrar / desencadear efeitos
Voz NPC falando
E muito mais!
Antes de iniciar este tutorial vamos explicar melhor o que é exatamente um NPC e por que devemos fazer antes de um how. NPC significa caráter não-jogável. Como o nome sugere, é simplesmente uma entidade que não é jogável. . Nós, como programadores têm total controle sobre a forma como eles interagem e trabalham Alguns exemplos do que um NPC pode fazer são:
Espectadores que anima. (Muito legal para fazer para o futebol Jam)
Um proprietário de loja de itens
Props para o seu mod. (Fazendo lâmpada / luz mensagens que poderiam flicker)
Tudo o que você pode pensar!
Tudo bem! Permite, finalmente, começar a fazer o nosso próprio NPC! Começamos incluindo as bibliotecas necessárias para tornar o nosso NPC
Código PHP:
Um grupo de variáveis globais que nosso NPC irá usar.
Código PHP:
Agora temos que precache nosso modelo e os sons ou o jogo vai cair! E também um comando para gerar o nosso NPC.
Nesta seção, temos de iniciar algumas encaminha para o nosso NPC. Trata-se de onde os recursos extras são chamados de.
Este também é o lugar onde nós podemos carregar nossas configurações salvas.
PHP Code:
Quando digitar "/ NPC" um menu será exibido com as opções do que fazer.
Nota: Eu estou usando o novo sistema de menu AMXX para exibir o menu, graças a EMP `
PHP Code:
Adicionar alguns recursos extras para o nosso NPC.
PHP Code:
Em caso de um novo ciclo. Vamos redefinir nossas propriedades NPC.
PHP Code:
O método que realmente cria o nosso NPC.
PHP Code:
Os métodos que carregar e salvar os locais do nosso NPC.
PHP Code:
Diversos método para a criação de animações para o nosso NPC.
PHP Code:
Pronto voce criou Seu Npc
Clique Aqui Para Baixar Sma Pronta
Creditos : Mini_Midget
Algumas delas são:
Bata animações
Animações de morte
Animações ociosas
Sangrar / desencadear efeitos
Voz NPC falando
E muito mais!
Antes de iniciar este tutorial vamos explicar melhor o que é exatamente um NPC e por que devemos fazer antes de um how. NPC significa caráter não-jogável. Como o nome sugere, é simplesmente uma entidade que não é jogável. . Nós, como programadores têm total controle sobre a forma como eles interagem e trabalham Alguns exemplos do que um NPC pode fazer são:
Espectadores que anima. (Muito legal para fazer para o futebol Jam)
Um proprietário de loja de itens
Props para o seu mod. (Fazendo lâmpada / luz mensagens que poderiam flicker)
Tudo o que você pode pensar!
Tudo bem! Permite, finalmente, começar a fazer o nosso próprio NPC! Começamos incluindo as bibliotecas necessárias para tornar o nosso NPC
Código PHP:
Código:
# Include < amxmodx >
# include < amxmisc >
# include < fakemeta >
# include < engine >
# include < hamsandwich >
Código PHP:
Código:
//Boolean of when NPC spawned
new bool: g_NpcSpawn[256];
//Boolean to check if NPC is alive or not
new bool: g_NpcDead[256];
//Classname for our NPC
new const g_NpcClassName[] = "ent_npc";
//Constant model for NPC
new const g_NpcModel[] = "models/barney.mdl";
//List of sounds our NPC will emit when damaged
new const g_NpcSoundPain[][] =
{
"barney/ba_pain1.wav",
"barney/ba_pain2.wav",
"barney/ba_pain3.wav"
}
//Sounds when killed
new const g_NpcSoundDeath[][] =
{
"barney/ba_die1.wav",
"barney/ba_die2.wav",
"barney/ba_die3.wav"
}
//Sounds when we knife our flesh NPC
new const g_NpcSoundKnifeHit[][] =
{
"weapons/knife_hit1.wav",
"weapons/knife_hit2.wav",
"weapons/knife_hit3.wav",
"weapons/knife_hit4.wav"
}
new const g_NpcSoundKnifeStab[] = "weapons/knife_stab.wav";
//List of idle animations
new const NPC_IdleAnimations[] = { 0, 1, 2, 3, 11, 12, 18, 21, 39, 63, 65 };
//Sprites for blood when our NPC is damaged
new spr_blood_drop, spr_blood_spray
//Player cooldown for using our NPC
new Float: g_Cooldown[32];
//Boolean to check if we knifed our NPC
new bool: g_Hit[32];
Nesta seção, temos de iniciar algumas encaminha para o nosso NPC. Trata-se de onde os recursos extras são chamados de.
Este também é o lugar onde nós podemos carregar nossas configurações salvas.
PHP Code:
Código:
public plugin_init()
{
register_plugin("NPC Plugin", "1.1", "Mazza");
register_clcmd("say /npc", "ClCmd_NPC");
register_event("HLTV", "Event_NewRound", "a", "1=0", "2=0");
RegisterHam(Ham_TakeDamage, "info_target", "npc_TakeDamage");
RegisterHam(Ham_Killed, "info_target", "npc_Killed");
RegisterHam(Ham_Think, "info_target", "npc_Think");
RegisterHam(Ham_TraceAttack, "info_target", "npc_TraceAttack");
RegisterHam(Ham_ObjectCaps, "player", "npc_ObjectCaps", 1 );
register_forward(FM_EmitSound, "npc_EmitSound");
}
public plugin_precache()
{
spr_blood_drop = precache_model("sprites/blood.spr")
spr_blood_spray = precache_model("sprites/bloodspray.spr")
new i;
for(i = 0 ; i < sizeof g_NpcSoundPain ; i++)
precache_sound(g_NpcSoundPain[i]);
for(i = 0 ; i < sizeof g_NpcSoundDeath ; i++)
precache_sound(g_NpcSoundDeath[i]);
precache_model(g_NpcModel)
}
public plugin_cfg()
{
Load_Npc()
}
Nota: Eu estou usando o novo sistema de menu AMXX para exibir o menu, graças a EMP `
PHP Code:
Código:
public ClCmd_NPC(id)
{
//Create a new menu
new menu = menu_create("NPC: Main Menu", "Menu_Handler");
//Add some items to the newly created menu
menu_additem(menu, "Create NPC", "1");
menu_additem(menu, "Delete NPC", "2");
menu_additem(menu, "Save current NPC locations", "3");
menu_additem(menu, "Delete all NPC", "4");
//Let the menu have an 'Exit' option
menu_setprop(menu, MPROP_EXIT, MEXIT_ALL);
//Display our menu
menu_display(id, menu);
}
public Menu_Handler(id, menu, item)
{
//If user chose to exit menu we will destroy our menu
if(item == MENU_EXIT)
{
menu_destroy(menu);
return PLUGIN_HANDLED;
}
new info[6], szName[64];
new access, callback;
menu_item_getinfo(menu, item, access, info, charsmax(info), szName, charsmax(szName), callback);
new key = str_to_num(info);
switch(key)
{
case 1:
{
//Create our NPC
Create_Npc(id);
}
case 2:
{
//Remove our NPC by the users aim
new iEnt, body, szClassname[32];
get_user_aiming(id, iEnt, body);
if (is_valid_ent(iEnt))
{
entity_get_string(iEnt, EV_SZ_classname, szClassname, charsmax(szClassname));
if (equal(szClassname, g_NpcClassName))
{
remove_entity(iEnt);
}
}
}
case 3:
{
//Save the current locations of all the NPCs
Save_Npc();
client_print(id, print_chat, "[AMXX] NPC origin saved succesfully");
}
case 4:
{
//Remove all NPCs from the map
remove_entity_name(g_NpcClassName);
client_print(id, print_chat, "[AMXX] ALL NPC origin removed");
}
}
//Keep the menu displayed when we choose an option
menu_display(id, menu);
return PLUGIN_HANDLED;
}
PHP Code:
Código:
public npc_TakeDamage(iEnt, inflictor, attacker, Float:damage, bits)
{
//Make sure we only catch our NPC by checking the classname
new className[32];
entity_get_string(iEnt, EV_SZ_classname, className, charsmax(className))
if(!equali(className, g_NpcClassName))
return;
//Play a random animation when damanged
Util_PlayAnimation(iEnt, random_num(13, 17), 1.25);
//Make our NPC say something when it is damaged
//NOTE: Interestingly... Our NPC mouth (which is a controller) moves!! That saves us some work!!
emit_sound(iEnt, CHAN_VOICE, g_NpcSoundPain[random(sizeof g_NpcSoundPain)], VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
}
public npc_Killed(iEnt)
{
new className[32];
entity_get_string(iEnt, EV_SZ_classname, className, charsmax(className))
if(!equali(className, g_NpcClassName))
return HAM_IGNORED;
//Player a death animation once our NPC is killed
Util_PlayAnimation(iEnt, random_num(25, 30))
//Because our NPC may look like it is laying down.
//The bounding box size is still there and it is impossible to change it so we will make the solid of our NPC to nothing
entity_set_int(iEnt, EV_INT_solid, SOLID_NOT);
//The voice of the NPC when it is dead
emit_sound(iEnt, CHAN_VOICE, g_NpcSoundDeath[random(sizeof g_NpcSoundDeath)], VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
//Our NPC is dead so it shouldn't take any damage and play any animations
entity_set_float(iEnt, EV_FL_takedamage, 0.0);
//Our death boolean should now be true!!
g_NpcDead[iEnt] = true;
//The most important part of this forward!! We have to block the death forward.
return HAM_SUPERCEDE
}
public npc_Think(iEnt)
{
if(!is_valid_ent(iEnt))
return;
static className[32];
entity_get_string(iEnt, EV_SZ_classname, className, charsmax(className))
if(!equali(className, g_NpcClassName))
return;
//We can remove our NPC here if we wanted to but I left this blank as I personally like it when there is a NPC coprse laying around
if(g_NpcDead[iEnt])
{
return;
}
//Our NPC just spawned
if(g_NpcSpawn[iEnt])
{
static Float: mins[3], Float: maxs[3];
pev(iEnt, pev_absmin, mins);
pev(iEnt, pev_absmax, maxs);
//Draw a box which is the size of the bounding NPC
message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
write_byte(TE_BOX)
engfunc(EngFunc_WriteCoord, mins[0])
engfunc(EngFunc_WriteCoord, mins[1])
engfunc(EngFunc_WriteCoord, mins[2])
engfunc(EngFunc_WriteCoord, maxs[0])
engfunc(EngFunc_WriteCoord, maxs[1])
engfunc(EngFunc_WriteCoord, maxs[2])
write_short(100)
write_byte(random_num(25, 255))
write_byte(random_num(25, 255))
write_byte(random_num(25, 255))
message_end();
//Our NPC spawn boolean is now set to false
g_NpcSpawn[iEnt] = false;
}
//Choose a random idle animation
Util_PlayAnimation(iEnt, NPC_IdleAnimations[random(sizeof NPC_IdleAnimations)]);
//Make our NPC think every so often
entity_set_float(iEnt, EV_FL_nextthink, get_gametime() + random_float(5.0, 10.0));
}
public npc_TraceAttack(iEnt, attacker, Float: damage, Float: direction[3], trace, damageBits)
{
if(!is_valid_ent(iEnt))
return;
new className[32];
entity_get_string(iEnt, EV_SZ_classname, className, charsmax(className))
if(!equali(className, g_NpcClassName))
return;
//Retrieve the end of the trace
new Float: end[3]
get_tr2(trace, TR_vecEndPos, end);
//This message will draw blood sprites at the end of the trace
message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
write_byte(TE_BLOODSPRITE)
engfunc(EngFunc_WriteCoord, end[0])
engfunc(EngFunc_WriteCoord, end[1])
engfunc(EngFunc_WriteCoord, end[2])
write_short(spr_blood_spray)
write_short(spr_blood_drop)
write_byte(247) // color index
write_byte(random_num(1, 5)) // size
message_end()
}
public npc_ObjectCaps(id)
{
//Make sure player is alive
if(!is_user_alive(id))
return;
//Check when player presses +USE key
if(get_user_button(id) & IN_USE)
{
//Check cooldown of player when using our NPC
static Float: gametime ; gametime = get_gametime();
if(gametime - 1.0 > g_Cooldown[id])
{
//Get the classname of whatever ent we are looking at
static iTarget, iBody, szAimingEnt[32];
get_user_aiming(id, iTarget, iBody, 75);
entity_get_string(iTarget, EV_SZ_classname, szAimingEnt, charsmax(szAimingEnt));
//Make sure our aim is looking at a NPC
if(equali(szAimingEnt, g_NpcClassName))
{
//Do more fancy stuff here such as opening a menu
//But for this tutorial I will only display a message to prove it works
client_print(id, print_chat, "Hello");
}
//Set players cooldown to the current gametime
g_Cooldown[id] = gametime;
}
}
}
public npc_EmitSound(id, channel, sample[], Float:volume, Float:attn, flag, pitch)
{
//Make sure player is alive
if(!is_user_connected(id))
return FMRES_SUPERCEDE;
//Catch the current button player is pressing
new iButton = get_user_button(id);
//If the player knifed the NPC
if(g_Hit[id])
{
//Catch the string and make sure its a knife
if (sample[0] == 'w' && sample[1] == 'e' && sample[8] == 'k' && sample[9] == 'n')
{
//Catch the file of _hitwall1.wav or _slash1.wav/_slash2.wav
if(sample[17] == 's' || sample[17] == 'w')
{
//If player is slashing then play the knife hit sound
if(iButton & IN_ATTACK)
{
emit_sound(id, CHAN_WEAPON, g_NpcSoundKnifeHit[random(sizeof g_NpcSoundKnifeHit)], volume, attn, flag, pitch);
}
//If player is tabbing then play the stab sound
else if(iButton & IN_ATTACK2)
{
emit_sound(id,CHAN_WEAPON, g_NpcSoundKnifeStab, volume, attn, flag, pitch);
}
//Reset our boolean as player is not hitting NPC anymore
g_Hit[id] = false;
//Block any further sounds to be played
return FMRES_SUPERCEDE
}
}
}
return FMRES_IGNORED
}
PHP Code:
Código:
public Event_NewRound()
{
new iEnt = -1;
//Scan and find all of the NPC classnames
while( ( iEnt = find_ent_by_class(iEnt, g_NpcClassName) ) )
{
//If we find a NPC which is dead...
if(g_NpcDead[iEnt])
{
//Reset the solid box
entity_set_int(iEnt, EV_INT_solid, SOLID_BBOX);
//Make our NPC able to take damage again
entity_set_float(iEnt, EV_FL_takedamage, 1.0);
//Make our NPC instanstly think
entity_set_float(iEnt, EV_FL_nextthink, get_gametime() + 0.01);
//Reset the NPC boolean to false
g_NpcDead[iEnt] = false;
}
//Reset the health of our NPC
entity_set_float(iEnt, EV_FL_health, 250.0);
}
}
PHP Code:
Código:
Create_Npc(id, Float:flOrigin[3]= { 0.0, 0.0, 0.0 }, Float:flAngle[3]= { 0.0, 0.0, 0.0 } )
{
//Create an entity using type 'info_target'
new iEnt = create_entity("info_target");
//Set our entity to have a classname so we can filter it out later
entity_set_string(iEnt, EV_SZ_classname, g_NpcClassName);
//If a player called this function
if(id)
{
//Retrieve the player's origin
entity_get_vector(id, EV_VEC_origin, flOrigin);
//Set the origin of the NPC to the current players location
entity_set_origin(iEnt, flOrigin);
//Increase the Z-Axis by 80 and set our player to that location so they won't be stuck
flOrigin[2] += 80.0;
entity_set_origin(id, flOrigin);
//Retrieve the player's angle
entity_get_vector(id, EV_VEC_angles, flAngle);
//Make sure the pitch is zeroed out
flAngle[0] = 0.0;
//Set our NPC angle based on the player's angle
entity_set_vector(iEnt, EV_VEC_angles, flAngle);
}
//If we are reading from a file
else
{
//Set the origin and angle based on the values of the parameters
entity_set_origin(iEnt, flOrigin);
entity_set_vector(iEnt, EV_VEC_angles, flAngle);
}
//Set our NPC to take damange and how much health it has
entity_set_float(iEnt, EV_FL_takedamage, 1.0);
entity_set_float(iEnt, EV_FL_health, 250.0);
//Set a model for our NPC
entity_set_model(iEnt, g_NpcModel);
//Set a movetype for our NPC
entity_set_int(iEnt, EV_INT_movetype, MOVETYPE_PUSHSTEP);
//Set a solid for our NPC
entity_set_int(iEnt, EV_INT_solid, SOLID_BBOX);
//Create a bounding box for oru NPC
new Float: mins[3] = {-12.0, -12.0, 0.0 }
new Float: maxs[3] = { 12.0, 12.0, 75.0 }
entity_set_size(iEnt, mins, maxs);
//Controllers for our NPC. First controller is head. Set it so it looks infront of itself
entity_set_byte(iEnt,EV_BYTE_controller1,125);
// entity_set_byte(ent,EV_BYTE_controller2,125);
// entity_set_byte(ent,EV_BYTE_controller3,125);
// entity_set_byte(ent,EV_BYTE_controller4,125);
//Drop our NPC to the floor
drop_to_floor(iEnt);
// set_rendering( ent, kRenderFxDistort, 0, 0, 0, kRenderTransAdd, 127 );
//We just spawned our NPC so it should not be dead
g_NpcSpawn[iEnt] = true;
g_NpcDead[iEnt] = false;
//Make it instantly think
entity_set_float(iEnt, EV_FL_nextthink, get_gametime() + 0.01)
PHP Code:
Código:
Save_Npc()
{
//Variables
new szConfigsDir[256], szFile[256], szNpcDir[256];
//Get the configs directory.
get_configsdir(szConfigsDir, charsmax(szConfigsDir));
//Get the current map name
new szMapName[32];
get_mapname(szMapName, charsmax(szMapName));
//Format 'szNpcDir' to ../configs/NPC
formatex(szNpcDir, charsmax(szNpcDir),"%s/NPC", szConfigsDir);
//Format 'szFile to ../configs/NPC/mapname.cfg
formatex(szFile, charsmax(szFile), "%s/%s.cfg", szNpcDir, szMapName);
//If there is already a .cfg for the current map. Delete it
if(file_exists(szFile))
delete_file(szFile);
//Variables
new iEnt = -1, Float:fEntOrigin[3], Float:fEntAngles[3];
new sBuffer[256];
//Scan and find all of my custom ents
while( ( iEnt = find_ent_by_class(iEnt, g_NpcClassName) ) )
{
//Get the entities' origin and angle
entity_get_vector(iEnt, EV_VEC_origin, fEntOrigin);
entity_get_vector(iEnt, EV_VEC_angles, fEntAngles);
//Format the line of one custom ent.
formatex(sBuffer, charsmax(sBuffer), "%d %d %d | %d", floatround(fEntOrigin[0]), floatround(fEntOrigin[1]), floatround(fEntOrigin[2]), floatround(fEntAngles[1]));
//Finally write to the mapname.cfg file and move on to the next line
write_file(szFile, sBuffer, -1);
//We are currentlying looping to find all custom ents on the map. If found another ent. Do the above till there is none.
}
}
PHP Code:
Código:
stock Util_PlayAnimation(index, sequence, Float: framerate = 1.0)
{
entity_set_float(index, EV_FL_animtime, get_gametime());
entity_set_float(index, EV_FL_framerate, framerate);
entity_set_float(index, EV_FL_frame, 0.0);
entity_set_int(index, EV_INT_sequence, sequence);
}
Clique Aqui Para Baixar Sma Pronta
Creditos : Mini_Midget