Jump to content

Recommended Posts

  • Administrator

20200814074607_1.jpg

Просто полезный инструмент для перемещения игрока из команды в команду. Проверено на нашем сервере Left 4 Dead 2. В панель администратора добавляется 4 новых раздела.

Команды:

  • sm_swap <игрок> - поменять местами команду игрока (ов).
  • sm_swap @all - поменять местами сразу всех игроков
  • sm_swapround <игрок> - поменять местами команду игрока (ов) в конце раунда / при смерти игрока.
  • sm_exch <игрок 1> <игрок 2> - меняет местами двух игроков в противоположных командах.
  • sm_exchround <игрок 1> <игрок 2> - меняет местами двух игроков в противоположных командах в конце раунда / при смерти игрока.

Перемещение игрока происходит мгновенно. Без таймеров и кулдаунов.

swapper.zip

4_105.png

> Никакой помощи через личку. Что то нужно, создавайте тему!


Личные сообщения я отключил, потому что вы не понимаете, что я прошу писать публично, дабы повысить актив на сайте. А вы игнорируете просьбы и пишете ЛС... 😭

 

Link to post
  • Administrator

Не рекомендуется использовать данную версию плагина на публичных серверах.

Мне надо было протестировать один из плагинов, и для тестов мне понадобилось установить этот плагин. И благодаря этому я обнаружил следующую ошибку.

С помощью спама командой "Сменить Команду", можно заспавнить огромное количество ботов.

 

И вообще, копаясь в исходнике плагина я понял что он написан исключительно для Counter Strike: Source. Как он у меня заработал (Хоть и с багом), ума не приложу.

#pragma semicolon 1
#include <sourcemod>
#include <sdktools>

#undef REQUIRE_EXTENSIONS
#include <cstrike>
#undef REQUIRE_PLUGIN
#include <adminmenu>

#define PLUGIN_VERSION "0.02"

#define YELLOW               0x01
#define NAME_TEAMCOLOR       0x02
#define TEAMCOLOR            0x03
#define GREEN                0x04 

public Plugin:myinfo =
{
	name = "Player swapper",
	author = "X@IDER",
	description = "Useful tool for swapping players",
	version = PLUGIN_VERSION,
	url = "http://www.sourcemod.net/"
};

// Array for end round swaps
new NewTeam[64];

// Handle to top menu
new Handle:hTopMenu;

// Handles to change models
new Handle:hGameConf;
new Handle:hSetModel;

// Handle to drop weapons
new Handle:hDrop;

// Teams
new TEAM1,TEAM2;
new game;

new String:t_models[4][PLATFORM_MAX_PATH] =
{
	"models/player/t_phoenix.mdl",
	"models/player/t_leet.mdl",
	"models/player/t_arctic.mdl",
	"models/player/t_guerilla.mdl"
};

new String:ct_models[4][PLATFORM_MAX_PATH] =
{
	"models/player/ct_urban.mdl",
	"models/player/ct_gsg9.mdl",
	"models/player/ct_sas.mdl",
	"models/player/ct_gign.mdl"
};

// Team names
new String:teams[4][16] = 
{
	"N/A",
	"SPEC",
	"T",
	"CT"
};

public OnPluginStart()
{
	LoadTranslations("common.phrases");
	LoadTranslations("plugin.swapper");

	decl String:gdir[PLATFORM_MAX_PATH];
	GetGameFolderName(gdir, sizeof(gdir));
	if (StrEqual(gdir,"cstrike",false)) game = 0;
	else if (StrEqual(gdir,"dod",false)) game = 1;
	else if (StrEqual(gdir,"hl2mp",false)) game = 2;
	else if (StrEqual(gdir,"Insurgency",false)) game = 3;
	else if (StrEqual(gdir,"tf",false)) game = 4;
	else game = 5;

	TEAM1 = 2;
	TEAM2 = 3;
	
	// Loading SetModel & WeaponDrop for CS:S
	if (!game)
	{
		hGameConf = LoadGameConfigFile("swapper.gamedata");
	
		StartPrepSDKCall(SDKCall_Player);
		PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "SetModel");
		PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer);
		hSetModel = EndPrepSDKCall();

		StartPrepSDKCall(SDKCall_Player);
		PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "DropWeapon");
		PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer);
		PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain);
		PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain);
		hDrop = EndPrepSDKCall();
	}

	RegAdminCmd("sm_swap", Swap, ADMFLAG_GENERIC);
	RegAdminCmd("sm_swapround", SwapRound, ADMFLAG_GENERIC);
	RegAdminCmd("sm_exch", Exchange, ADMFLAG_GENERIC);
	RegAdminCmd("sm_exchround", ExchangeRound, ADMFLAG_GENERIC);

	if (game) HookEvent("player_death",PlayerDeath);

	HookEvent("player_spawn",PlayerSpawn);
	HookEvent("round_end",RoundEnd);
}

public OnMapStart()
{
	// Getting team names for mods other than cstrike
	if (game)
	{
		GetTeamName(TEAM1,teams[TEAM1],16);
		GetTeamName(TEAM2,teams[TEAM2],16);
	}
}

public PrintToChatAllEx(from,const String:format[], any:...)
{
	decl String:message[256];
	VFormat(message,sizeof(message),format,3);
	
	if (game == 1)
	{
		PrintToChatAll(message);
		return;
	}

	new Handle:hBf = StartMessageAll("SayText2");
	if (hBf != INVALID_HANDLE)
	{
		BfWriteByte(hBf, from);
		BfWriteByte(hBf, true);
		BfWriteString(hBf, message);
	
		EndMessage();
	}
}

public ChangeClientTeamEx(client,team)
{
	if (game)
	{
		ChangeClientTeam(client,team);
		return;
	}

	new oldTeam = GetClientTeam(client);
	CS_SwitchTeam(client,team);

	decl String:model[PLATFORM_MAX_PATH],String:newmodel[PLATFORM_MAX_PATH];
	GetClientModel(client,model,sizeof(model));
	newmodel = model;

	if (oldTeam == TEAM1)
	{
		new c4 = GetPlayerWeaponSlot(client,CS_SLOT_C4);
		if (c4 != -1) SDKCall(hDrop,client,c4,true,false);

		if (StrContains(model,t_models[0],false)) newmodel = ct_models[0];
		if (StrContains(model,t_models[1],false)) newmodel = ct_models[1];
		if (StrContains(model,t_models[2],false)) newmodel = ct_models[2];
		if (StrContains(model,t_models[3],false)) newmodel = ct_models[3];		
	} else
	if (oldTeam == TEAM2)
	{
		SetEntProp(client, Prop_Send, "m_bHasDefuser", 0, 1);

		if (StrContains(model,ct_models[0],false)) newmodel = t_models[0];
		if (StrContains(model,ct_models[1],false)) newmodel = t_models[1];
		if (StrContains(model,ct_models[2],false)) newmodel = t_models[2];
		if (StrContains(model,ct_models[3],false)) newmodel = t_models[3];		
	}

	SDKCall(hSetModel, client, newmodel);
}

public SwapPlayer(client,target)
{
	if (GetClientTeam(target) == TEAM1) ChangeClientTeamEx(target,TEAM2); else
	if (GetClientTeam(target) == TEAM2) ChangeClientTeamEx(target,TEAM1);
}

public SwapPlayerRound(client,target)
{
	decl String:buffer[64];
	GetClientName(target,buffer,sizeof(buffer));
	if (NewTeam[target])
	{
		PrintToChatAllEx(target,"%t","Swap Cancel",YELLOW,TEAMCOLOR,buffer,YELLOW,GREEN,teams[NewTeam[target]],YELLOW);
		NewTeam[target] = 0;
		return;
	}
	if (GetClientTeam(target) == TEAM1) NewTeam[target] = TEAM2; else
	if (GetClientTeam(target) == TEAM2) NewTeam[target] = TEAM1;
	PrintToChatAllEx(target,"%t","Swap",YELLOW,TEAMCOLOR,buffer,YELLOW,GREEN,teams[NewTeam[target]],YELLOW);
}

public ExchangePlayers(client,cl1,cl2)
{
	if ((GetClientTeam(cl1) == TEAM1) && (GetClientTeam(cl2) == TEAM2))
	{
		ChangeClientTeamEx(cl1,TEAM2);
		ChangeClientTeamEx(cl2,TEAM1);
	} else
	if ((GetClientTeam(cl1) == TEAM2) && (GetClientTeam(cl2) == TEAM1))
	{
		ChangeClientTeamEx(cl1,TEAM1);
		ChangeClientTeamEx(cl2,TEAM2);
	} else
	ReplyToCommand(client,"%t","Bad targets");
}

public ExchangePlayersRound(client,cl1,cl2)
{
	if (((GetClientTeam(cl1) == TEAM1) && (GetClientTeam(cl2) == TEAM2)) || 
		((GetClientTeam(cl1) == TEAM2) && (GetClientTeam(cl2) == TEAM1)))
	{
		SwapPlayerRound(client,cl1);
		SwapPlayerRound(client,cl2);
	} else
	ReplyToCommand(client,"%t","Bad targets");
}

public Action:Swap(client,args)
{
	if (!args)
	{
		ReplyToCommand(client,"\x04sm_swap <player>");
		return Plugin_Handled;
	}
	decl String:pattern[64],String:buffer[64];
	GetCmdArg(1,pattern,sizeof(pattern));

	new Targets[64],bool:mb;

	new count = ProcessTargetString(pattern,client,Targets,sizeof(Targets),0,buffer,sizeof(buffer),mb);

	if (!count) ReplyToCommand(client,"%t","No target",YELLOW,TEAMCOLOR,pattern,YELLOW);
	else
	for (new i = 0; i < count; i++) SwapPlayer(client,Targets[i]);

	return Plugin_Handled;
}

public Action:SwapRound(client,args)
{
	if (!args)
	{
		ReplyToCommand(client,"\x04sm_swapround <player>");
		return Plugin_Handled;
	}
	new String:pattern[64],String:buffer[64];
	GetCmdArg(1,pattern,sizeof(pattern));

	new Targets[64],bool:mb;

	new count = ProcessTargetString(pattern,client,Targets,sizeof(Targets),0,buffer,sizeof(buffer),mb);

	if (!count) ReplyToCommand(client,"%t","No target",YELLOW,TEAMCOLOR,pattern,YELLOW);
	else
	for (new i = 0; i < count; i++) SwapPlayerRound(client,Targets[i]);

	return Plugin_Handled;	
}

public Action:Exchange(client,args)
{
	if (args < 2)
	{
		ReplyToCommand(client,"\x04sm_exch <player1> <player2>");
		return Plugin_Handled;
	}

	new String:p1[64],String:p2[64];
	GetCmdArg(1,p1,sizeof(p1));
	GetCmdArg(2,p2,sizeof(p2));

	new cl1 = FindTarget(client,p1);
	new cl2 = FindTarget(client,p2);

	if (cl1 == -1) ReplyToCommand(client,"%t","No target",YELLOW,TEAMCOLOR,p1,YELLOW);
	if (cl2 == -1) ReplyToCommand(client,"%t","No target",YELLOW,TEAMCOLOR,p2,YELLOW);

	if ((cl1 > 0) && (cl2 > 0)) ExchangePlayers(client,cl1,cl2);

	return Plugin_Handled;	
}

public Action:ExchangeRound(client,args)
{
	if (args < 2)
	{
		ReplyToCommand(client,"\x04sm_exchround <player1> <player2>");
		return Plugin_Handled;
	}

	new String:p1[64],String:p2[64];
	GetCmdArg(1,p1,sizeof(p1));
	GetCmdArg(2,p2,sizeof(p2));

	new cl1 = FindTarget(client,p1);
	new cl2 = FindTarget(client,p2);

	if (cl1 == -1) ReplyToCommand(client,"%t","No target",YELLOW,TEAMCOLOR,p1,YELLOW);
	if (cl2 == -1) ReplyToCommand(client,"%t","No target",YELLOW,TEAMCOLOR,p2,YELLOW);

	if ((cl1 > 0) && (cl2 > 0)) ExchangePlayersRound(client,cl1,cl2);

	return Plugin_Handled;	
}

public PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
	// mystery protection
	new client = GetClientOfUserId(GetEventInt(event,"userid"));
	NewTeam[client] = 0;
}

public PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
	new client = GetClientOfUserId(GetEventInt(event,"userid"));
	if (NewTeam[client])
	{
		ChangeClientTeamEx(client,NewTeam[client]);
		NewTeam[client] = 0;
	}
}

public RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
	for (new i = 1; i < sizeof(NewTeam); i++)
	if (NewTeam[i] && IsClientInGame(i))
	{
		ChangeClientTeamEx(i,NewTeam[i]);
		NewTeam[i] = 0;
	}
}

public OnAdminMenuReady(Handle:topmenu)
{
	if (topmenu == hTopMenu)
	{
		return;
	}
	hTopMenu = topmenu;

	new TopMenuObject:player_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_PLAYERCOMMANDS);

	if (player_commands != INVALID_TOPMENUOBJECT)
	{
		AddToTopMenu(hTopMenu,"sm_swap",TopMenuObject_Item,AdminMenu_Swap,player_commands,"sm_swap",ADMFLAG_GENERIC);
		AddToTopMenu(hTopMenu,"sm_swapround",TopMenuObject_Item,AdminMenu_SwapRound,player_commands,"sm_swap",ADMFLAG_GENERIC);
		AddToTopMenu(hTopMenu,"sm_exch",TopMenuObject_Item,AdminMenu_Exchange,player_commands,"sm_swap",ADMFLAG_GENERIC);
		AddToTopMenu(hTopMenu,"sm_exchround",TopMenuObject_Item,AdminMenu_ExchangeRound,player_commands,"sm_swap",ADMFLAG_GENERIC);
	}
}

public AdminMenu_Swap(Handle:topmenu,TopMenuAction:action,TopMenuObject:object_id,param,String:buffer[],maxlength)
{
	if (action == TopMenuAction_DisplayOption)
	{
		Format(buffer, maxlength, "%T", "Swap Now", param);
	}
	else if (action == TopMenuAction_SelectOption)
	{
		DisplaySwapMenu(param,false);
	}
}

public AdminMenu_SwapRound(Handle:topmenu,TopMenuAction:action,TopMenuObject:object_id,param,String:buffer[],maxlength)
{
	if (action == TopMenuAction_DisplayOption)
	{
		Format(buffer, maxlength, "%T", "Swap Round", param);
	}
	else if (action == TopMenuAction_SelectOption)
	{
		DisplaySwapMenu(param,true);
	}
}

public AdminMenu_Exchange(Handle:topmenu,TopMenuAction:action,TopMenuObject:object_id,param,String:buffer[],maxlength)
{
	if (action == TopMenuAction_DisplayOption)
	{
		Format(buffer, maxlength, "%T", "Exchange Now", param);
	}
	else if (action == TopMenuAction_SelectOption)
	{
		DisplayExchangeMenu(param,false);
	}
}

public AdminMenu_ExchangeRound(Handle:topmenu,TopMenuAction:action,TopMenuObject:object_id,param,String:buffer[],maxlength)
{
	if (action == TopMenuAction_DisplayOption)
	{
		Format(buffer, maxlength, "%T", "Exchange Round", param);
	}
	else if (action == TopMenuAction_SelectOption)
	{
		DisplayExchangeMenu(param,true);
	}
}

public MenuHandler_Swap(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_End) CloseHandle(menu);
    else if (action == MenuAction_Cancel)
    {
        if ((param2 == MenuCancel_ExitBack) && (hTopMenu != INVALID_HANDLE))
            DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
    }
	else if (action == MenuAction_Select)
	{
		decl String:title[100],String:id[16],String:Round[100];
        GetMenuItem(menu, param2, id, sizeof(id));
		new target = GetClientOfUserId(StringToInt(id));

		GetMenuTitle(menu, title, sizeof(title));
		Format(Round, sizeof(Round), "%t", "Swap Round", param1);
		if (!strcmp(Round,title)) SwapPlayerRound(param1,target);
		else SwapPlayer(param1,target);

		DisplaySwapMenu(param1,!strcmp(Round,title));
	}
}

public DisplaySwapMenu(client,bool:round)
{
	new Handle:menu = CreateMenu(MenuHandler_Swap);
	SetMenuExitBackButton(menu,true);
	
	decl String:title[100],String:name[64],String:id[16];
	if (round) Format(title, sizeof(title), "%t", "Swap Round", client);
	else Format(title, sizeof(title), "%t", "Swap Now", client);
	SetMenuTitle(menu, title);

	for (new i = 1; i <= MaxClients; i++)
	if (IsClientInGame(i))
	{
		new team = GetClientTeam(i);
		if (team > CS_TEAM_SPECTATOR)
		{
			GetClientName(i,name,sizeof(name));
			if (NewTeam[i]) Format(title, sizeof(title), "[>>%s] %s",teams[NewTeam[i]],name);
			else Format(title, sizeof(title), "[%s] %s",teams[team],name);
			IntToString(GetClientUserId(i),id,sizeof(id));
			AddMenuItem(menu,id,title);
		}
	}

	DisplayMenu(menu, client, MENU_TIME_FOREVER);
}

public MenuHandler_Exchange2(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_End)
		CloseHandle(menu);
    else if (action == MenuAction_Cancel)
    {
        if ((param2 == MenuCancel_ExitBack) && (hTopMenu != INVALID_HANDLE))
            DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
    }
	else if (action == MenuAction_Select)
	{
		decl String:id1[16],String:id2[16],String:Round[100];
        GetMenuItem(menu, 0, id1, sizeof(id1));
        GetMenuItem(menu, param2, id2, sizeof(id2));

		new cl1 = GetClientOfUserId(StringToInt(id1));
		new cl2 = GetClientOfUserId(StringToInt(id2));

		decl String:title[100];
		GetMenuTitle(menu, title, sizeof(title));
		Format(Round, sizeof(Round), "%t", "Exchange Round", param1);
		if (!strcmp(Round,title)) ExchangePlayersRound(param1,cl1,cl2);
		else ExchangePlayers(param1,cl1,cl2);
	}
}

public MenuHandler_Exchange(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_End)
		CloseHandle(menu);
    else if (action == MenuAction_Cancel)
    {
        if ((param2 == MenuCancel_ExitBack) && (hTopMenu != INVALID_HANDLE))
            DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
    }
	else if (action == MenuAction_Select)
	{
		decl String:title[100],String:id[16],String:name[64];
        GetMenuItem(menu, param2, id, sizeof(id));
		new target = GetClientOfUserId(StringToInt(id));

		new team = GetClientTeam(target);

		new Handle:menu2 = CreateMenu(MenuHandler_Exchange2);
		SetMenuExitBackButton(menu2,true);
		GetMenuTitle(menu, title, sizeof(title));
		SetMenuTitle(menu2, title);

		GetClientName(target,name,sizeof(name));
		Format(title, sizeof(title), "[%s] %s",teams[team],name);
		AddMenuItem(menu2,id,title,ITEMDRAW_DISABLED);

		for (new i = 1; i <= MaxClients; i++)
		if (IsClientInGame(i) && (i != target))
		{
			new team2 = GetClientTeam(i);
			if ((team2 > CS_TEAM_SPECTATOR) && (team != team2))
			{
				GetClientName(i,name,sizeof(name));
				Format(title, sizeof(title), "[%s] %s",teams[team2],name);
				IntToString(GetClientUserId(i),id,sizeof(id));
				AddMenuItem(menu2,id,title);
			}
		}
		DisplayMenu(menu2, param1, MENU_TIME_FOREVER);
	}
}

public DisplayExchangeMenu(client,bool:round)
{
	new Handle:menu = CreateMenu(MenuHandler_Exchange);
	SetMenuExitBackButton(menu,true);
	
	decl String:title[100],String:name[64],String:id[16];
	if (round) Format(title, sizeof(title), "%t", "Exchange Round", client);
	else Format(title, sizeof(title), "%t", "Exchange Now", client);
	SetMenuTitle(menu, title);

	for (new i = 1; i <= MaxClients; i++)
	if (IsClientInGame(i))
	{
		new team = GetClientTeam(i);
		if (team > CS_TEAM_SPECTATOR)
		{
			GetClientName(i,name,sizeof(name));
			Format(title, sizeof(title), "[%s] %s",teams[team],name);
			IntToString(GetClientUserId(i),id,sizeof(id));
			AddMenuItem(menu,id,title);
		}
	}

	DisplayMenu(menu, client, MENU_TIME_FOREVER);
}

 

4_105.png

> Никакой помощи через личку. Что то нужно, создавайте тему!


Личные сообщения я отключил, потому что вы не понимаете, что я прошу писать публично, дабы повысить актив на сайте. А вы игнорируете просьбы и пишете ЛС... 😭

 

Link to post
  • Administrator

20200819011632_1.jpg

Версия, которая кикает абсолютно всех ботов при подключении. Это может пригодится, если сервер заточен на игру без ботов.

 

swapper.smx swapper.sp

4_105.png

> Никакой помощи через личку. Что то нужно, создавайте тему!


Личные сообщения я отключил, потому что вы не понимаете, что я прошу писать публично, дабы повысить актив на сайте. А вы игнорируете просьбы и пишете ЛС... 😭

 

Link to post
  • Similar Content

    • By Shadow
      Я тут плагин нашел для рейтинга.
      @Ryancoolround Мне нужно чтобы ты на сайте сделал систему рейтинга.
      Я тебе там кое чего скинул, посмотри.
    • By Shadow
      Предлагаю сервера добавить на популярные группы стим, объясняю , допустим пользователь подписан на группу Steam Client Beta,то зайдя в left 4 dead 2 в списке серверов групп стим он увидите наш сервер,а так же сделать чтобы могли присоединяться игроки с пиратки и таким образом там почти всегда люди будут и сделать так что бы на них можно было в любую кампанию с играть и в любой режим, тогда и сервера простаивать не будут.
      Так же можно придумать систему рейтинга и подарков (вип например, который будет давать разные плюшки),чтобы у пользователей был стимул играть.
      Никакой продажи випа или еще чего либо ,всё бесплатно.
      p.s
      Еще бы понять как сделать чтобы когда через лобби создаешь игру и выбираешь лучший сервер, попадался наш сервак.
    • By Ryancoolround
      Обновление сообщества Left 4 Dead 2: The Last Stand выходит 24 сентября, и мы празднуем его бесплатными выходными!
      Хватайте своих друзей и сражайтесь с ордой зомби на более чем двадцати новых аренах выживания, четырех новых аренах сбора и кампании, основанной на карте выживания Маяк. Новые достижения, холодное оружие, анимация, диалоги, корректировка игрового баланса и другие сюрпризы завершают это официальное обновление Left 4 Dead 2, созданное сообществом для сообщества.
      Взгляните на новый трейлер ниже!
       
    • By Ryancoolround
      Что же друзья! Мы долго ждали! Я не делал обновления плагинов, в преддверии того, что сейчас не т смысла этого делать. Я представляю вам официальный трейлер обновления «Последний бой» которое выйдет на ПК 24 сентября.
      Это официально, это новый контент, который будет получать Left 4 Dead 2, но это еще не все!
      Также будет 30 новых достижений, 2 совершенно новые мутации Rocket Dude и Tank Run и множество исправлений ошибок и эксплойтов! Это обновление - результат совместных усилий 30+ членов сообщества, которые работали вместе с Valve в течение 11 месяцев над созданием нового контента для этой любимой игры, которая будет доступна всем игрокам на ПК в виде бесплатного DLC уже 24 сентября!
       

       
       
       
      Помимо этого мы еще получим:
      4 новых карты для сбора 2 новых оружия ближнего боя (Лопата и Вилы) Новые и ранее не использованные диалоги Новые анимации персонажей Изменение баланса в PvP Новые лестницы для зараженных Улучшение пользовательского интерфейса А помимо этого у Left 4 Dead 2 появятся наконец таки бесплатные выходные, которых не было уже с 2013 года. Счастья вам друзья! Мы ждали этого вечность!
       
       
    • By Ryancoolround
      Ну, как и предпологалось, новое обновление Left 4 Dead 2 принесет нам новые достижения. На всякий случай вот ссылка на источник скриншота.
      Упоминание "Collect Gold Crowbar", намекает нам на то, что возможно в обновлении мы на карте теперь будем собирать золотые монтировки для некой коллекции.
      Я сплю и вижу как я фармлю эти награды, ведь L4D2 для меня в Steam идеальная игра. У меня выполнены все ачивки, и как я понял, возможно мне в будущем придется повозится.
       
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...