а) Снаряд так и не "воскрешается". Т. е. после перехода из Death в Spawn просто висит на месте и не исчезает даже при столкновении с чем-либо.
Ага, я совсем забыл одну деталь.
Я правильно понимаю, что он оживляется (и об него можно убиться), но висит на месте?
Если да, то проблема в том, что он потерял скорость. Нужно снова его пнуть. У меня это было реализовано через три узервара, которые на старте запоминали абсолютную скорость по трём координатам:
MISS F 0 A_SetUserVar("user_VelX",VelX)
MISS F 0 A_SetUserVar("user_VelY",VelY)
MISS F 0 A_SetUserVar("user_VelZ",VelZ)
А после реанимации снаряда помимо восстановления флага делается A_ChangeVelocity(user_VelX, user_VelY, user_VelZ, CVF_REPLACE)
Void Weaver:
а) Как "воскрешать" снаряд при ударе с шутебл акторами, но убивать оный при столкновении с обычными препятствиями (стенами, полами, и т. д.)?
Очевидно, проверять наличие трасера, как таковое. A_JumpIf(IsPointerEqual(AAPTR_TRACER,AAPTR_NULL) == TRUE,"Death2")
Ну и универсальное: A_Jumpif(CallACS("Скриптнейм") == 1,"Death2"). В скриптнейме уже конкретную проверку всех необходимых флагов прописать.
Void Weaver Это для имитации снаряда мумии-радаманта из Д2? Может, поступить проще - снаряд долетает до первого трупака, оживляет его, следующий снаряд уже оживляет следующего и т.д. ?
Я правильно понимаю, что он оживляется (и об него можно убиться), но висит на месте?
Если да, то проблема в том, что он потерял скорость. Нужно снова его пнуть.
Не-а. Просто безобидная декорация, так-то "статичные" снаряды обычно коцают, а этот - нет. 0_о Мэйби это из-за класса ФастПрожектиль? Но пнуть всё равно попробую.
Герр Смертоносец:
Очевидно, проверять наличие трасера, как таковое. A_JumpIf(IsPointerEqual(AAPTR_TRACER,AAPTR_NULL) == TRUE,"Death2")
Зыс, ваще красотища! Соб-сно что-то такое искал в А_Функциях.
Shadowman Не совсем. Во-первых, это для анхоли болта - он хилит нежить и коцает живых, соб-сно я именно такой и скрафтил, но мне ужасно захотелось сделать его пирсящим (шутер жи); а во-вторых, мумии воскрешают нежить вообще без снарядов. Кстати именно такое воскрешение я уже написал - на дистанции ~1000 и только нежить.
Добавлено спустя 6 часов 41 минуту 18 секунд:
Хз что я делаю не так но снаряд упорно не желает ресаться и ведёт себя просто адово:
var int user_VelX;
var int user_VelY;
var int user_VelZ;
States
{
Spawn: //Разово задаём юзервары скоростей
TNT1 A 0 A_SetUserVar ("user_VelX",VelX)
TNT1 A 0 A_SetUserVar ("user_VelY",VelY)
TNT1 A 0 A_SetUserVar ("user_VelZ",VelZ)
Spawn2:
TNT1 A 0 A_ChangeFlag ("MISSILE",1) //Возвращаем флаг
TNT1 A 0 A_ChangeVelocity(user_VelX,user_VelY,user_VelZ, CVF_REPLACE,AAPTR_DEFAULT) //Каждый раз ставим сохранённые скорости
Spawn3:
BAL1 AB 1 //Импбол спрайты для удобства тестирования
Loop
Кидает его в каких-то дичайших направлениях, дамажит в кашу даже без попадания, снаряды таки не исчезают при столкновении с шутэбл акторами, летит с рандомными скоростями.
Void Weaver Хрен его знает, срсли. Навскидку могу выдумать несколько причин разной степени фантастичности:
- Либо дело в однотиковых задержках в нужном месте.
- Либо дело с какими-то недокументированными отличиями Spawn от See.
- Либо, если прописывалось в зскрипте, при его парсинге есть какие-то неочевидные отличия от декорейта.
- Либо в поздних версиях гоззы что-то намудрили, и метод перестал работать.
- Либо какие-то неочевидные эффекты из-за немгновенного включения A_NoBlocking у монстра, которые нейтрализовываются особенностями у меня скрипта и не были воспроизведены с кондачка.
Из того, что вижу в лоб: перед запоминанием скорости отсутствует буферный стейт ненулевой длительности, из-за которого нулевые стейты пропускаются или неадекватно работают. Нужен либо буферный стейт длительностью 1, либо особое приглашение "NoDelay", которое в некоторых случаях может глючить.
Собственно, оригинальный код моего снаряда и скрипта, взаимодействующего с трасером. Предлагаю начать с него.
Скрытый текст:
ACTOR Int_AP_shell : FastProjectile //ракета-риппер, которая пробивает количество врагов в зависимости от их суммарного здоровья.
{
Radius 8
Height 10
Speed 5 //реально 100
Damage (0)
Scale 0.65
Projectile
+ROCKETTRAIL
+HITTRACER
+THRUGHOST
SeeSound "cannonshell/fly"
var int user_shellhealth;
var int user_VelX;
var int user_VelY;
var int user_VelZ;
States
{
Spawn:
MISS F 1 Bright
MISS F 0 A_SetUserVar("user_shellhealth",random(1000,1200)) //Full damage potential of the shell
MISS F 0 A_ScaleVelocity(20) //до 100
TNT1 A 0 A_ChangeFlag("THRUGHOST",0)
goto Spawn2
Spawn2:
MISS F 0 A_SetUserVar("user_VelX",VelX)
MISS F 0 A_SetUserVar("user_VelY",VelY)
MISS F 0 A_SetUserVar("user_VelZ",VelZ)
goto See
See:
MISS F 1 Bright
Loop
Death:
MISS F 1
MISS F 0 ACS_NamedExecuteAlways("Shell_penetraion") //Shell health - tracer health
MISS F 0 A_JumpIf(user_shellhealth == 0, "Death2")
MISS F 0 A_JumpIf(IsPointerEqual(AAPTR_TRACER,AAPTR_NULL) == TRUE,"Death2")
MISS F 0 A_ChangeVelocity(user_VelX, user_VelY, user_VelZ, CVF_REPLACE)
MISS F 0 A_ChangeFlag ("MISSILE",1)
goto See
Death2:
MISS F 0 A_CustomMissile("Shell88mmExplosion", 0, 0, 0)
MISS F 0 A_Explode(200,140) //damage,radius
Stop
}
}
script "Shell_penetraion" (void)
{
int H = GetUserVariable(0,"user_shellhealth") ;
int Dam = H;
int NTID = UniqueTID(1600,2600);
Thing_ChangeTID(0,NTID);
SetActivator(0,AAPTR_TRACER);
int P = GetActorProperty(0,APROP_Health); //запишем здоровье цели
if(P<0) P=0;
if(P == 0) //если попали в нечто, не обладающее здоровьем
{
H=0;
SetUserVariable(NTID,"user_shellhealth",0);
Thing_ChangeTID(NTID,0);
terminate;
}
H = H-P;
if(H<0) H=0;
SetUserVariable(NTID,"user_shellhealth",H);
Thing_Damage2(0,Dam,"Extreme");
SetActivator(NTID);
Thing_ChangeTID(NTID,0);
SetPointer(AAPTR_TRACER,0);
//PrintBold(s:"Shellhealth is ",d:H); //для отладки
}
Герр Смертоносец, премного благодарствую; внёс тебя в кредисты! ^_^
Разбор полётов:
Скрытый текст:
Void Weaver:
Кидает его в каких-то дичайших направлениях, дамажит в кашу даже без попадания, снаряды таки не исчезают при столкновении с шутэбл акторами, летит с рандомными скоростями.
1. Скорость и направления:
Проблема была в том что у меня A_ChangeFlag и находился не в том стэйте, а A_SetUserVar выполнялась только единожды.
2. Вездесущий увеличивающийся непрерывный урон:
Поскольку соурс дамага A_DamageTracer, то при попадании по ЛЮБОМУ актору с навешиванием HITTRACER приводило к тому что ВСЕ последующие акторы, помеченные HITTRACERом лавинообразно передавали новые порции урона! Вылечил добавлением A_RearrangePointers (AAPTR_DEFAULT,AAPTR_DEFAULT,AAPTR_NULL).
3. Возникновение статичных "декоративных" снарядов:
Ломал голову дольше всего (вероятно не допёр бы без твоего кода). Причиной являлся какой-то странный лимит стартовой скорости снаряда - при базовых значениях скорости выше 8, снаряд необратимо "умирал" на первом тике, если сталкивался с ЛЮБЫМ актором. Пофиксил понижением базового значения скорости и добавлением A_ScaleVelocity. До сих пор не понимаю с чем связана нестабильность на первом тике... 0_0
Буферные кадры на удивление нигде не пригодились... НО!
В моём случае добавление 1 тика в начало Death стэйта превращает "РИППЕР" снаряд в... "ДРИЛЛЕР"!
Т. е. вместо того чтобы единожды прошить все акторы на траектории, снаряд внезапно упирается в первый встреченный на пути актор и буквально "сверлит" его до тех пор пока оный не сдохнет или не будет отброшен в сторону от вектора движения, затем продолжает движение на тех же условиях. Получается на порядки убойнее обычного "РИППЕР" варианта - если зажмёт в угол то это пистец любой цели даже при дамаге в районе 2-3.
Объясните, пожалуйста, как можно реализовать снаряд, которым можно управлять от "первого лица" самого снаряда(через ACS или ZSCRIPT)? Как в игре PO'ed?
Объясните, пожалуйста, как можно реализовать снаряд, которым можно управлять от "первого лица" самого снаряда(через ACS или ZSCRIPT)? Как в игре PO'ed?
Сам снаряд невидимый, на него вешается камера с проверкой координат и обновлением их в реал времени (с задержкой 1 неверно лучше). Активатору скрипта (то есть игроку) выдаётся PROP_TOTALLYFROZEN и его клавиши движения регятся скриптом через getplayerinput. Как будет действовать на снаряд движения игрока - это реашать уже не мне. Можно сделать поворотом, можно линейно отклонять. Дабы не дать полный контроль. Ну кнопку уничтожения снаряда и проверку на существование тоже надо не забыть. Можно влепить переменные в стейты spawn и death. Всё должно быть в цикле.
Пример есть в Russian Overkill, там фиолетовая снайперка (пардон, вспомнил, там просто летящий снаряд, который потом стреляет по команде игрока). Правда возможно понадобится декомпилить АЦС.
Сам снаряд невидимый, на него вешается камера с проверкой координат и обновлением их в реал времени (с задержкой 1 неверно лучше). Активатору скрипта (то есть игроку) выдаётся PROP_TOTALLYFROZEN и его клавиши движения регятся скриптом через getplayerinput. Как будет действовать на снаряд движения игрока - это реашать уже не мне. Можно сделать поворотом, можно линейно отклонять. Дабы не дать полный контроль. Ну кнопку уничтожения снаряда и проверку на существование тоже надо не забыть. Можно влепить переменные в стейты spawn и death. Всё должно быть в цикле.
Пример есть в Russian Overkill, там фиолетовая снайперка (пардон, вспомнил, там просто летящий снаряд, который потом стреляет по команде игрока). Правда возможно понадобится декомпилить АЦС.
Прикрутить камеру к снаряду я уже смог и без Russian Overkill и DoomStorm. Пытался разобрать код управления из Russian Overkill и DoomStorm - ничерта не понял . Видимо, из-за того, что с ACS я очень редко работаю . Можешь объяснить, как там работает код управления? Очень хочу разобраться и узнать что-то новое.
Можешь объяснить, как там работает код управления?
К сожалению, я не декомпилил ACS ни в RO, ни в DoomStorm, увы, поэтому кода не видел. Предположил механику, которую изложил выше. GetPlayerInput => SetActorAngle, SetActorVelocity, SetActorPitch и.т.д. Клавиши можно взять от управления игрока BT_FORWARD, BT_BACK, BT_MOVELEFT, BT_MOVERIGHT. Все команды и переменные к ним есть на Вики. В реальном времени делается камера и привязываются к ней координаты снаряда через ChangeCamera. Внимание стоит уделить проверкам на существование.
Другая проблема. Я решил создать для снаряда кнопку самоуничтожения. Думал, что это также просто, как поссать сходить. Но и это не работает как надо. При нажатии кнопки, выводится лишь сообщение.
nailzx128 Не уверен, но может проблема в том, что обращение идёт к 0 tid, так как в примере из Uniquetid последняя строчка отвечает за зануление. Попробуй сделать проверку на кнопку до thing_changetid(missleid, 0).
Вообще наверно надо впихнуть в проверку на стейт смену камеры, потому что после взрыва, камера движется ещё какое-то время.
Кстати, интересная фишка. В проверке на стейт, отключение камеры работает! Так-же как и сообщение. Но не проверка стейта. Не могу понять, что я делаю не так...
Т. е. вместо того чтобы единожды прошить все акторы на траектории, снаряд внезапно упирается в первый встреченный на пути актор и буквально "сверлит" его до тех пор пока оный не сдохнет или не будет отброшен в сторону от вектора движения, затем продолжает движение на тех же условиях. Получается на порядки убойнее обычного "РИППЕР" варианта - если зажмёт в угол то это пистец любой цели даже при дамаге в районе 2-3.
Потому что сквозное пролетание надо прописывать отдельно.
Мой снаряд от 88 флака пролетает насквозь только если дамагнул актора насмерть. (Чтобы пробить, например, 10 импов и застрять в десятом.) Типа реализм. Подохший актор в любом случае теряет коллижен, поэтому снаряд автоматически летит дальше.
В твоём случае снаряд будет стремиться пролететь дальше, пока не "засверлит" актора насмерть. Чтобы этого не происходило, нужно:
- Либо после столкновения временно включать снаряду флаг +RIPPER и после короткой задержки отключать. (Просто, но может заглючить в плотной толпе монстров разной толщины.)
- Либо, имея скриптовое сношение снаряда с трасером, каким-либо способом установить прозрачность коллижена именно этого актора для именно этого снаряда.
Во втором случае есть несколько путей. Я предполагаю работу с CheckActorProperty/GetActorProperty/SetActorProperty по параметру APROP_Species с назначением обоим рандомно сгенерированной строки при наличии флага +THRUSPECIES. При этом нужно прописать исключение на тот случай, если у монстра уже есть Species - в этом случае снаряд должен брать параметр с монстра.
(Species - редко используемая фича, позволяющая запретить монстрам с одинаковым именем группы драться между собой.)
Всё работает как часы - снаряд пирсит, хилит одних и коцает других, спаунится и убивается в рамках поставленной задачи.
Просто при добавлении буферного тика в стэйт, происходит "глубокое бурение", т. е. это не баг а фича, имхо:
Death:
//TNT1 A 1 //DRILLER MISSILE!!! :0 //Толкает цели по вектору движения, постоянно нанося урон; продолжает полёт если путь свободен!
TNT1 A 0 A_JumpIf(IsPointerEqual(AAPTR_TRACER,AAPTR_NULL) == TRUE,"Death2")
TNT1 A 0 A_GiveInventory ("UndeadCure",1,AAPTR_TRACER)
TNT1 A 0 A_DamageTracer (random(26,66),"None",DMSS_AFFECTARMOR|DMSS_EXSPECIES|DMSS_INFLICTORDMGTYPE,"None","Undead",AAPTR_TARGET)
TNT1 A 0 A_ChangeVelocity(user_VelX,user_VelY,user_VelZ, CVF_REPLACE)
TNT1 A 0 A_ChangeFlag ("MISSILE",1) //Missile "revive"
Goto Spawn
Убираешь кадр - и снова классический РИППЕР. Я из этой хрени мобильную "чёрную дыру" сделал - дописываешь в Spawn2 (где цикл анимации) A_RadiusThrust(-Х) и вообще мясорубка получается.
Всё ок, ещё раз спасибо.
Другая проблема. Я решил создать для снаряда кнопку самоуничтожения. Думал, что это также просто, как поссать сходить. Но и это не работает как надо. При нажатии кнопки, выводится лишь сообщение.
Итак, ЧЯДНТ?
У тебя на картинке CheckActorState.. Наверное ты думаешь что acs работает как декорейт?
bool CheckActorState (int tid, str statename [, bool exact]);
оно вернет true или false если это засунуть сюда
if(CheckActorState (int tid, "str statename") )// Если такой стейт есть
do something;
else// если нет
do something;
Что бы сделать управляемый снаряд, в подробности вдаваться не хочу, но в кратце вижу это как-то так:
Script "Missile_Control" (void)//Запускает снаряд
{
int MissileID = UniqueTID(); // Даем переменной хз какой тид
Thing_ChangeTID(0, MissileID); // Задаем снаряду тид выбранный строкой выше
SetActivatorToTarget(MissileID); // Делаем активатором скрипта игрока
ChangeCamera(MissileID, 0, 0); // т.к. тид игрока теперь известен, меняем ГГ камеру на снаряд
SetPlayerProperty(0, ON, PROP_Frozen); // морозим ГГ, что бы не двигался
///Thing_ChangeTID(MissileID, 0); // убираем тид ставим активатором скрипта опять снаряд.
int but = GetPlayerInput(-1, INPUT_BUTTONS);
while (GetActorProperty(MissileID, APROP_Health) > 0)//цикл работает пока снаряд живой
{
if(but == BT_ATTACK)//если нажать на атаку, скрипт прервется
{
PrintBold(s:"abort missile control");
///тут уже меняешь камеру ГГ обратно, размораживаешь его и что там еще надо делаешь.
Terminate;
}
if(but == BT_LEFT)
{
SetActorVelocity (MissileID, 15.0, GetActorVelY(MissileID), GetActorVelZ(MissileID), false, false);//Если нажал влево, заменяем скорость снаряда на эту
}
if(but == BT_RIGHT)
{
SetActorVelocity (MissileID, -15.0, GetActorVelY(MissileID), GetActorVelZ(MissileID), false, false);//Если нажал вправо, заменяем скорость снаряда на эту
}
PrintBold(s:"Control work");
Delay(1);
}
}