weapon_ammo modified

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #21
tanya kovsky,
PHP:
#pragma semicolon 1

#include <sourcemod>
#include <sdktools_functions>

public OnClientPutInServer(client)
{
    SDKHook(client, SDKHook_WeaponCanUse, OnWeaponCanUse);
}

public Action:OnWeaponCanUse(client, weapon)
{
    if(!IsClientInGame(client))
        return Plugin_Continue;
    
    if(GetClientTeam(client) <= 1)
        return Plugin_Continue;

    if (IsClientInGame(client) && IsPlayerAlive(client))
    {
            decl String:ClassName[30];
            new WeaponIndex = GetEntPropEnt(spec, Prop_Send, "m_hActiveWeapon");
            //GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName)); это пока не нужно
                new m_iPrimaryAmmoType = -1;
                if ((m_iPrimaryAmmoType = GetEntProp(WeaponIndex, Prop_Send, "m_iPrimaryAmmoType")) != -1)
                {
                    SetEntProp(WeaponIndex , Prop_Send, "m_iClip1", 15);
                    SetEntProp(client, Prop_Send, "m_iAmmo", 50, _, m_iPrimaryAmmoType);
                }
    }

    return Plugin_Handled;
}
 

tanya kovsky

Участник
Сообщения
12
Реакции
2
Это я уже давно делала, но весь ступор у меня на выдачу патронов именно на определенное оружие! Не только на активное а на все или некоторое из всего оружия. А именно мучения с StrEqual(classname, "weapon_оружие") и т.д. :beer:
 

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #24
tanya kovsky, на ксс работает:

PHP:
#pragma semicolon 1 
#include <sourcemod>
#include <sdkhooks>
#include <sdktools_functions> 

new g_iActiveWeaponOffset = -1,
	g_iClip1Offset = -1,
	g_iClip2Offset = -1;

public OnPluginStart()
{
	g_iActiveWeaponOffset = FindSendPropOffs("CBasePlayer", "m_hActiveWeapon");
	if (g_iActiveWeaponOffset == -1) SetFailState("Fatal Error: Unable to find offset: \"m_hActiveWeapon\"");
	g_iClip1Offset = FindSendPropOffs("CBaseCombatWeapon", "m_iClip1");
	if (g_iClip1Offset == -1) SetFailState("Fatal Error: Unable to find offset: \"m_iClip2\"");
	g_iClip2Offset = FindSendPropOffs("CBaseCombatWeapon", "m_iClip2");
	if (g_iClip2Offset == -1) SetFailState("Fatal Error: Unable to find offset: \"m_iClip2\"");
}

public OnClientPutInServer(client) SDKHook(client, SDKHook_WeaponCanUse, OnWeaponCanUse); 
public OnClientDisconnect(client) SDKUnhook(client, SDKHook_WeaponCanUse, OnWeaponCanUse);

public Action:OnWeaponCanUse(client, weapon) 
{ 
	if (IsClientInGame(client) && IsPlayerAlive(client)) 
	{
		// Еще несколько проверок игрока
		new WeaponIndex = GetEntDataEnt2(client, g_iActiveWeaponOffset);
		if(WeaponIndex > 0)
		{
			/*Далее проверка на оружие (пока не нужна)
			decl String:ClassName[30]; 
			GetEdictClassname(WeaponIndex, ClassName, sizeof(ClassName));
			https://wiki.alliedmods.net/Half-Life_2:_Deathmatch_Weapons
			*/
			SetEntData(WeaponIndex, g_iClip1Offset, 15, 4, true);
			SetEntData(WeaponIndex, g_iClip2Offset, 50, 4, true);
			return Plugin_Handled; 
		} 
	} 

	return Plugin_Continue; 
}
 
Последнее редактирование:

Root

AWOL.
Сообщения
76
Реакции
195
Вот очень упрощенный вариант изменения кол-ва патронов в обоиме и запасе.
Изменение патронов очень запарная тема, и этот метод не самый лучший.
Если интересует "как это работает" то можно посмотреть исходный код DoD:S ZombieMod.
PHP:
#include <sdkhooks>
#include <sdktools>

new	m_iAmmo, m_iClip1, bool:HasEquipped[MAXPLAYERS + 1]; // Глобальные переменные

public OnPluginStart()
{
	m_iAmmo  = GetSendPropOffset("CBasePlayer",       "m_iAmmo"); // Для оптимизации в начале плагина отловим офсет для изменения патронов клиенту
	m_iClip1 = GetSendPropOffset("CBaseCombatWeapon", "m_iClip1"); // И для изменения патронов в магазине для оружия

	HookEvent("player_spawn", OnPlayerSpawnPost); // Заодно отловим момент возрождения игрока

	for (new i = 1; i <= MaxClients; i++)
		if (IsClientInGame(i)) OnClientPutInServer(i); // На случай если плагин был перезагружен
}

public OnClientPutInServer(client)
{
	SDKHook(client, SDKHook_WeaponDropPost,  OnWeaponDropPost); // Отследим момент когда игрок выбрасывает и
	SDKHook(client, SDKHook_WeaponEquipPost, OnWeaponEquipPost); // подбирает любое оружие
}

public OnPlayerSpawnPost(Handle:event, const String:name[], bool:dontBroadcast)
{
	OnWeaponDropPost(GetClientOfUserId(GetEventInt(event, "userid")), 0); // При каждом респауне ... 
}

public OnWeaponDropPost(client, weapon)
{
	HasEquipped[client] = false; // И каждом выбрасывании оружия изменим значение HasEquipped для данного игрока на false
}

public OnWeaponEquipPost(client, weapon)
{
	decl String:class[32];
	GetEdictClassname(weapon, class, sizeof(class))

	if (HasEquipped[client] == false // Игрок ранее не подбирал какое-либо оружие?
	&&(StrEqual(class[7], "shotgun", false) // Нет - заодно сверим классификацию этого оружия
	|| StrEqual(class[7], "crossbow", false) // Пропустим первые 7 символов, чтобы проигнорировать префикс 'weapon_' (оптимизация)
	|| StrEqual(class[7], "rpg",  false)))
	{
		// С небольшой задержкой изменим боеприпасы для игрока и его оружия
		CreateTimer(0.1, Timer_SetAmmo, client|(weapon << 16), TIMER_FLAG_NO_MAPCHANGE);
	}
}

public Action:OnWeaponReloaded(weapon)
{
	if (GetEntData(weapon, m_iClip1) >= 15) // Если у оружия на данный момент больше 15 патронов в магазине, запретим перезарядку
		return Plugin_Handled;

	// В противном случае снова изменим кол-во патронов, но не будем указывать индекс клиента (ВАЖНО!)
	CreateTimer(0.1, Timer_SetAmmo, 0|(weapon << 16), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
	return Plugin_Continue;
}

public Action:Timer_SetAmmo(Handle:timer, any:data)
{
	new client = data & 0x0000FFFF; // Это магическое действие позволяет передавать в таймере два индекса сразу
	new weapon = data >> 16;

	if (IsValidEntity(weapon)) // Сверим если наше оружие в порядке
	{
		// Чтобы изменить патроны для N-ного оружия, нужно узнать его офсет
        // Оружейный индекс можно узнать через значение m_iPrimaryAmmoType, умноженное на 4
        // Зачем? Потому что (параметр?) m_iAmmo требует этот самый офсет
		new WeaponID = GetEntProp(weapon, Prop_Send, "m_iPrimaryAmmoType");

		if (GetEntData(weapon, m_iClip1) > 15)
			SetEntData(weapon, m_iClip1, 15); // Подкорректируем кол-во патронов для этого оружия

		// Индекс клиента в порядке - а значит он вручную перезарядил оружие и хук сработал
		if (1 <= client <= MaxClients)
		{
			if (HasEquipped[client] == false) // Проверим не подбирал ли игрок это оружие
			{
				HasEquipped[client] = true;

				// Если вы знаете что офсет weapon_prg равен 32, то можно просто указать
                // SetEntData(client, m_iAmmo + 32, (кол-во патронов))
				// Но мы не ищем легких путей...
				if (GetEntData(client, m_iAmmo + (WeaponID * 4)) > 50)
					SetEntData(client, m_iAmmo + (WeaponID * 4), 50);
			}

			if (SDKHookEx(weapon, SDKHook_Reload, OnWeaponReloaded))
			{
				//PrintToChat(client, "m_iClip1 %i m_iAmmo %i", GetEntData(weapon, m_iClip1), GetEntData(client, m_iAmmo + (WeaponID * 4)));
				return Plugin_Stop;
			}
		}

		// Нет - индекс клиента равен нулю. Заодно проверим перезарядилось ли наше оружие до конца.
		else if (!GetEntProp(weapon, Prop_Data, "m_bInReload"))
		{
			// Да - остановим повторяющийся таймер
			return Plugin_Stop;
		}
	}

	return Plugin_Continue;
}

GetSendPropOffset(const String:serverClass[64], const String:propName[64])
{
	new offset = FindSendPropOffs(serverClass, propName);

	if (offset == -1)
	{
		SetFailState("Unable to find offset: \"%s::%s\"!", serverClass, propName);
	}

	return offset;
}
 
Последнее редактирование:
Сверху Снизу