Проблема в том, что монстры созданные через копирование : кода от других монстров не сражаются между собой и не могут друг друга убить если их натравить друг на друга(снарядами не могут, но огнестрелом могут убить). Сей час попробую твой вариант сделать, но без использования : копирования настроек существующих монстров. Мне нужно чтобы кошмарные дрались с простыми монстрами, если те заденут других и наоборот.
Чтобы был инфайт, добавь следующие строки в свойства актора:
Species "None"
+DOHARMSPECIES
+HARMFRIENDS
+FORCEINFIGHTING
-DONTHARMSPECIES
-NOINFIGHTSPECIES
-NOINFIGHTING
-NOTARGET
-NEVERTARGET
theleo_ua https://yadi.sk/d/LJr7KXAi3MwEFg Всё проверил, вроде работает как надо и даже огонь сам починился не понял как. И флаги дополнительные не понадобились.
Но обычный арч либо вообще не воскрешает импов либо только одного и один раз всего. По ссылке микро вад, выдрал только самое необходимое, декорейт(конкретно с арчами и импами) и спрайты чтобы уже на конечной балванке проверить.
пс: воскрешение твоё пока что только на импах прописано, на других монстрах не проверяй.
Скрытый текст:
Actor HOTDImp : DoomImp Replaces DoomImp
{ States
{ Death:
TROO I 8 A_SetScale(ScaleX * RandomPick(-1.0, 1.0), ScaleY)
TROO J 8 A_Scream
TROO K 6
TROO L 6 A_NoBlocking
Goto DeathCheck
DeathCheck:
TNT1 A 0 A_JumpIfInventory("BLACK_Vile", 1, "DeathWithoutRaise")
TNT1 A 0 A_JumpIfInventory("NORMAL_Vile", 1, "DeathWithRaise")
TROO M 1
Loop
DeathWithRaise:
TNT1 A 0 A_TakeInventory("NORMAL_Vile", 1)
TNT1 A 0 A_TakeInventory("BLACK_Vile", 1)
TROO M 1 CanRaise
Goto DeathCheck
DeathWithoutRaise:
TNT1 A 0 A_TakeInventory("NORMAL_Vile", 1)
TNT1 A 0 A_TakeInventory("BLACK_Vile", 1)
TROO M 1
Goto DeathCheck
XDeath:
TROO N 5
TROO O 5 A_XScream
TROO P 5
TROO Q 5 A_NoBlocking
TROO RST 5
Goto XDeathLoop
XDeathLoop:
TROO U 1
Loop
Raise:
TROO MLKJI 8
Goto See
Crush:
POL5 A -1 A_PlaySound ("Misc/Gibbed")
stop
}
}
Actor HOTDArchvile : Archvile Replaces Archvile
{ States
{ See:
VILE A 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE A 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE A 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE A 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE B 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE B 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE B 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE B 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE C 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE C 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE C 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE C 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE D 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE D 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE D 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE D 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE E 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE E 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE E 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE E 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE F 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE F 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE F 1 A_VileChase
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE F 1
TNT1 A 0 A_RadiusGive("NORMAL_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
Loop
Death:
VILE Q 7 A_SetScale(ScaleX * RandomPick(-1.0, 1.0), ScaleY)
VILE R 7 A_Scream
VILE S 7 A_NoBlocking
VILE TUVWXY 7
VILE Z -1
Stop
Crush:
POL5 A -1 A_PlaySound ("Misc/Gibbed")
stop
}
}
Я так понял в XDeath надо убрать -1 так как здесь пишут что By default, only states with a -1 (infinite) duration are eligible.
Кстати, вопрос в зрительный зал: если вышеописанный метод с CanRaise пашет, может имеет смысл сделать так:
Я над таким способом работал уже два дня, но ты меня опередил. Жаль: хотел выдать уже готовый продукт. Обидно...
(Идею подсказал мне Crazy Eagle).
По коду у меня всё в разы проще:
Скрытый текст:
В ZDoom'е не работает; мешает A_CheckProximity. Однако в G- и Q- ZDoom'ах всё в порядке.
Actor NmZombie: Zombieman 6554 // Взял свой DoomEdNum, не из проекта.
{ // Тестировал новые функции на этом акторе, не на "StZombie".
States
{
Death:
POSS H 5
POSS I 5 A_Scream
POSS J 0 A_UnsetShootable
POSS J 5 A_UnsetSolid
POSS K 5
Death.Wait:
POSS L 1 A_CheckProximity( "Death.HelpMe", "NmArch", 56 )
Loop
Death.HelpMe:
// POSS L 0 A_CheckProximity( "Death"/*‼!‼*/, "Actor", 48 , 1, CPXF_ANCESTOR )
POSS L 0 A_CheckProximity( "Death.Wait", "Actor", 48 , 1, CPXF_ANCESTOR )
// Строчка выше проверяет, возможно ли встать, ничего ли не блокирует воскрешение?
// Не работает: ошибается, если на трупе лежит, например, "Clip". Или другой труп.
// В этой строчке, конкретно, в "CPXF_ANCESTOR", спасибо UsernameAK.
POSS L -1 A_Fall
Stop
}
}
Actor StZombie: Zombieman 6553
{
States
{
Death:
POSS H 5
POSS I 5 A_Scream
POSS J 0 A_UnsetShootable
POSS J 5 A_UnsetSolid
POSS K 5
Death.Wait:
POSS L 1 A_CheckProximity( "Death.HelpMe", "Archvile", 56 )
Loop
Death.HelpMe:
POSS L 0 A_Fall
POSS L -1
Stop
}
}
Застопорился на проверке возможности воскрешения. А так всё работает правильно -- обычный воскрешает только обычных, кошмарный -- своих. Если с цифрами поиграть, то шанса воскрешения не того вообще не будет.
* * *
Вообще ещё думал над таким способом проверки "а кто меня воскрешает":
1) Арч всё время создаёт актор, который проверяет трупы вокруг;
2) Когда находит, "таргетится" на него (устанавливает свой AAPTR_TARGET);
3) Через A_TransferPointer передаёт арчу свой target-указатель как на tracer'а (например).
После этого через указатели вполне легко можно проверить, что за зверь мой воскрешатель и кого он поднимает.
Вообще ещё думал над таким способом проверки "а кто меня воскрешает":
1) Арч всё время создаёт актор, который проверяет трупы вокруг;
2) Когда находит, "таргетится" на него (устанавливает свой AAPTR_TARGET);
3) Через A_TransferPointer передаёт арчу свой target-указатель как на tracer'а (например).
После этого через указатели вполне легко можно проверить, что за зверь мой воскрешатель и кого он поднимает.
Идея неплоха, но надо проверять на практике, там думаю много подводных камней будет, например если много арчей и много трупов (вот как у пытавшихся выше арч воскрешал сразу нескольких трупов одновременно, или например арч подойдет и сразу все трупы по очереди будет воскрешать без остановки, так ни разу и не отбежав)
Да, ты прав, здесь нужен "Death.Wait". Моя ошибка, исправил.
═════════════════════════
theleo_ua:
надо проверять на практике, там думаю много подводных камней будет
Этот способ я рассматривал как самый крайний случай. Вижу в нём ошибки, которые проще всего решать "костылями", что явно неправильно.
═════════════════════════
theleo_ua:
А зачем она [проверка] нужна? В оригинале разве проверяется?
Проверка на блокировку каким-либо плотным монстром текущих координат. Я же там даже комментарий в коде написал:
JSO x:
// Строчка выше проверяет, возможно ли встать, ничего ли не блокирует воскрешение?
Правда, нужна какая-то другая функция, эта не работает так, как нужно. Возможно, подойдёт A_CheckLOF с атрибутом Range, равным ( Radius * 2 ).
Зачем эта проверка нужна в принципе: если кошмарный арчвайл пробежит рядом, а по трупу будут ходить, то труп перестанет прикидываться мёртвым и умрёт на самом деле; однако его не оживят -- мешают, стоят над ним. А чуть позже рядом обычный арч пробегает. И всё... Сможет спокойно оживить не того.
Зачем эта проверка нужна в принципе: если кошмарный арчвайл пробежит рядом, а по трупу будут ходить, то труп перестанет прикидываться мёртвым и умрёт на самом деле; однако его не оживят -- мешают, стоят над ним. А чуть позже рядом обычный арч пробегает. И всё... Сможет спокойно оживить не того.
Понял, спасибо. Пересмотрел еще раз твой код - т.е. идея такая, что если задетекчен арч вблизи, то прыгаем на фрейм, который -1
Возникает вопрос: а если арч (в этот самый момент, когда проверка на арча на нужном расстоянии прошла успешно) захочет проигнорить такой труп? Либо если арча убьют в этот момент. Мне кажется в таком коде арчи должны иногда воскрешать не своих
Если я не ошибаюсь, стандартный vilechase арча прдразумевает иногда игнор трупа даже если труп на нужном расстоянии и ничто не блокирует воскрешение (возможно те кто смотрели исходники vilechase прояснят этот момент)
Возникает вопрос: а если арч (в этот самый момент, когда проверка на арча на нужном расстоянии прошла успешно) захочет проигнорить такой труп? Либо если арча убьют в этот момент.
Я просто не нашёл функции, как "оживить" монстра так, чтобы он остался лежать и продолжал быть неплотным и неподстреливаемым, а также чтобы из него потом не выпадали лишние DropItem'ы. Различные A_Respawn и A_ResetHealth, которые я смог найти, для такого не подходят. Поэтому проверки на уход арча из радиуса воскрешения я пока сделать не смог, хоть и пытался.
* * *
Сейчас попытаюсь реализовать одну идею, должно заработать.
Поэтому проверки на уход арча из радиуса воскрешения я пока сделать не смог, хоть и пытался.
Ну одно из решений это сделать как у меня:
если арч рядом, то POSS L 1 CanRaise
если арч не рядом, то POSS L 1
Проблема разве что будет, если 2 разных арча окажутся рядом по разные стороны (хотя вероятность такого резиста крайне мала), но здесь опять же можно сделать как у меня: дополнительную проверку, что рядом есть оба арча одновременно (но это удлинит код, да)
Добавлено спустя 11 минут 1 секунду:
SSV_Victoryan:
Но обычный арч либо вообще не воскрешает импов либо только одного и один раз всего.
Поэтому, внимательно глянь декорейт код под спойлером по этой ссылке и сделай у себя по аналогии так же, т.е. скопируй себе полностью все состояния оригинального импа (порядок важен), а те, которые ты хочешь переопределить, замени новым кодом (точно так же сделай и с арчем). Если возникают непонятки что и как, смотри мой код арча и зомбимена как пример
З.Ы. И все равно вопрос в зрительный зал остается открытым: есть ли способ избежать такой проблемы, чтобы не копипастить весь код? Есть ли аналогичная проблема в зскрипте когда наследуешь актора?
VILE A 1 A_VileChase
TNT1 A 0 A_RadiusGive("BLACK_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE A 1
TNT1 A 0 A_RadiusGive("BLACK_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE A 1 A_VileChase
TNT1 A 0 A_RadiusGive("BLACK_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
VILE A 1
TNT1 A 0 A_RadiusGive("BLACK_Vile", 130, RGF_CORPSES | RGF_KILLED | RGF_NOSIGHT, 1)
, а также от циклов с ссылками на ссылку вызова:
theleo_ua:
DeathCheck:
TNT1 A 0 A_JumpIfInventory("BLACK_Vile", 1, "DeathWithoutRaise")
TNT1 A 0 A_JumpIfInventory("NORMAL_Vile", 1, "DeathWithRaise")
POSS L 1
Loop
DeathWithRaise:
TNT1 A 0 A_TakeInventory("NORMAL_Vile", 1)
TNT1 A 0 A_TakeInventory("BLACK_Vile", 1)
POSS L 1 CanRaise
Goto DeathCheck
DeathWithoutRaise:
TNT1 A 0 A_TakeInventory("NORMAL_Vile", 1)
TNT1 A 0 A_TakeInventory("BLACK_Vile", 1)
POSS L 1
Goto DeathCheck
Это всё проверяется постоянно, и хоть производительность от такого не особо сильно страдает (ZDoom написан хорошо), но лучше так не делать.
для того, чтобы твой переопределенный стейт SEE работал корректно (например если в Raise было написано Goto See чтобы оно прыгало именно на твой переопределенный See), то надо весь декорейт арча/зомбимена копипастить в твоего переопределенного
Пропиши вместо "Goto See" - A_jump(256, "See") и не нужно лишнего
Добавлено спустя 4 минуты:
theleo_ua:
И все равно вопрос в зрительный зал остается открытым: есть ли способ избежать такой проблемы, чтобы не копипастить весь код? Есть ли аналогичная проблема в зскрипте когда наследуешь актора?
Это не проблема - это особенность. И ею можно пользоваться в своих целях. НО только JUMP-ы дают нужное тебе (причем какой именно JUMP не имеет значения, главное чтобы был JUMP)...
Добавлено спустя 1 минуту 56 секунд:
Т.е. код например выглядит так:
Melee:
PALA GHIJ 3 A_FaceTarget
PALA K 0 A_PlaySound("MONSTERS/ZombieAxe/fighterhammermiss")
PALA K 3 A_CustomMeleeAttack(random(1,8)*6,"FighterAxeHitThing","none")
PALA LG 3
PALA G 0 A_JUMP(256, "See")
Stop
Пропиши вместо "Goto See" - A_jump(256, "See") и не нужно лишнего
Проблема в том, что этот Goto See прописан в старом коде (например в стейте raise), и чтобы вместо него написать A_jump(256, "See"), надо скопипастить весь этот стейт raise. Т.е. мне надо проштудировать всего актора, понаходить там все goto на переопределенные стейты, покопипастить все эти стейты в которых были эти goto и там уже менять goto на jump
Причем смена goto на jump уже как бы будет неактуальна, так как банального факта копипасты уже будет достаточно чтобы работало как надо (за редким исключением, когда там какая-то хитровжаренная логика стейтов)
YURA_111:
Это не проблема - это особенность. И ею можно пользоваться в своих целях.
А можно как-то эту особенность отключить? Вот например я переопределил see и не хочу копипастить raise (чтобы в raise менять goto на jump) - есть способ сделать это без копипасты raise в нового актора?
Также интересует, как с этой особенностью обстоят дела в зскрипте
Actor NightmareZombie: Zombieman 6554
{
States
{
Death:
POSS H 5
POSS I 5 A_Scream
POSS J 0 A_DropItem( "Clip" )
POSS J 0 A_UnsetShootable
POSS J 5 A_UnsetSolid
POSS K 5
Death.Wait:
POSS L 1
POSS L 0 A_CheckProximity( "Death.HelpMe", "NightmareArch", 64 )
Loop
Death.HelpMe:
POSS L 1 CanRaise A_CheckProximity( "Death.Wait", "Archvile", 64 )
POSS L 0 CanRaise A_CheckProximity( "Death.HelpMe", "NightmareArch", 64 )
Goto Death.Wait
}
}
Actor StandartZombie: Zombieman 6553
{
States
{
Death:
POSS H 5
POSS I 5 A_Scream
POSS J 0 A_DropItem( "Clip" )
POSS J 0 A_UnsetShootable
POSS J 5 A_UnsetSolid
POSS K 5
Death.Wait:
POSS L 1
POSS L 0 A_CheckProximity( "Death.HelpMe", "Archvile", 64 )
Loop
Death.HelpMe:
POSS L 1 CanRaise A_CheckProximity( "Death.Wait", "NightmareArch", 64 )
POSS L 0 CanRaise A_CheckProximity( "Death.HelpMe", "Archvile", 64 )
Goto Death.Wait
}
}
Всё. В арчвайлах больше ничего прописывать не нужно.
http://my-files.ru/dy6xnp -- пример, как оживляют арчвайлы монстров только своего типа. Делал новых акторов, с палитрой "кошмарных" не заморачивался, поэтому вид у них... Непрезентабельный.
Красная кнопка в каждой комнате -- убить всех зомби, кнопка-глаз -- разозлить арчвайлов на импа в центре комнаты.
POSS L 1 CanRaise A_CheckProximity( "Death.Wait", "Archvile", 64 )
Я правильно понял, если на этой строке рядом окажется неправильный арч и затриггерится на CanRaise, то вместо джампа на Death.Wait зомбимен джампнется на raise ? В гздуме приоритет на арча или на A_CheckProximity ?
Всё. В арчвайлах больше ничего прописывать не нужно.
http://my-files.ru/dy6xnp -- пример, как оживляют арчвайлы монстров только своего типа. Делал новых акторов, с палитрой "кошмарных" не заморачивался, поэтому вид у них... Непрезентабельный.
Красная кнопка в каждой комнате -- убить всех зомби, кнопка-глаз -- разозлить арчвайлов на импа в центре комнаты.
Что за файл в ваде? NE_DECOR
кАрч оживляет зомбарей обоих типов, а не только кЗомби.
кАрч оживляет зомбарей обоих типов даже если их трубы разорваны на куски(Xdeath), такого не должно быть.
кАрч оживляет зомбарей обоих типов даже если их трубы разорваны на куски, такого не должно быть.
Должен. Я только что проверил.
Если не нужно -- добавляешь к каждому "XDeath: <оригинальные стейты>", на последнем вместо "-1" -- положительное число. И "Wait" в конце.
XDeath:
<...>
POSS U 900
Wait
SSV_Victoryan:
Что за файл в ваде? NE_DECOR
Альтернативный вариант проверки воскрешения. Недоделан, как следствие не нужен.
theleo_ua:
зачем вместо
POSS J 5 A_NoBlocking
ты пишешь
[какую-то фигню]?
С предыдущего раза ещё осталось -- ведь арчи они и на A_Fall/A_NoBlocking тоже по-своему должны реагировать; я решил перестраховаться.
theleo_ua:
Я правильно понял, если на этой строке рядом окажется неправильный арч и затриггерится на CanRaise, то вместо джампа на Death.Wait зомбимен джампнется на raise ? В гздуме приоритет на арча или на A_CheckProximity ?
Проверка, есть ли рядом арч не своего типа, если есть, то отменяем возможность воскрешения.
Вначале идёт действие, затем -- проверка на CanRaise, насколько я знаю.
Проверка, есть ли рядом арч не своего типа, если есть, то отменяем возможность воскрешения.
Вначале идёт действие, затем -- проверка на CanRaise, насколько я знаю.
Я вот к чему спросил: получается у нас сначала идет check на арча (допустим арч есть и check решил джампнуть на Death.Wait), но так как в строке check у нас стоит 1, то после check-a движок гздума подождет 1 тик
Теперь вспоминаем, что этот 1 тик у нас с припиской CanRaise, и следовательно даже после успешного джампа на Death.Wait у нас зомбимен простоит 1 тик в CanRaise, и даст возможность неправильному арчу его воскресить?
Если дело так обстоит, то лучше переделать сейчас:
Death.HelpMe:
POSS L 0 A_CheckProximity( "Death.Wait", "Archvile", 68 ) // Почему "68"? Предупреждение подхода не того арчвайла за один тик.
POSS L 1 CanRaise
POSS L 0 CanRaise A_CheckProximity( "Death.HelpMe", "NightmareArch", 64 )
Goto Death.Wait
Однако все jump'ы делаются непосредственно после того, как они будут выполнены.