PDA

Просмотр полной версии : [Function] SetPlayerPosEx



#iDanny
06.03.2018, 18:47
Всем привет.

В группе форума столкнулся на одного интересного персонажа, он не мог решить проблему с подгрузкой объектов. Накидал ему функцию чтобы "фризило", воть. Сейчас успокоился, немного оптимизировал и добавил функционала и делюсь с вами.

Параметры:
*обязательные*

playerid - id телепортируемого игрока
x, y, z - координаты X, Y, Z телепортируемого места
*не обязательные*

angle - угол поворота
virtualworldid - id виртуального мира
interiorid - id интеръера
vehicletp - режим телепорта транспорта (1 - ТП с транспортом, 0 - Без). П.с: условии для проверки на нахождение в транспорте не требуется, функция это предусматривает.
passengertp - режим телепорта пассажиров (работает при включенном "vehicletp"): 0 - слапнуть, 1 - телепортировать вместе.
freezetime - время до разморозки игрока (используйте 0 чтобы не замораживать). Default: 0


Пример использования:

CMD:tp(playerid)
{
SetPlayerPosEx(playerid, 0.0, 0.0, 2.0);
return SendClientMessage(playerid, -1, !"Вы телепортированы в центр карты.");
}

Функция:

stock
SetPlayerPosEx(playerid, Float:x, Float:y, Float:z, Float:angle = 0.0,
virtualworldid = -1, interiorid = -1, vehicletp = 0, passengertp = 0, freezetime = 0)
{
new
vehicleid = GetPlayerVehicleID(playerid);

#if defined Streamer_IncludeFileVersion
Streamer_UpdateEx(playerid, x, y, z, virtualworldid, interiorid);
#endif
if(!vehicletp || GetPlayerState(playerid) != PLAYER_STATE_DRIVER)
{
SetPlayerPos(playerid, x, y, z);

if(-1 != interiorid)
SetPlayerInterior(playerid, interiorid);

if(-1 != virtualworldid)
SetPlayerVirtualWorld(playerid, virtualworldid);

if(0.0 != angle)
SetPlayerFacingAngle(playerid, angle);

if(freezetime)
{
TogglePlayerControllable(playerid, 0);
SetTimerEx(!"@__unfreezeLoadInt", freezetime, 0, !"i", playerid);
}
return SetCameraBehindPlayer(playerid);
}

static
Float:pos_x,
Float:pos_y,
Float:pos_z;

GetPlayerPos(playerid, pos_x, pos_y, pos_z);

if(-1 != interiorid)
LinkVehicleToInterior(vehicleid, interiorid);

if(-1 != virtualworldid)
SetVehicleVirtualWorld(vehicleid, virtualworldid);

#if defined foreach
foreach(Player, i)
#else
for(new i; i < MAX_PLAYERS; i++)
#endif
{
if(GetPlayerVehicleID(i) == vehicleid)
{
if(!passengertp)
{
#if defined Streamer_IncludeFileVersion
Streamer_UpdateEx(i, x, y, z, virtualworldid, interiorid);
#endif
if(-1 != interiorid)
SetPlayerInterior(i, interiorid);

if(-1 != virtualworldid)
SetPlayerVirtualWorld(i, virtualworldid);

SetCameraBehindPlayer(i);
}
else
SetPlayerPos(i, pos_x, pos_y+0.5, pos_z+1.0);
}
}
SetVehiclePos(vehicleid, x, y, z + 1.0);

if(angle != 0.0)
SetVehicleZAngle(vehicleid, angle);

SetPlayerPos(playerid, x, y, z);
PutPlayerInVehicle(playerid, vehicleid, 0);
return SetCameraBehindPlayer(playerid);
}

@__unfreezeLoadInt(playerid);
@__unfreezeLoadInt(playerid)
return TogglePlayerControllable(playerid, 1);

Автор: Danny Moore

DeimoS
06.03.2018, 19:47
http://pro-pawn.ru/showthread.php?6278

Daniel_Cortez
06.03.2018, 23:48
http://pro-pawn.ru/showthread.php?6278
Следует заметить, что тот инклуд был написан ещё в конце 2013-го, я тогда не учёл возможность телепортации пассажиров в транспорте (которая учтена в этой теме). Впрочем, это не так уж и сложно добавить.

whale
07.03.2018, 12:47
Следует заметить, что тот инклуд был написан ещё в конце 2013-го, я тогда не учёл возможность телепортации пассажиров в транспорте (которая учтена в этой теме). Впрочем, это не так уж и сложно добавить.

Думаю, что вариант ниже вполне подойдёт (если нет - укажите на ошибки, пожалуйста). Правда, пришлось сначала делать проверку с tppassenger и уже после запускать цикл, ибо как-то не хотелось создавать 3 переменные при каждой итерации (в случае с функцией ТС`а игроков телепортирует на координаты playerid и немного в сторону, в этом случае их просто смещает с их координат).


stock SetPlayerPosEx(playerid, Float:x, Float:y, Float:z, interior = cellmin, world = cellmin, tpcar = 1, tppassenger = 1)
{
Streamer_ToggleIdleUpdate(playerid, 1);

if(dc_spp__control{playerid})
{
TogglePlayerControllable(playerid, 0);
SetTimerEx("@__dc_spp__unfreeze", DC_SPP__FREEZE_TIME, 0, "d", playerid);
}

Streamer_UpdateEx(playerid, x, y, z, world, interior);

if(interior != cellmin)
SetPlayerInterior(playerid, interior);

if(world != cellmin)
SetPlayerVirtualWorld(playerid, world);

if(tpcar && GetPlayerState(playerid) == PLAYER_STATE_DRIVER)
{
new
veh = GetPlayerVehicleID(playerid);

if(world != cellmin)
SetVehicleVirtualWorld(veh, world);

SetVehiclePos(veh, x, y, z);

if(tppassenger)
{
#if defined foreach
foreach(new i: Player)
#elseif defined GetPlayerPoolSize
for(new i = GetPlayerPoolSize(); i >= 0; i--)
#else
for(new i = MAX_PLAYERS; i >= 0; i--)
#endif
{
if(0 == IsPlayerConnected(i))
continue;

if(i != playerid && GetPlayerVehicleID(i) == veh)
{
if(interior != cellmin)
SetPlayerInterior(i, interior);

if(world != cellmin)
SetPlayerVirtualWorld(i, world);
}
}
}
else
{
new
Float:pos_x,
Float:pos_y,
Float:pos_z;

#if defined foreach
foreach(new i: Player)
#elseif defined GetPlayerPoolSize
for(new i = GetPlayerPoolSize(); i >= 0; i--)
#else
for(new i = MAX_PLAYERS; i >= 0; i--)
#endif
{
if(0 == IsPlayerConnected(i))
continue;

if(i != playerid && GetPlayerVehicleID(i) == veh)
{
GetPlayerPos(i, pos_x, pos_y, pos_z);
SetPlayerPos(i, pos_x, pos_y + 0.5, pos_z + 1.0);
}
}
}
}
else
SetPlayerPos(playerid, x, y, z);
}

Daniel_Cortez
07.03.2018, 12:59
Думаю, что вариант ниже вполне подойдёт (если нет - укажите на ошибки, пожалуйста). Правда, пришлось сначала делать проверку с tppassenger и уже после запускать цикл, ибо как-то не хотелось создавать 3 переменные при каждой итерации (в случае с функцией ТС`а игроков телепортирует на координаты playerid и немного в сторону, в этом случае их просто смещает с их координат.
Спасибо, конечно, но я уже добавил нужный код, правда, не настолько сложный (хотя бы потому, что в моём варианте нет опции tppassenger, хоть я и могу добавить её в будущем, если кому понадобится).

EDIT: Очень некрасиво смотрится, когда одна ветка if взята в фигурные скобки, а другая нет, сбивает с толку.


if(tpcar && GetPlayerState(playerid) == PLAYER_STATE_DRIVER)
{
// ...
}
else
SetPlayerPos(playerid, x, y, z);

Поскольку после ветки else больше никакого кода нет (конец функции), ИМХО лучше сделать ветвление по принципу отсечки, без else.


if(tpcar == 0 || GetPlayerState(playerid) != PLAYER_STATE_DRIVER)
return SetPlayerPos(playerid, x, y, z);
// ...

Также в цикле с перебором игроков вызывается IsPlayerConnected() - зачем? Мало того, что это не нужно в случае с foreach, так проверка на подключение и без того встроена в другие функции, работающие с ID игроков. Например, GetPlayerVehicleID() вернёт 0 (неправильный ID транспорта), если игрок не подключен, т.е. можно сделать так:


if (GetPlayerVehicleID() != veh)
continue;

Вот так элегантно можно вызовом одной функции и проверить подключение, и получить ID ТС игрока.

Учитывать возможность того, что функция GetPlayerPoolSize() не объявлена, тоже сомнительно, времена 0.3z прошли ещё 3 года назад.

Касаемо содержимого цикла, проверку на равенство i и playerid можно выкинуть, вынеся действия только с playerid в ветку if, в которой обрабатывается ситуация, когда игрок не за рулём (см. выше).


if(tpcar == 0 || GetPlayerState(playerid) != PLAYER_STATE_DRIVER)
{
if(interior != -1)
SetPlayerInterior(playerid, interior);
if(world != -1)
SetPlayerVirtualWorld(playerid, world);
return SetPlayerPos(playerid, x, y, z);
}


Также можно сэкономить несколько байт за счёт упаковки строк.

#iDanny
07.03.2018, 20:08
Скоро обновлю функцию. Меня больше беспокоит то, что если игрок заморожен "в другом скрипте" (например командой /freeze) мой скрипт разморозит его. Если есть мысли по этой теме - буду рад их услышать.

Daniel_Cortez
07.03.2018, 22:36
Скоро обновлю функцию. Меня больше беспокоит то, что если игрок заморожен "в другом скрипте" (например командой /freeze) мой скрипт разморозит его. Если есть мысли по этой теме - буду рад их услышать.
Насколько знаю, нет такой функции, которой можно узнать состояние заморозки игрока, и которая правильно работает при заморозке из других скриптов. ИМХО, самый железный способ здесь - перехватить функцию TogglePlayerControllable из плагина, чтобы отслеживать заморозку из всех скриптов, и из того же плагина предоставить функцию IsPlayerControllable. Перебор это или нет - вам решать.
EDIT: Либо это, либо взаимодействие с помощью PVar'ов.

#iDanny
07.03.2018, 22:49
Daniel_Cortez,

Увы, скорее всего так и есть. Думал вынести функцию в отдельный инклюд и перехватить функцию TogglePlayerControllable в скрипте, но я счёл это "вмешательством" в чужой код. Увы, не знаток в написании плагинов, скорее всего так и оставлю. Пользователям остается предусматривать это в своих модах, или коллективно просить кую добавить функцию IsPlayerControllable :dirol:

#iDanny
08.03.2018, 02:01
Функция обновлена до версии 1.2.

Исправлена ошибка, когда транспорту задавалось неправильный ID виртуального мира.
Добавлена поддержка стримера, теперь загрузка объектов начинается до телепорта игрока в указанное место.

SliM
08.03.2018, 09:54
а зачем эти условия?

if(-1 != interiorid)
SetPlayerInterior(playerid, interiorid);
if(-1 != virtualworldid)
SetPlayerVirtualWorld(playerid, virtualworldid);
if(0.0 != angle)
SetPlayerFacingAngle(playerid, angle);
по дефолту установить 0 заместо -1

Float:angle = 0.0, virtualworldid = 0, interiorid = 0
SetPlayerInterior(playerid, interiorid);
SetPlayerVirtualWorld(playerid, virtualworldid);
SetPlayerFacingAngle(playerid, angle);
ну с углом поворота можно и оставить условие...

DeimoS
08.03.2018, 10:01
Ну так а если тебе нужно телепортировать игрока, который находится в виртуальном мире, в тот же виртуальный мир (пусть даже нулевой)? Проверить условие будет быстрее и проще, нежели отправить пакет игроку, дабы тот установил виртуальный мир по новой.

SliM
08.03.2018, 10:07
Ну так а если тебе нужно телепортировать игрока, который находится в виртуальном мире, в тот же виртуальный мир (пусть даже нулевой)? Проверить условие будет быстрее и проще, нежели отправить пакет игроку, дабы тот установил виртуальный мир по новой.
Использовать SetPlayerPos а если даже из мира в мир телепортировать и миры одинаковы, сработает и условие и пакет отправится. А если ты заведомо знаешь что и интерьер и мир равны, проще и быстрее будет дефолтная SetPlayerPos
EDD:сижу и думаю, что за х**ню я написал

DeimoS
08.03.2018, 10:26
Использовать SetPlayerPos а если даже из мира в мир телепортировать и миры одинаковы, сработает и условие и пакет отправится. А если ты заведомо знаешь что и интерьер и мир равны, проще и быстрее будет дефолтная SetPlayerPos
EDD:сижу и думаю, что за х**ню я написал

Ты немного не понимаешь предназначения данной функции. Она нужна, в основном, чтоб телепортировать игроков на серверный маппинг, который должен успеть прогрузиться.
Если я, например, сделал большой интерьер, но, дабы не было проблем с прогрузкой, разделил его на комнаты и расположил их на расстоянии (так, чтоб они не попадали в зону стрима), то зачем мне тревожить виртуальный мир и интерьер в случае перехода между комнатами? Если сделать функцию такой, какой предложил ты, интерьер и виртуальный мир придётся устанавливать всегда (иначе игроки будут перемещаться в нулевой виртуальный мир и нулевой интерьер). В текущем же случае я могу просто не передавать значения в аргументы виртуального мира/интерьера и никакие пакеты отправляться не будут.

SliM
08.03.2018, 10:47
Ты немного не понимаешь предназначения данной функции. Она нужна, в основном, чтоб телепортировать игроков на серверный маппинг, который должен успеть прогрузиться.
Если я, например, сделал большой интерьер, но, дабы не было проблем с прогрузкой, разделил его на комнаты и расположил их на расстоянии (так, чтоб они не попадали в зону стрима), то зачем мне тревожить виртуальный мир и интерьер в случае перехода между комнатами? Если сделать функцию такой, какой предложил ты, интерьер и виртуальный мир придётся устанавливать всегда (иначе игроки будут перемещаться в нулевой виртуальный мир и нулевой интерьер). В текущем же случае я могу просто не передавать значения в аргументы виртуального мира/интерьера и никакие пакеты отправляться не будут.

да, я понял.