Одержимый зомби: оружие на поясе по ремеслу (#37), карта по M (#36)

- Пояс - нижний ряд сумки зомби; оружие ближнего боя в руку, если этот тип зомби
  им владеет (ZombieWeapons: таблица умений, утверждена 25.10). Удар как у персонажа:
  ItemActionAttack.Hit, урон предмета без перков зомби, износ, темп оружия.
  Модель на кость RightWeapon, хват WeaponHoldType; иконки и износ на поясе HUD.
  Неумелое или сломанное - кулаки и подсказка (13 языков). Выключатель
  necroarms weapon on|off; dev-команда necropossess для проверок без мыши.
- M в зомби и волке открывал сумку вместо карты (перехват windowpaging) - исправлено;
  туман карты и карта на одержимом - у любого одержимого зомби, как у стервятника.
- Глава «Одержимость» (EN/RU): пояс - оружие, M - карта.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-25 14:26:55 +03:00
co-authored by Claude Opus 5.5
parent 5ebd09dacd
commit cf61d98bb7
8 changed files with 626 additions and 18 deletions
+4 -2
View File
@@ -356,7 +356,7 @@ necroPossessHintWolfBag,items,Item,,,,"E at a container or loot bag - loot into
necroPossessSearchBag,items,Item,,,,{0} Search: {1},,{0} Durchsuchen: {1},{0} Registrar: {1},{0} Fouiller : {1},{0} Perquisisci: {1},{0} 調べる: {1},{0} 뒤지기: {1},{0} Przeszukaj: {1},{0} Vasculhar: {1},{0} Обыскать: {1},{0} Ara: {1},{0} 搜索:{1},{0} 搜索:{1} necroPossessSearchBag,items,Item,,,,{0} Search: {1},,{0} Durchsuchen: {1},{0} Registrar: {1},{0} Fouiller : {1},{0} Perquisisci: {1},{0} 調べる: {1},{0} 뒤지기: {1},{0} Przeszukaj: {1},{0} Vasculhar: {1},{0} Обыскать: {1},{0} Ara: {1},{0} 搜索:{1},{0} 搜索:{1}
necroWolfBagWindowTitle,items,Item,,,,Wolf bag,,Wolfstasche,Bolsa del lobo,Sac du loup,Borsa del lupo,狼の袋,늑대 가방,Torba wilka,Bolsa do lobo,Сумка волка,Kurt çantası,狼的包,狼的包 necroWolfBagWindowTitle,items,Item,,,,Wolf bag,,Wolfstasche,Bolsa del lobo,Sac du loup,Borsa del lupo,狼の袋,늑대 가방,Torba wilka,Bolsa do lobo,Сумка волка,Kurt çantası,狼的包,狼的包
necroWolfBagWaypoint,items,Item,,,,Wolf bag,,Wolfstasche,Bolsa del lobo,Sac du loup,Borsa del lupo,狼の袋,늑대 가방,Torba wilka,Bolsa do lobo,Сумка волка,Kurt çantası,狼的包,狼的包 necroWolfBagWaypoint,items,Item,,,,Wolf bag,,Wolfstasche,Bolsa del lobo,Sac du loup,Borsa del lupo,狼の袋,늑대 가방,Torba wilka,Bolsa do lobo,Сумка волка,Kurt çantası,狼的包,狼的包
necroZombieBagWindowTitle,items,Item,,,,Zombie bag,,Zombietasche,Bolsa del zombi,Sac du zombie,Borsa dello zombi,ゾンビの袋,좀비 가방,Torba zombie,Bolsa do zumbi,Сумка зомби,Zombi çantası,僵尸的包,殭屍的包 necroZombieBagWindowTitle,items,Item,,,,"Zombie bag · bottom row - belt",,"Zombietasche · untere Reihe - Gürtel","Bolsa del zombi · fila inferior: cinturón","Sac du zombie · rangée du bas : ceinture","Borsa dello zombi · fila in basso: cintura","ゾンビの袋・下の段はベルト","좀비 가방 · 아래 줄 - 벨트","Torba zombie · dolny rząd - pas","Bolsa do zumbi · fileira de baixo - cinto","Сумка зомби · нижний ряд - пояс","Zombi çantası · alt sıra - kemer","僵尸的包 · 下排是腰带","殭屍的包 · 下排是腰帶"
necroZombieBagWaypoint,items,Item,,,,Zombie bag,,Zombietasche,Bolsa del zombi,Sac du zombie,Borsa dello zombi,ゾンビの袋,좀비 가방,Torba zombie,Bolsa do zumbi,Сумка зомби,Zombi çantası,僵尸的包,殭屍的包 necroZombieBagWaypoint,items,Item,,,,Zombie bag,,Zombietasche,Bolsa del zombi,Sac du zombie,Borsa dello zombi,ゾンビの袋,좀비 가방,Torba zombie,Bolsa do zumbi,Сумка зомби,Zombi çantası,僵尸的包,殭屍的包
necroPossessHintZombieBag,items,Item,,,,"Tab - bag, E at a container, loot bag or item - into the bag",,"Tab - Tasche, E an Behälter, Beutel oder Gegenstand - in die Tasche","Tab: bolsa, E en un contenedor, saco o objeto: a la bolsa","Tab : sac, E sur un conteneur, un sac ou un objet : dans le sac","Tab - borsa, E su contenitore, sacca o oggetto - nella borsa","Tab: 袋、コンテナ・袋・アイテムにE: 袋へ","Tab - 가방, 상자·자루·물건에 E - 가방으로","Tab - torba, E na pojemniku, worku lub przedmiocie - do torby","Tab - bolsa, E em contêiner, saco ou item - para a bolsa","Tab - сумка, E на ящик, мешок или предмет - в сумку","Tab - çanta, kaba, torbaya veya eşyaya E - çantaya","Tab - 包,对容器、袋子或物品按E - 放入包","Tab - 包,對容器、袋子或物品按E - 放入包" necroPossessHintZombieBag,items,Item,,,,"Tab - bag, E at a container, loot bag or item - into the bag",,"Tab - Tasche, E an Behälter, Beutel oder Gegenstand - in die Tasche","Tab: bolsa, E en un contenedor, saco o objeto: a la bolsa","Tab : sac, E sur un conteneur, un sac ou un objet : dans le sac","Tab - borsa, E su contenitore, sacca o oggetto - nella borsa","Tab: 袋、コンテナ・袋・アイテムにE: 袋へ","Tab - 가방, 상자·자루·물건에 E - 가방으로","Tab - torba, E na pojemniku, worku lub przedmiocie - do torby","Tab - bolsa, E em contêiner, saco ou item - para a bolsa","Tab - сумка, E на ящик, мешок или предмет - в сумку","Tab - çanta, kaba, torbaya veya eşyaya E - çantaya","Tab - 包,对容器、袋子或物品按E - 放入包","Tab - 包,對容器、袋子或物品按E - 放入包"
necroPossessHintDoor,items,Item,,,,"E at a door or switch - use it",,"E an Tür oder Schalter - benutzen","E en una puerta o interruptor: usar","E sur une porte ou un interrupteur : utiliser","E su porta o interruttore - usare","ドアやスイッチにE: 使う","문이나 스위치에 E - 사용","E na drzwiach lub przełączniku - użyj","E em porta ou interruptor - usar","E на дверь или выключатель - открыть, включить","Kapıya veya şaltere E - kullan","对门或开关按E - 使用","對門或開關按E - 使用" necroPossessHintDoor,items,Item,,,,"E at a door or switch - use it",,"E an Tür oder Schalter - benutzen","E en una puerta o interruptor: usar","E sur une porte ou un interrupteur : utiliser","E su porta o interruttore - usare","ドアやスイッチにE: 使う","문이나 스위치에 E - 사용","E na drzwiach lub przełączniku - użyj","E em porta ou interruptor - usar","E на дверь или выключатель - открыть, включить","Kapıya veya şaltere E - kullan","对门或开关按E - 使用","對門或開關按E - 使用"
@@ -364,6 +364,8 @@ necroPossessTraderArea,items,Item,,,,"The trader's land does not accept the dead
necroZombieBagTook,items,Item,,,,"Zombie bag ({1}/{2}): {0}",,"Zombietasche ({1}/{2}): {0}","Bolsa del zombi ({1}/{2}): {0}","Sac du zombie ({1}/{2}) : {0}","Borsa dello zombi ({1}/{2}): {0}","ゾンビの袋({1}/{2}):{0}","좀비 가방 ({1}/{2}): {0}","Torba zombie ({1}/{2}): {0}","Bolsa do zumbi ({1}/{2}): {0}","Сумка зомби ({1}/{2}): {0}","Zombi çantası ({1}/{2}): {0}","僵尸的背包({1}/{2}):{0}","殭屍的背包({1}/{2}):{0}" necroZombieBagTook,items,Item,,,,"Zombie bag ({1}/{2}): {0}",,"Zombietasche ({1}/{2}): {0}","Bolsa del zombi ({1}/{2}): {0}","Sac du zombie ({1}/{2}) : {0}","Borsa dello zombi ({1}/{2}): {0}","ゾンビの袋({1}/{2}):{0}","좀비 가방 ({1}/{2}): {0}","Torba zombie ({1}/{2}): {0}","Bolsa do zumbi ({1}/{2}): {0}","Сумка зомби ({1}/{2}): {0}","Zombi çantası ({1}/{2}): {0}","僵尸的背包({1}/{2}):{0}","殭屍的背包({1}/{2}):{0}"
necroZombieBagFull,items,Item,,,,"The zombie bag is full ({1}/{2}): {0}",,"Die Zombietasche ist voll ({1}/{2}): {0}","La bolsa del zombi está llena ({1}/{2}): {0}","Le sac du zombie est plein ({1}/{2}) : {0}","La borsa dello zombi è piena ({1}/{2}): {0}","ゾンビの袋がいっぱい({1}/{2}):{0}","좀비 가방이 가득 찼습니다 ({1}/{2}): {0}","Torba zombie jest pełna ({1}/{2}): {0}","A bolsa do zumbi está cheia ({1}/{2}): {0}","Сумка зомби полна ({1}/{2}): {0}","Zombi çantası dolu ({1}/{2}): {0}","僵尸的背包已满({1}/{2}):{0}","殭屍的背包已滿({1}/{2}):{0}" necroZombieBagFull,items,Item,,,,"The zombie bag is full ({1}/{2}): {0}",,"Die Zombietasche ist voll ({1}/{2}): {0}","La bolsa del zombi está llena ({1}/{2}): {0}","Le sac du zombie est plein ({1}/{2}) : {0}","La borsa dello zombi è piena ({1}/{2}): {0}","ゾンビの袋がいっぱい({1}/{2}):{0}","좀비 가방이 가득 찼습니다 ({1}/{2}): {0}","Torba zombie jest pełna ({1}/{2}): {0}","A bolsa do zumbi está cheia ({1}/{2}): {0}","Сумка зомби полна ({1}/{2}): {0}","Zombi çantası dolu ({1}/{2}): {0}","僵尸的背包已满({1}/{2}):{0}","殭屍的背包已滿({1}/{2}):{0}"
necroZombieBagShow,items,Item,,,,"Zombie bag ({1}/{2}): {0}",,"Zombietasche ({1}/{2}): {0}","Bolsa del zombi ({1}/{2}): {0}","Sac du zombie ({1}/{2}) : {0}","Borsa dello zombi ({1}/{2}): {0}","ゾンビの袋({1}/{2}):{0}","좀비 가방 ({1}/{2}): {0}","Torba zombie ({1}/{2}): {0}","Bolsa do zumbi ({1}/{2}): {0}","Сумка зомби ({1}/{2}): {0}","Zombi çantası ({1}/{2}): {0}","僵尸的背包({1}/{2}):{0}","殭屍的背包({1}/{2}):{0}" necroZombieBagShow,items,Item,,,,"Zombie bag ({1}/{2}): {0}",,"Zombietasche ({1}/{2}): {0}","Bolsa del zombi ({1}/{2}): {0}","Sac du zombie ({1}/{2}) : {0}","Borsa dello zombi ({1}/{2}): {0}","ゾンビの袋({1}/{2}):{0}","좀비 가방 ({1}/{2}): {0}","Torba zombie ({1}/{2}): {0}","Bolsa do zumbi ({1}/{2}): {0}","Сумка зомби ({1}/{2}): {0}","Zombi çantası ({1}/{2}): {0}","僵尸的背包({1}/{2}):{0}","殭屍的背包({1}/{2}):{0}"
necroZombieWeaponUnknown,items,Item,,,,"This dead one doesn't know what to do with it: {0}",,"Dieser Tote weiß nichts damit anzufangen: {0}","Este muerto no sabe qué hacer con esto: {0}","Ce mort ne sait pas quoi en faire : {0}","Questo morto non sa cosa farsene: {0}","この死者はこれの使い方を知らない:{0}","이 망자는 이것을 어떻게 쓰는지 모릅니다: {0}","Ten martwiec nie wie, co z tym zrobić: {0}","Este morto não sabe o que fazer com isso: {0}","Этот мертвец не знает, что с этим делать: {0}","Bu ölü bununla ne yapacağını bilmiyor: {0}","这个死者不知道该拿它怎么办:{0}","這個死者不知道該拿它怎麼辦:{0}"
necroZombieWeaponBroken,items,Item,,,,"{0} is broken - fists it is",,"{0} ist kaputt - also Fäuste","{0} está roto: toca usar los puños","{0} est cassé : ce sera aux poings","{0} è rotto: si va di pugni","{0}は壊れている――素手で殴るしかない","{0}이(가) 부서졌습니다 - 주먹으로 싸웁니다","{0} jest zepsuty - zostają pięści","{0} está quebrado - vai no soco","{0}: сломано - остаются кулаки","{0} kırık - yumruklar kaldı","{0}坏了——只能用拳头","{0}壞了——只能用拳頭"
necroPossessHintAfterFear,items,Item,,,,"after you leave, the zombie flees in chilling terror",,"nach dem Verlassen flieht der Zombie in eisigem Schrecken","al salir, el zombi huye presa de un terror helado","quand tu le quittes, le zombie fuit, glacé d'effroi","quando lo lasci, lo zombi fugge in preda al terrore","離れるとゾンビは凍てつく恐怖で逃げ出す","빠져나가면 좀비는 얼어붙는 공포에 질려 달아남","po wyjściu zombie ucieka w lodowatym przerażeniu","ao sair, o zumbi foge em terror gelado","после выхода зомби в леденящем ужасе убегает","çıktığında zombi dondurucu bir dehşetle kaçar","离开后,僵尸会在刺骨的恐惧中逃跑","離開後,殭屍會在刺骨的恐懼中逃跑" necroPossessHintAfterFear,items,Item,,,,"after you leave, the zombie flees in chilling terror",,"nach dem Verlassen flieht der Zombie in eisigem Schrecken","al salir, el zombi huye presa de un terror helado","quand tu le quittes, le zombie fuit, glacé d'effroi","quando lo lasci, lo zombi fugge in preda al terrore","離れるとゾンビは凍てつく恐怖で逃げ出す","빠져나가면 좀비는 얼어붙는 공포에 질려 달아남","po wyjściu zombie ucieka w lodowatym przerażeniu","ao sair, o zumbi foge em terror gelado","после выхода зомби в леденящем ужасе убегает","çıktığında zombi dondurucu bir dehşetle kaçar","离开后,僵尸会在刺骨的恐惧中逃跑","離開後,殭屍會在刺骨的恐懼中逃跑"
necroWolfBagEmpty,items,Item,,,,"empty",,"leer","vacía","vide","vuota","空","비어 있음","pusta","vazia","пусто","boş","空","空" necroWolfBagEmpty,items,Item,,,,"empty",,"leer","vacía","vide","vuota","空","비어 있음","pusta","vazia","пусто","boş","空","空"
necroWolfBagTook,items,Item,,,,"Wolf bag ({1}/{2}): {0}",,"Wolfstasche ({1}/{2}): {0}","Bolsa del lobo ({1}/{2}): {0}","Sac du loup ({1}/{2}) : {0}","Borsa del lupo ({1}/{2}): {0}","狼の袋({1}/{2}):{0}","늑대 가방 ({1}/{2}): {0}","Torba wilka ({1}/{2}): {0}","Bolsa do lobo ({1}/{2}): {0}","Сумка волка ({1}/{2}): {0}","Kurt çantası ({1}/{2}): {0}","狼的背包({1}/{2}):{0}","狼的背包({1}/{2}):{0}" necroWolfBagTook,items,Item,,,,"Wolf bag ({1}/{2}): {0}",,"Wolfstasche ({1}/{2}): {0}","Bolsa del lobo ({1}/{2}): {0}","Sac du loup ({1}/{2}) : {0}","Borsa del lupo ({1}/{2}): {0}","狼の袋({1}/{2}):{0}","늑대 가방 ({1}/{2}): {0}","Torba wilka ({1}/{2}): {0}","Bolsa do lobo ({1}/{2}): {0}","Сумка волка ({1}/{2}): {0}","Kurt çantası ({1}/{2}): {0}","狼的背包({1}/{2}):{0}","狼的背包({1}/{2}):{0}"
@@ -496,7 +498,7 @@ necroTomeKnifeP1,XUi,Menu,,,,"[ff7a55]Knife[-]\nLMB - strike: heals you for half
necroTomeKnifeP2,XUi,Menu,,,,"The first tool. It is cut from a single bone of the dead and never forged: metal holds no Kvazaŭvivo, bone remembers it.\n\nThe blade does not so much cut as take. With every strike it draws denso out of the dead - as much as the hand that holds it can bear. In an untrained hand it is a bone splinter; in a practised one, a death sentence. Half of what is taken goes back into the hand - hence the healing.",,,,,,,,,,"Первый инструмент. Его вырезают из одной кости мертвеца и никогда не куют: металл не держит Kvazaŭvivo, а кость помнит её.\n\nКлинок не столько режет, сколько забирает. Каждым ударом он тянет из мёртвого denso - столько, сколько способна удержать держащая его рука. В неопытной руке это костяная щепка, в опытной - приговор. Половина взятого уходит обратно в руку - отсюда и лечение.",,, necroTomeKnifeP2,XUi,Menu,,,,"The first tool. It is cut from a single bone of the dead and never forged: metal holds no Kvazaŭvivo, bone remembers it.\n\nThe blade does not so much cut as take. With every strike it draws denso out of the dead - as much as the hand that holds it can bear. In an untrained hand it is a bone splinter; in a practised one, a death sentence. Half of what is taken goes back into the hand - hence the healing.",,,,,,,,,,"Первый инструмент. Его вырезают из одной кости мертвеца и никогда не куют: металл не держит Kvazaŭvivo, а кость помнит её.\n\nКлинок не столько режет, сколько забирает. Каждым ударом он тянет из мёртвого denso - столько, сколько способна удержать держащая его рука. В неопытной руке это костяная щепка, в опытной - приговор. Половина взятого уходит обратно в руку - отсюда и лечение.",,,
necroTomeKnifeP3,XUi,Menu,,,,"A zombie wounded by the knife becomes a Victim: its structure is torn open, and from its corpse one takes what no other blade yields. Every rite of the art begins with a Victim.\n\nAt night the bone remembers the snake whose vertebra it once was. Give it nearly all of your blood and its spirit will crawl out of the blade - with rezo above zero towards you and below zero towards every other dead thing.\n\nOnly under a blood moon is the bone silent. The moon draws dead dust out of the earth, the air grows as thick with necroenergy as the body itself, and the spirit has no way out: no snake, no mind palace, no possession - not even of your own servants - and the Horns frighten no one until dawn. Whoever was outside the body when the tide rose is thrown back into it.",,,,,,,,,,"Зомби, раненный ножом, становится Жертвой: его структура вскрыта, и из трупа берут то, что не отдаст ни один другой клинок. С Жертвы начинается любой обряд.\n\nНочью кость вспоминает змею, чьим позвонком она когда-то была. Отдайте ей почти всю свою кровь - и дух змеи выползет из клинка: rezo к вам выше нуля, ко всему прочему мёртвому - ниже.\n\nЛишь в ночь кровавой луны кость молчит. Луна вытягивает из земли некротлен, воздух становится так же плотен некроэнергией, как само тело, и духу некуда выйти: ни змеи, ни чертогов разума, ни одержимости - даже своими слугами, - а «коза» до рассвета никого не пугает. Кто был вне тела, когда поднялся прилив, - выброшен обратно в тело.",,, necroTomeKnifeP3,XUi,Menu,,,,"A zombie wounded by the knife becomes a Victim: its structure is torn open, and from its corpse one takes what no other blade yields. Every rite of the art begins with a Victim.\n\nAt night the bone remembers the snake whose vertebra it once was. Give it nearly all of your blood and its spirit will crawl out of the blade - with rezo above zero towards you and below zero towards every other dead thing.\n\nOnly under a blood moon is the bone silent. The moon draws dead dust out of the earth, the air grows as thick with necroenergy as the body itself, and the spirit has no way out: no snake, no mind palace, no possession - not even of your own servants - and the Horns frighten no one until dawn. Whoever was outside the body when the tide rose is thrown back into it.",,,,,,,,,,"Зомби, раненный ножом, становится Жертвой: его структура вскрыта, и из трупа берут то, что не отдаст ни один другой клинок. С Жертвы начинается любой обряд.\n\nНочью кость вспоминает змею, чьим позвонком она когда-то была. Отдайте ей почти всю свою кровь - и дух змеи выползет из клинка: rezo к вам выше нуля, ко всему прочему мёртвому - ниже.\n\nЛишь в ночь кровавой луны кость молчит. Луна вытягивает из земли некротлен, воздух становится так же плотен некроэнергией, как само тело, и духу некуда выйти: ни змеи, ни чертогов разума, ни одержимости - даже своими слугами, - а «коза» до рассвета никого не пугает. Кто был вне тела, когда поднялся прилив, - выброшен обратно в тело.",,,
necroTomePossessTitle,XUi,Menu,,,,Possession,,,,,,,,,,Одержимость,,, necroTomePossessTitle,XUi,Menu,,,,Possession,,,,,,,,,,Одержимость,,,
necroTomePossessP1,XUi,Menu,,,,"[ff7a55]Entering[-]\nEmpty hand, R - the ""Bang-bang"" hold, LMB at a zombie in your sights - the spirit passes into it. The body stays standing, numb.\n\n[ff7a55]In the zombie's body[-]\nWASD - walk · Shift - run · Space - jump · C - crouch\nLMB - strike · RMB and E - this zombie's own powers, if it has any\nE at a door or switch - open, switch on\nF - eat carrion · R - return to your body\n\n[ff7a55]If the body scheme is close[-]\n~ - see through its eyes · 1, 2 - belt · Tab - bag\nE at a container, loot bag or item - into the bag",,,,,,,,,,"[ff7a55]Вселение[-]\nПустая рука, R - хват «Пиф-паф», ЛКМ по зомби в прицеле - дух переходит в него. Тело остаётся стоять, оцепенев.\n\n[ff7a55]В теле зомби[-]\nWASD - ходьба · Shift - бег · Пробел - прыжок · C - пригнуться\nЛКМ - удар · ПКМ и E - приёмы этого зомби, если они у него есть\nE на дверь или выключатель - открыть, включить\nF - есть падаль · R - вернуться в своё тело\n\n[ff7a55]Если схема тела близка[-]\n~ (Ё) - смотреть его глазами · 1, 2 - пояс · Tab - сумка\nE на ящик, мешок или предмет - в сумку",,, necroTomePossessP1,XUi,Menu,,,,"[ff7a55]Entering[-]\nEmpty hand, R - the ""Bang-bang"" hold, LMB at a zombie in your sights - the spirit passes into it. The body stays standing, numb.\n\n[ff7a55]In the zombie's body[-]\nWASD - walk · Shift - run · Space - jump · C - crouch\nLMB - strike · RMB and E - this zombie's own powers, if it has any\nE at a door or switch - open, switch on\nF - eat carrion · R - return to your body · M - map\n\n[ff7a55]If the body scheme is close[-]\n~ - see through its eyes · 1, 2 - belt: the weapon this zombie knew in life · Tab - bag\nE at a container, loot bag or item - into the bag",,,,,,,,,,"[ff7a55]Вселение[-]\nПустая рука, R - хват «Пиф-паф», ЛКМ по зомби в прицеле - дух переходит в него. Тело остаётся стоять, оцепенев.\n\n[ff7a55]В теле зомби[-]\nWASD - ходьба · Shift - бег · Пробел - прыжок · C - пригнуться\nЛКМ - удар · ПКМ и E - приёмы этого зомби, если они у него есть\nE на дверь или выключатель - открыть, включить\nF - есть падаль · R - вернуться в своё тело · M - карта\n\n[ff7a55]Если схема тела близка[-]\n~ (Ё) - смотреть его глазами · 1, 2 - пояс: оружие, привычное зомби при жизни · Tab - сумка\nE на ящик, мешок или предмет - в сумку",,,
necroTomePossessP2,XUi,Menu,,,,"Wild zombies are not raised - they rise on their own. They are born of necroenergy outbursts: where Kvazaŭvivo breaks out of the earth, the dead get up by themselves, with no necromancer and no master. An outburst does not sculpt, it floods: quasi-life pours into whatever body scheme is left and warps it however it happens to.\n\nPossession is not the gift of an artefact but a skill drilled through years of training: it is always with the necromancer, even with no artefacts and no grimoires at hand - as long as the necroenergy density around allows the spirit to leave. It is the art of moving their own spirit into such a frame. The spirit enters someone else's scheme the way a hand enters a glove: the closer the glove is to the hand, the freer the fingers.",,,,,,,,,,"Стихийные зомби не подняты - они встали сами. Их порождают выбросы некроэнергии: где Kvazaŭvivo вырывается из земли, мёртвые поднимаются без некроманта и без хозяина. Выброс не лепит, а заливает: квазижизнь течёт в ту схему тела, какая осталась, и искажает её как придётся.\n\nОдержимость - не дар артефакта, а умение, выработанное годами учёбы: оно всегда при некроманте, даже когда при нём нет ни артефактов, ни гримуаров, - если только плотность некроэнергии вокруг позволяет духу выйти. Это умение переселить свой дух в такой каркас. Дух входит в чужую схему, как рука в перчатку: чем ближе перчатка по форме к руке, тем свободнее пальцы.",,, necroTomePossessP2,XUi,Menu,,,,"Wild zombies are not raised - they rise on their own. They are born of necroenergy outbursts: where Kvazaŭvivo breaks out of the earth, the dead get up by themselves, with no necromancer and no master. An outburst does not sculpt, it floods: quasi-life pours into whatever body scheme is left and warps it however it happens to.\n\nPossession is not the gift of an artefact but a skill drilled through years of training: it is always with the necromancer, even with no artefacts and no grimoires at hand - as long as the necroenergy density around allows the spirit to leave. It is the art of moving their own spirit into such a frame. The spirit enters someone else's scheme the way a hand enters a glove: the closer the glove is to the hand, the freer the fingers.",,,,,,,,,,"Стихийные зомби не подняты - они встали сами. Их порождают выбросы некроэнергии: где Kvazaŭvivo вырывается из земли, мёртвые поднимаются без некроманта и без хозяина. Выброс не лепит, а заливает: квазижизнь течёт в ту схему тела, какая осталась, и искажает её как придётся.\n\nОдержимость - не дар артефакта, а умение, выработанное годами учёбы: оно всегда при некроманте, даже когда при нём нет ни артефактов, ни гримуаров, - если только плотность некроэнергии вокруг позволяет духу выйти. Это умение переселить свой дух в такой каркас. Дух входит в чужую схему, как рука в перчатку: чем ближе перчатка по форме к руке, тем свободнее пальцы.",,,
necroTomePossessP3,XUi,Menu,,,,"Hence the main rule: the closer the body scheme is to the necromancer's own body, and the less the outburst has changed the zombie, the more possession opens up.\n\nAn ordinary dead man of human build hardly differs from the necromancer: the spirit sees through his eyes, acts with his hands, carries a belt and a bag, opens doors. A bloated one, one burnt by radiation, one reshaped into a spider or a mutant is alien: the spirit only steers it from outside - but gets what the outburst gave it: spit, swarm, blast, leap.",,,,,,,,,,"Отсюда главное правило: чем ближе схема тела к телу самого некроманта и чем меньше выброс изменил зомби, тем больше открывается в одержимости.\n\nОбычный мертвец человеческого сложения почти не отличается от некроманта: дух видит его глазами, действует его руками, носит пояс и сумку, открывает двери. Раздутый, обожжённый излучением, перекроенный в паука или мутанта - чужой: дух лишь правит им со стороны, зато получает то, чем его наделил выброс, - плевок, рой, подрыв, прыжок.",,, necroTomePossessP3,XUi,Menu,,,,"Hence the main rule: the closer the body scheme is to the necromancer's own body, and the less the outburst has changed the zombie, the more possession opens up.\n\nAn ordinary dead man of human build hardly differs from the necromancer: the spirit sees through his eyes, acts with his hands, carries a belt and a bag, opens doors. A bloated one, one burnt by radiation, one reshaped into a spider or a mutant is alien: the spirit only steers it from outside - but gets what the outburst gave it: spit, swarm, blast, leap.",,,,,,,,,,"Отсюда главное правило: чем ближе схема тела к телу самого некроманта и чем меньше выброс изменил зомби, тем больше открывается в одержимости.\n\nОбычный мертвец человеческого сложения почти не отличается от некроманта: дух видит его глазами, действует его руками, носит пояс и сумку, открывает двери. Раздутый, обожжённый излучением, перекроенный в паука или мутанта - чужой: дух лишь правит им со стороны, зато получает то, чем его наделил выброс, - плевок, рой, подрыв, прыжок.",,,
necroTomePossessP4,XUi,Menu,,,,"While the spirit is away, the necromancer's body is empty and defenceless - an enemy near it will be sensed. If the possessed dies, the spirit returns to the body, and everything it carried stays in a bag where it fell; leaving a zombie, the necromancer drops its load on the spot as well. Behind the departing spirit a cold trace remains: the former host flees in chilling terror.\n\nOn the night of the blood tide the spirit does not leave the body - possession is unavailable.",,,,,,,,,,"Пока дух в чужом теле, тело некроманта пусто и беззащитно - враг рядом с ним будет замечен. Если одержимый погибнет, дух вернётся в тело, а всё, что тот нёс, останется мешком на месте гибели; покидая зомби, некромант тоже оставляет его ношу на месте. За уходящим духом тянется холодный след: бывший носитель в леденящем ужасе бежит прочь.\n\nВ ночь кровавого прилива дух не покидает тела - одержимость недоступна.",,, necroTomePossessP4,XUi,Menu,,,,"While the spirit is away, the necromancer's body is empty and defenceless - an enemy near it will be sensed. If the possessed dies, the spirit returns to the body, and everything it carried stays in a bag where it fell; leaving a zombie, the necromancer drops its load on the spot as well. Behind the departing spirit a cold trace remains: the former host flees in chilling terror.\n\nOn the night of the blood tide the spirit does not leave the body - possession is unavailable.",,,,,,,,,,"Пока дух в чужом теле, тело некроманта пусто и беззащитно - враг рядом с ним будет замечен. Если одержимый погибнет, дух вернётся в тело, а всё, что тот нёс, останется мешком на месте гибели; покидая зомби, некромант тоже оставляет его ношу на месте. За уходящим духом тянется холодный след: бывший носитель в леденящем ужасе бежит прочь.\n\nВ ночь кровавого прилива дух не покидает тела - одержимость недоступна.",,,
1 Key File Type UsedInMainMenu NoTranslate KeepLoaded english Context / Alternate Text german spanish french italian japanese koreana polish brazilian russian turkish schinese tchinese
356 necroPossessSearchBag items Item {0} Search: {1} {0} Durchsuchen: {1} {0} Registrar: {1} {0} Fouiller : {1} {0} Perquisisci: {1} {0} 調べる: {1} {0} 뒤지기: {1} {0} Przeszukaj: {1} {0} Vasculhar: {1} {0} Обыскать: {1} {0} Ara: {1} {0} 搜索:{1} {0} 搜索:{1}
357 necroWolfBagWindowTitle items Item Wolf bag Wolfstasche Bolsa del lobo Sac du loup Borsa del lupo 狼の袋 늑대 가방 Torba wilka Bolsa do lobo Сумка волка Kurt çantası 狼的包 狼的包
358 necroWolfBagWaypoint items Item Wolf bag Wolfstasche Bolsa del lobo Sac du loup Borsa del lupo 狼の袋 늑대 가방 Torba wilka Bolsa do lobo Сумка волка Kurt çantası 狼的包 狼的包
359 necroZombieBagWindowTitle items Item Zombie bag Zombie bag · bottom row - belt Zombietasche Zombietasche · untere Reihe - Gürtel Bolsa del zombi Bolsa del zombi · fila inferior: cinturón Sac du zombie Sac du zombie · rangée du bas : ceinture Borsa dello zombi Borsa dello zombi · fila in basso: cintura ゾンビの袋 ゾンビの袋・下の段はベルト 좀비 가방 좀비 가방 · 아래 줄 - 벨트 Torba zombie Torba zombie · dolny rząd - pas Bolsa do zumbi Bolsa do zumbi · fileira de baixo - cinto Сумка зомби Сумка зомби · нижний ряд - пояс Zombi çantası Zombi çantası · alt sıra - kemer 僵尸的包 僵尸的包 · 下排是腰带 殭屍的包 殭屍的包 · 下排是腰帶
360 necroZombieBagWaypoint items Item Zombie bag Zombietasche Bolsa del zombi Sac du zombie Borsa dello zombi ゾンビの袋 좀비 가방 Torba zombie Bolsa do zumbi Сумка зомби Zombi çantası 僵尸的包 殭屍的包
361 necroPossessHintZombieBag items Item Tab - bag, E at a container, loot bag or item - into the bag Tab - Tasche, E an Behälter, Beutel oder Gegenstand - in die Tasche Tab: bolsa, E en un contenedor, saco o objeto: a la bolsa Tab : sac, E sur un conteneur, un sac ou un objet : dans le sac Tab - borsa, E su contenitore, sacca o oggetto - nella borsa Tab: 袋、コンテナ・袋・アイテムにE: 袋へ Tab - 가방, 상자·자루·물건에 E - 가방으로 Tab - torba, E na pojemniku, worku lub przedmiocie - do torby Tab - bolsa, E em contêiner, saco ou item - para a bolsa Tab - сумка, E на ящик, мешок или предмет - в сумку Tab - çanta, kaba, torbaya veya eşyaya E - çantaya Tab - 包,对容器、袋子或物品按E - 放入包 Tab - 包,對容器、袋子或物品按E - 放入包
362 necroPossessHintDoor items Item E at a door or switch - use it E an Tür oder Schalter - benutzen E en una puerta o interruptor: usar E sur une porte ou un interrupteur : utiliser E su porta o interruttore - usare ドアやスイッチにE: 使う 문이나 스위치에 E - 사용 E na drzwiach lub przełączniku - użyj E em porta ou interruptor - usar E на дверь или выключатель - открыть, включить Kapıya veya şaltere E - kullan 对门或开关按E - 使用 對門或開關按E - 使用
364 necroZombieBagTook items Item Zombie bag ({1}/{2}): {0} Zombietasche ({1}/{2}): {0} Bolsa del zombi ({1}/{2}): {0} Sac du zombie ({1}/{2}) : {0} Borsa dello zombi ({1}/{2}): {0} ゾンビの袋({1}/{2}):{0} 좀비 가방 ({1}/{2}): {0} Torba zombie ({1}/{2}): {0} Bolsa do zumbi ({1}/{2}): {0} Сумка зомби ({1}/{2}): {0} Zombi çantası ({1}/{2}): {0} 僵尸的背包({1}/{2}):{0} 殭屍的背包({1}/{2}):{0}
365 necroZombieBagFull items Item The zombie bag is full ({1}/{2}): {0} Die Zombietasche ist voll ({1}/{2}): {0} La bolsa del zombi está llena ({1}/{2}): {0} Le sac du zombie est plein ({1}/{2}) : {0} La borsa dello zombi è piena ({1}/{2}): {0} ゾンビの袋がいっぱい({1}/{2}):{0} 좀비 가방이 가득 찼습니다 ({1}/{2}): {0} Torba zombie jest pełna ({1}/{2}): {0} A bolsa do zumbi está cheia ({1}/{2}): {0} Сумка зомби полна ({1}/{2}): {0} Zombi çantası dolu ({1}/{2}): {0} 僵尸的背包已满({1}/{2}):{0} 殭屍的背包已滿({1}/{2}):{0}
366 necroZombieBagShow items Item Zombie bag ({1}/{2}): {0} Zombietasche ({1}/{2}): {0} Bolsa del zombi ({1}/{2}): {0} Sac du zombie ({1}/{2}) : {0} Borsa dello zombi ({1}/{2}): {0} ゾンビの袋({1}/{2}):{0} 좀비 가방 ({1}/{2}): {0} Torba zombie ({1}/{2}): {0} Bolsa do zumbi ({1}/{2}): {0} Сумка зомби ({1}/{2}): {0} Zombi çantası ({1}/{2}): {0} 僵尸的背包({1}/{2}):{0} 殭屍的背包({1}/{2}):{0}
367 necroZombieWeaponUnknown items Item This dead one doesn't know what to do with it: {0} Dieser Tote weiß nichts damit anzufangen: {0} Este muerto no sabe qué hacer con esto: {0} Ce mort ne sait pas quoi en faire : {0} Questo morto non sa cosa farsene: {0} この死者はこれの使い方を知らない:{0} 이 망자는 이것을 어떻게 쓰는지 모릅니다: {0} Ten martwiec nie wie, co z tym zrobić: {0} Este morto não sabe o que fazer com isso: {0} Этот мертвец не знает, что с этим делать: {0} Bu ölü bununla ne yapacağını bilmiyor: {0} 这个死者不知道该拿它怎么办:{0} 這個死者不知道該拿它怎麼辦:{0}
368 necroZombieWeaponBroken items Item {0} is broken - fists it is {0} ist kaputt - also Fäuste {0} está roto: toca usar los puños {0} est cassé : ce sera aux poings {0} è rotto: si va di pugni {0}は壊れている――素手で殴るしかない {0}이(가) 부서졌습니다 - 주먹으로 싸웁니다 {0} jest zepsuty - zostają pięści {0} está quebrado - vai no soco {0}: сломано - остаются кулаки {0} kırık - yumruklar kaldı {0}坏了——只能用拳头 {0}壞了——只能用拳頭
369 necroPossessHintAfterFear items Item after you leave, the zombie flees in chilling terror nach dem Verlassen flieht der Zombie in eisigem Schrecken al salir, el zombi huye presa de un terror helado quand tu le quittes, le zombie fuit, glacé d'effroi quando lo lasci, lo zombi fugge in preda al terrore 離れるとゾンビは凍てつく恐怖で逃げ出す 빠져나가면 좀비는 얼어붙는 공포에 질려 달아남 po wyjściu zombie ucieka w lodowatym przerażeniu ao sair, o zumbi foge em terror gelado после выхода зомби в леденящем ужасе убегает çıktığında zombi dondurucu bir dehşetle kaçar 离开后,僵尸会在刺骨的恐惧中逃跑 離開後,殭屍會在刺骨的恐懼中逃跑
370 necroWolfBagEmpty items Item empty leer vacía vide vuota 空 비어 있음 pusta vazia пусто boş 空 空
371 necroWolfBagTook items Item Wolf bag ({1}/{2}): {0} Wolfstasche ({1}/{2}): {0} Bolsa del lobo ({1}/{2}): {0} Sac du loup ({1}/{2}) : {0} Borsa del lupo ({1}/{2}): {0} 狼の袋({1}/{2}):{0} 늑대 가방 ({1}/{2}): {0} Torba wilka ({1}/{2}): {0} Bolsa do lobo ({1}/{2}): {0} Сумка волка ({1}/{2}): {0} Kurt çantası ({1}/{2}): {0} 狼的背包({1}/{2}):{0} 狼的背包({1}/{2}):{0}
498
499
500
501
502
503
504
+73 -1
View File
@@ -68,6 +68,9 @@ namespace NecromancerTome
static GameObject _rig; static GameObject _rig;
static SDCSUtils.TransformCatalog _bones; static SDCSUtils.TransformCatalog _bones;
/// <summary>Модель оружия с пояса в правой руке (#37) и чья она (тип предмета; -1 - кулаки).</summary>
static AssetManagement.InstanceHandle<GameObject> _weapon;
static int _weaponType = -1;
static bool _layerWasShown; static bool _layerWasShown;
public static bool Active => _rig != null; public static bool Active => _rig != null;
@@ -279,6 +282,65 @@ namespace NecromancerTome
} }
} }
/// <summary>Оружие с пояса в руках (#37): модель предмета на кость RightWeapon, как у персонажа от первого лица
/// (ItemInventoryData.SeatModel - без сдвига), и хват WeaponHoldType этого предмета; null - кулаки (хват 0).
/// Ставится каждый кадр, пересобирается только при смене предмета.</summary>
public static void SetWeapon(ItemValue _iv)
{
int type = _iv != null && !_iv.IsEmpty() ? _iv.type : -1;
if (_rig == null || type == _weaponType)
{
return;
}
DestroyWeapon();
_weaponType = type;
Animator a = Anim;
ItemClass ic = type >= 0 ? _iv.ItemClass : null;
Transform hand = _rig.transform.FindInChilds("RightWeapon");
if (ic != null && hand != null)
{
_weapon = ic.CloneModel(_iv, hand, BlockShape.MeshPurpose.Hold);
GameObject go = _weapon.Instance;
if (go != null)
{
go.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
if (!ic.GetCorrectionScale().Equals(Vector3.zero))
{
go.transform.localScale = ic.GetCorrectionScale();
}
int layer = LayerMask.NameToLayer("HoldingItem");
foreach (Transform t in go.GetComponentsInChildren<Transform>(true))
{
t.gameObject.layer = layer;
}
foreach (Collider c in go.GetComponentsInChildren<Collider>(true))
{
c.enabled = false;
}
go.SetActive(!ic.HoldingItemHidden);
}
}
if (a != null)
{
SetParam(a, "WeaponHoldType", ic != null ? ic.HoldType.Value : 0f);
SetParam(a, "ItemHasChangedTrigger", 1f);
}
Debug.Log("[NecromancerTome] FpArms: в руке " + (ic != null ? ic.Name + " (хват " + ic.HoldType.Value + (_weapon.Instance != null ? "" : ", БЕЗ модели") + ")" : "ничего - кулаки") +
(hand == null && ic != null ? " - кость RightWeapon не найдена" : ""));
}
static void DestroyWeapon()
{
if (_weapon.Instance != null)
{
_weapon.Instance.transform.SetParent(null, false);
_weapon.Instance.SetActive(false);
_weapon.Destroy();
}
_weapon = default(AssetManagement.InstanceHandle<GameObject>);
_weaponType = -1;
}
/// <summary>Анимация удара рук вошла в состояние удара (AnimatorMeleeAttackState у наших рук): через RaycastTime - /// <summary>Анимация удара рук вошла в состояние удара (AnimatorMeleeAttackState у наших рук): через RaycastTime -
/// миг удара, как у персонажа (impactStart). Руки на это время уходят в нижнее положение (под прицел).</summary> /// миг удара, как у персонажа (impactStart). Руки на это время уходят в нижнее положение (под прицел).</summary>
public static void OnSwingEnter(float _raycastTime) public static void OnSwingEnter(float _raycastTime)
@@ -421,6 +483,7 @@ namespace NecromancerTome
{ {
_player?.ShowHoldingItemLayer(false); _player?.ShowHoldingItemLayer(false);
} }
DestroyWeapon();
SDCSUtils.UnloadViz(_rig); SDCSUtils.UnloadViz(_rig);
_rig = null; _rig = null;
_bones = null; _bones = null;
@@ -437,7 +500,7 @@ namespace NecromancerTome
public class ConsoleCmdNecroArms : ConsoleCmdAbstract public class ConsoleCmdNecroArms : ConsoleCmdAbstract
{ {
public override string[] getCommands() => new[] { "necroarms" }; public override string[] getCommands() => new[] { "necroarms" };
public override string getDescription() => "NecromancerTome: руки одержимого от первого лица - [pos x y z | parent cam|ufps | anim | mat | set <param> <value> | trigger <name> | tint r g b | albedo on|off | punch <trigger> | fov <deg>]"; public override string getDescription() => "NecromancerTome: руки одержимого от первого лица - [pos x y z | parent cam|ufps | anim | mat | set <param> <value> | trigger <name> | tint r g b | albedo on|off | punch <trigger> | fov <deg> | weapon [on|off]]";
void Out(string _s) void Out(string _s)
{ {
@@ -477,6 +540,15 @@ namespace NecromancerTome
{ {
FpArms.ArmsFov = fov; FpArms.ArmsFov = fov;
} }
else if (cmd == "weapon")
{
if (_params.Count >= 2)
{
ZombieWeapons.Enabled = _params[1] != "off";
}
Out(Possession.Active && Possession.Zombie != null ? ZombieWeapons.Dump(Possession.Zombie) : "оружие " + (ZombieWeapons.Enabled ? "вкл" : "ВЫКЛ") + " (не в одержимости)");
return;
}
else if (cmd == "anim") else if (cmd == "anim")
{ {
Out(FpArms.DumpAnim()); Out(FpArms.DumpAnim());
+69
View File
@@ -19,6 +19,7 @@ namespace NecromancerTome
/// necrobox x y z list|clear|add предмет число - содержимое хранилища (ящик, сумка урны) /// necrobox x y z list|clear|add предмет число - содержимое хранилища (ящик, сумка урны)
/// necrourn x y z info|skull 0|1|from|to|out bx by bz|clear|sample предмет число|group все|нет - урна /// necrourn x y z info|skull 0|1|from|to|out bx by bz|clear|sample предмет число|group все|нет - урна
/// necroclick 0|1 - отпущенная ЛКМ/ПКМ ножом для нитей по блоку под прицелом /// necroclick 0|1 - отпущенная ЛКМ/ПКМ ножом для нитей по блоку под прицелом
/// necropossess near [класс]|fp|hit|look id / x y z|slot N|belt N предмет [качество]|info|exit - одержимость без мыши (#37, 25.10)
/// </summary> /// </summary>
public static class NecroDev public static class NecroDev
{ {
@@ -265,6 +266,7 @@ namespace NecromancerTome
Out("necrocanplace: " + (TEFeatureCinerarium.PlacementRefusal(P(_params, 1), Block.GetBlockByName(_params[0], true)) ?? "можно")); Out("necrocanplace: " + (TEFeatureCinerarium.PlacementRefusal(P(_params, 1), Block.GetBlockByName(_params[0], true)) ?? "можно"));
break; break;
case "necrogod": God(pl, _params.Count == 0 || _params[0] != "off"); break; case "necrogod": God(pl, _params.Count == 0 || _params[0] != "off"); break;
case "necropossess": Possess(w, pl, _params); break;
case "necroclick": case "necroclick":
// necroclick 0|1 [x y z] - по блоку под прицелом или по указанной клетке (луч не всегда попадает в низкий череп). // necroclick 0|1 [x y z] - по блоку под прицелом или по указанной клетке (луч не всегда попадает в низкий череп).
if (_params.Count >= 4) if (_params.Count >= 4)
@@ -310,6 +312,64 @@ namespace NecromancerTome
/// <summary>Режим бога и невидимость для ИИ: пока ассистент проверяет, персонаж стоит - зомби тестового мира его не трогают /// <summary>Режим бога и невидимость для ИИ: пока ассистент проверяет, персонаж стоит - зомби тестового мира его не трогают
/// (18.10: «пока персонаж просто стоит, его скушают»; «Я обычно ставлю режим Бога»).</summary> /// (18.10: «пока персонаж просто стоит, его скушают»; «Я обычно ставлю режим Бога»).</summary>
/// <summary>necropossess: вселиться в ближайшего зомби (класс - начало имени), вид от первого лица, ЛКМ, ячейка и
/// предмет пояса, состояние, выход.</summary>
static void Possess(World _w, EntityPlayerLocal _pl, List<string> _p)
{
string cmd = _p.Count > 0 ? _p[0] : "info";
EntityAlive z = Possession.Zombie;
switch (cmd)
{
case "near":
{
string cls = _p.Count > 1 ? _p[1] : "";
EntityAlive best = null;
float bestD = 40f * 40f;
foreach (EntityAlive e in _w.EntityAlives)
{
if (!Possession.CanPossess(e) || !EntityClass.list.TryGetValue(e.entityClass, out EntityClass ec) ||
!ec.entityClassName.StartsWith(cls, System.StringComparison.OrdinalIgnoreCase))
{
continue;
}
float d = (e.position - _pl.position).sqrMagnitude;
if (d < bestD)
{
bestD = d;
best = e;
}
}
Out("necropossess: " + (best == null ? "зомби рядом нет" : best.EntityName + " (" + best.entityId + ") - " + (Possession.Enter(_pl, best) ? "вселился" : "не вышло")));
return;
}
case "fp": Possession.DevView = true; break;
case "hit": Possession.DevAttack = true; break;
case "look":
{
// look id - на существо (грудь), look x y z - на точку
Vector3 at = _p.Count >= 4 ? new Vector3(F(_p[1]), F(_p[2]), F(_p[3]))
: (_w.GetEntity(int.Parse(_p[1])) as EntityAlive)?.getChestPosition() ?? Vector3.zero;
Possession.DevLookAt(at);
break;
}
case "slot": Possession.BeltSlot = Mathf.Clamp(int.Parse(_p[1]) - 1, 0, Possession.BeltSlots - 1); break;
case "belt":
{
ItemValue iv = ItemClass.GetItem(_p[2]);
if (iv == null || iv.IsEmpty())
{
Out("necropossess: нет предмета " + _p[2]);
return;
}
iv = new ItemValue(iv.type, _p.Count > 3 ? int.Parse(_p[3]) : 1, _p.Count > 3 ? int.Parse(_p[3]) : 1);
Out("necropossess: " + (ZombieBag.DevSetBelt(int.Parse(_p[1]) - 1, new ItemStack(iv, 1)) ? "положено" : "сумки нет"));
break;
}
case "exit": Possession.Exit("necropossess"); Out("necropossess: выход"); return;
}
Out("necropossess: " + (z == null ? "не в одержимости" : z.EntityName + ", от первого лица " + Possession.FirstPerson + "\n" + ZombieWeapons.Dump(z)));
}
static void God(EntityPlayerLocal _pl, bool _on) static void God(EntityPlayerLocal _pl, bool _on)
{ {
_pl.IsGodMode.Value = _on; _pl.IsGodMode.Value = _on;
@@ -592,6 +652,15 @@ namespace NecromancerTome
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo) => NecroDev.Run("necroclick", _params); public override void Execute(List<string> _params, CommandSenderInfo _senderInfo) => NecroDev.Run("necroclick", _params);
} }
[Preserve]
public class ConsoleCmdNecroPossess : ConsoleCmdAbstract
{
public override string[] getCommands() => new[] { "necropossess" };
public override string getDescription() => "NecromancerTome (разработка): одержимость без мыши - near [класс] | fp | hit | slot N | belt N предмет [качество] | info | exit";
public override bool AllowedInMainMenu => false;
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo) => NecroDev.Run("necropossess", _params);
}
[Preserve] [Preserve]
public class ConsoleCmdNecroGod : ConsoleCmdAbstract public class ConsoleCmdNecroGod : ConsoleCmdAbstract
{ {
+107 -13
View File
@@ -55,6 +55,9 @@ namespace NecromancerTome
public static EntityAlive Zombie; public static EntityAlive Zombie;
/// <summary>Вселились в своего питомца (книга призыва), а не в чужого зомби.</summary> /// <summary>Вселились в своего питомца (книга призыва), а не в чужого зомби.</summary>
public static bool IsPet; public static bool IsPet;
/// <summary>Разведка (туман карты вокруг одержимого, карта открывается на нём): стервятник (08.10) и любой одержимый
/// зомби (#36, 25.10); прочие питомцы - нет.</summary>
public static bool Scouts => Active && (Flying || !IsPet);
/// <summary>Питомец-стервятник: летает (FlyTick из его updateTasks).</summary> /// <summary>Питомец-стервятник: летает (FlyTick из его updateTasks).</summary>
public static bool Flying; public static bool Flying;
/// <summary>Враг у тела (CheckBody) - для надписи в PossessionHud.</summary> /// <summary>Враг у тела (CheckBody) - для надписи в PossessionHud.</summary>
@@ -153,7 +156,13 @@ namespace NecromancerTome
public static bool FirstPerson; public static bool FirstPerson;
/// <summary>Пояс одержимого (13.10): 2 ячейки, пока пустые; выбор 1/2 или колесом, только от первого лица.</summary> /// <summary>Пояс одержимого (13.10): 2 ячейки, пока пустые; выбор 1/2 или колесом, только от первого лица.</summary>
public const int BeltSlots = 2; public const int BeltSlots = 2;
/// <summary>Чаще руки не успевают доиграть замах (ножи в игре - до 100+ ударов в минуту).</summary>
const float MinWeaponSwing = 0.45f;
public static int BeltSlot; public static int BeltSlot;
/// <summary>Нажатия от necropossess (проверка без мыши): ЛКМ и смена вида - срабатывают в ближайшем кадре.</summary>
public static bool DevAttack, DevView;
/// <summary>Ячейка и тип предмета в ней при прошлой проверке - подсказка о неумелом оружии один раз на смену.</summary>
static int _beltKey = -1;
public const float FpForward = 0.12f, FpSmooth = 12f; public const float FpForward = 0.12f, FpSmooth = 12f;
static Vector3 _fpEye; static Vector3 _fpEye;
static readonly List<Renderer> _hiddenRenderers = new List<Renderer>(); static readonly List<Renderer> _hiddenRenderers = new List<Renderer>();
@@ -218,6 +227,18 @@ namespace NecromancerTome
/// <summary>Игрок, который сейчас в одержимом (тело).</summary> /// <summary>Игрок, который сейчас в одержимом (тело).</summary>
public static EntityPlayerLocal Player => _player; public static EntityPlayerLocal Player => _player;
static float _yaw, _pitch; static float _yaw, _pitch;
/// <summary>necropossess look: взгляд одержимого на точку (проверка без мыши).</summary>
public static void DevLookAt(Vector3 _pos)
{
if (Zombie == null)
{
return;
}
Vector3 d = _pos - Zombie.getHeadPosition();
_yaw = Mathf.Atan2(d.x, d.z) * Mathf.Rad2Deg;
_pitch = Mathf.Atan2(d.y, new Vector2(d.x, d.z).magnitude) * Mathf.Rad2Deg;
}
/// <summary>Куда повёрнуто тело зомби. Шага вбок и назад у зомби нет (тест 03.10: «не работают A, D и S») - /// <summary>Куда повёрнуто тело зомби. Шага вбок и назад у зомби нет (тест 03.10: «не работают A, D и S») -
/// при движении тело разворачивается в сторону шага и идёт вперёд, без движения - смотрит туда же, куда камера.</summary> /// при движении тело разворачивается в сторону шага и идёт вперёд, без движения - смотрит туда же, куда камера.</summary>
static float _bodyYaw; static float _bodyYaw;
@@ -567,9 +588,9 @@ namespace NecromancerTome
Exit("кровавый прилив - дух выброшен в тело"); Exit("кровавый прилив - дух выброшен в тело");
return; return;
} }
if (Flying) if (Scouts)
{ {
RevealMap(world, z); // разведка - только стервятник (указание 08.10) RevealMap(world, z); // разведка - стервятник (указание 08.10) и одержимый зомби (#36)
} }
// Подсказка тела (тест 08.10: «стоял перед дверью - подсказка осталась в стервятнике»): её гасит // Подсказка тела (тест 08.10: «стоял перед дверью - подсказка осталась в стервятнике»): её гасит
// PlayerMoveController.Update, а он на время одержимости выключен. Прочие сообщения (голод и т.п.) - // PlayerMoveController.Update, а он на время одержимости выключен. Прочие сообщения (голод и т.п.) -
@@ -615,7 +636,8 @@ namespace NecromancerTome
move = new Vector2(input.Move.X, input.Move.Y); move = new Vector2(input.Move.X, input.Move.Y);
run = input.Run.IsPressed; run = input.Run.IsPressed;
jump = input.Jump.WasPressed; jump = input.Jump.WasPressed;
attack = input.Primary.WasPressed; attack = input.Primary.WasPressed || DevAttack;
DevAttack = false;
ranged = input.Secondary.WasPressed; ranged = input.Secondary.WasPressed;
rangedHeld = input.Secondary.IsPressed; rangedHeld = input.Secondary.IsPressed;
// E на клавиатуре - в PlayerActionsPermanent.Activate; у PlayerActionsLocal.Activate только // E на клавиатуре - в PlayerActionsPermanent.Activate; у PlayerActionsLocal.Activate только
@@ -630,8 +652,9 @@ namespace NecromancerTome
} }
// Только гуманоиды обычной комплекции (указание 13.10, FpArms.Allows) - у остальных ~/ё ничего не делает. // Только гуманоиды обычной комплекции (указание 13.10, FpArms.Allows) - у остальных ~/ё ничего не делает.
if (input != null && !gui && FpArms.Allows(z) && if (input != null && !gui && FpArms.Allows(z) &&
(input.CameraChange.WasPressed || input.QuickMenu.WasPressed || input.PermanentActions.QuickMenu.WasPressed)) (input.CameraChange.WasPressed || input.QuickMenu.WasPressed || input.PermanentActions.QuickMenu.WasPressed || DevView))
{ {
DevView = false;
FirstPerson = !FirstPerson; FirstPerson = !FirstPerson;
_fpEye = Vector3.zero; _fpEye = Vector3.zero;
if (FirstPerson) if (FirstPerson)
@@ -665,6 +688,21 @@ namespace NecromancerTome
Debug.Log("[NecromancerTome] Possession: пояс - ячейка " + (BeltSlot + 1)); Debug.Log("[NecromancerTome] Possession: пояс - ячейка " + (BeltSlot + 1));
} }
} }
if (FirstPerson && ZombieBag.Active)
{
// Оружие с пояса (#37): в руки - если зомби им владеет; сменился выбранный предмет - подсказка, если нет.
ItemStack sel = ZombieBag.Belt(BeltSlot);
int key = BeltSlot * 100000 + (sel != null ? sel.itemValue.type : -1);
if (key != _beltKey)
{
_beltKey = key;
if (sel != null)
{
ZombieWeapons.Hint(_player, z, sel.itemValue);
}
}
FpArms.SetWeapon(ZombieWeapons.Current(z));
}
if (input != null && !gui && input.ToggleCrouch.WasPressed) if (input != null && !gui && input.ToggleCrouch.WasPressed)
{ {
_crouchToggled = !_crouchToggled; _crouchToggled = !_crouchToggled;
@@ -845,7 +883,8 @@ namespace NecromancerTome
{ {
// Tab в зомби - окно его сумки, а не рюкзак персонажа (указание 13.10), как у волка. // Tab в зомби - окно его сумки, а не рюкзак персонажа (указание 13.10), как у волка.
GUIWindowManager zwm = LocalPlayerUI.GetUIForPlayer(_player)?.windowManager; GUIWindowManager zwm = LocalPlayerUI.GetUIForPlayer(_player)?.windowManager;
if (zwm != null && zwm.IsWindowOpen("windowpaging") && !ZombieBag.WindowOpen && !RatLoot.Viewing) // M открывает то же окно windowpaging (с картой) - карту не подменять (#36: M в зомби открывал сумку).
if (zwm != null && zwm.IsWindowOpen("windowpaging") && !zwm.IsWindowOpen("map") && !ZombieBag.WindowOpen && !RatLoot.Viewing)
{ {
zwm.CloseAllOpenModalWindows(); zwm.CloseAllOpenModalWindows();
zwm.Close("windowpaging"); zwm.Close("windowpaging");
@@ -862,7 +901,7 @@ namespace NecromancerTome
{ {
// Tab в волке - окно его сумки, а не рюкзак персонажа (тест 10.10: «в волке Tab - вижу сумку игрока»). // Tab в волке - окно его сумки, а не рюкзак персонажа (тест 10.10: «в волке Tab - вижу сумку игрока»).
GUIWindowManager wm = LocalPlayerUI.GetUIForPlayer(_player)?.windowManager; GUIWindowManager wm = LocalPlayerUI.GetUIForPlayer(_player)?.windowManager;
if (wm != null && wm.IsWindowOpen("windowpaging") && !WolfBag.WindowOpen && !RatLoot.Viewing) if (wm != null && wm.IsWindowOpen("windowpaging") && !wm.IsWindowOpen("map") && !WolfBag.WindowOpen && !RatLoot.Viewing)
{ {
wm.CloseAllOpenModalWindows(); wm.CloseAllOpenModalWindows();
wm.Close("windowpaging"); wm.Close("windowpaging");
@@ -905,7 +944,10 @@ namespace NecromancerTome
// до точки удара (FpArms -> FpImpact), а не по его собственному замаху. // до точки удара (FpArms -> FpImpact), а не по его собственному замаху.
PossessionPowers.BreakInvis(z, "удар"); PossessionPowers.BreakInvis(z, "удар");
FpArms.Punch(); FpArms.Punch();
_nextAttack = Time.time + Mathf.Max(FpArms.SwingTime, z.GetAttackTimeoutTicks() / 20f); ItemValue weapon = ZombieWeapons.Current(z);
// С оружием - темп оружия, как у персонажа (#37); кулаками - темп зомби.
_nextAttack = Time.time + (weapon != null ? Mathf.Max(MinWeaponSwing, ZombieWeapons.SwingTime(weapon))
: Mathf.Max(FpArms.SwingTime, z.GetAttackTimeoutTicks() / 20f));
} }
else if (attack && Time.time >= _nextAttack) else if (attack && Time.time >= _nextAttack)
{ {
@@ -2386,6 +2428,18 @@ namespace NecromancerTome
{ {
return; return;
} }
if (ZombieWeapons.Current(z) != null)
{
// Оружие с пояса (#37) - удар как у персонажа, луч из глаз (камера одержимости - в голове зомби). Камера - в
// координатах Unity, луч мира - с Origin (тест 25.10: без него «луч ничего не задел» вплотную).
Transform cam = _player?.playerCamera != null ? _player.playerCamera.transform : null;
Vector3 head = z.getHeadPosition();
if (!ZombieWeapons.Strike(z.world, z, cam != null ? new Ray(cam.position + Origin.position, cam.forward) : new Ray(head, (AimPoint() - head).normalized)))
{
Debug.Log("[NecromancerTome] ZombieWeapons: удар мимо");
}
return;
}
z.SetLookPosition(MeleeAim(z.world, z)); z.SetLookPosition(MeleeAim(z.world, z));
_meleeLookUntil = Time.time + 0.6f; _meleeLookUntil = Time.time + 0.6f;
if (!StartStrike(z)) if (!StartStrike(z))
@@ -2590,14 +2644,13 @@ namespace NecromancerTome
} }
} }
/// <summary>Карта в одержимом стервятнике открывается на нём, а не на теле (указание 08.10: разведка - /// <summary>Карта в одержимом стервятнике и зомби открывается на нём, а не на теле (указание 08.10, #36).</summary>
/// только у стервятника).</summary>
[HarmonyPatch(typeof(XUiC_MapArea), nameof(XUiC_MapArea.OnOpen))] [HarmonyPatch(typeof(XUiC_MapArea), nameof(XUiC_MapArea.OnOpen))]
public static class Patch_XUiC_MapArea_OnOpen_Possession public static class Patch_XUiC_MapArea_OnOpen_Possession
{ {
public static void Postfix(XUiC_MapArea __instance) public static void Postfix(XUiC_MapArea __instance)
{ {
if (Possession.Active && Possession.Flying && Possession.Zombie != null) if (Possession.Scouts && Possession.Zombie != null)
{ {
__instance.PositionMapAt(Possession.Zombie.GetPosition()); __instance.PositionMapAt(Possession.Zombie.GetPosition());
} }
@@ -2610,7 +2663,7 @@ namespace NecromancerTome
{ {
public static bool Prefix(XUiC_MapArea __instance) public static bool Prefix(XUiC_MapArea __instance)
{ {
if (!Possession.Active || !Possession.Flying || Possession.Zombie == null) if (!Possession.Scouts || Possession.Zombie == null)
{ {
return true; return true;
} }
@@ -2871,8 +2924,9 @@ namespace NecromancerTome
} }
} }
/// <summary>Пояс одержимого от первого лица (указание 13.10: «собственный пояс на 2 ячейки», пока пустой) - на месте /// <summary>Пояс одержимого от первого лица (указание 13.10: «собственный пояс на 2 ячейки») - на месте пояса персонажа,
/// пояса персонажа, внизу по центру; выбранная ячейка в рамке. Ячейки пустые - бьёт кулаками.</summary> /// внизу по центру; выбранная ячейка в рамке. В ячейке - иконка предмета из нижнего ряда сумки (#37), полоска износа;
/// чем зомби не владеет или что сломано - бледное (бьёт кулаками).</summary>
static void DrawBelt(float _scale) static void DrawBelt(float _scale)
{ {
float size = 64f * _scale, gap = 6f * _scale, frame = 3f * _scale; float size = 64f * _scale, gap = 6f * _scale, frame = 3f * _scale;
@@ -2890,11 +2944,51 @@ namespace NecromancerTome
GUI.color = Back; GUI.color = Back;
GUI.DrawTexture(r, Texture2D.whiteTexture); GUI.DrawTexture(r, Texture2D.whiteTexture);
GUI.color = prev; GUI.color = prev;
ItemStack st = ZombieBag.Belt(i);
if (st != null)
{
bool fits = ZombieWeapons.Enabled && ZombieWeapons.Check(Possession.Zombie, st.itemValue) == ZombieWeapons.Fit.Ok;
DrawIcon(new Rect(r.x + 6f * _scale, r.y + 6f * _scale, r.width - 12f * _scale, r.height - 12f * _scale), st.itemValue, fits ? 1f : 0.3f);
if (st.itemValue.MaxUseTimes > 0)
{
float left = 1f - Mathf.Clamp01(st.itemValue.UseTimes / st.itemValue.MaxUseTimes);
GUI.color = Color.Lerp(new Color(0.85f, 0.2f, 0.15f), new Color(0.35f, 0.8f, 0.3f), left);
GUI.DrawTexture(new Rect(r.x + 4f * _scale, r.yMax - 6f * _scale, (r.width - 8f * _scale) * left, 3f * _scale), Texture2D.whiteTexture);
GUI.color = prev;
}
if (st.count > 1)
{
GUI.Label(new Rect(r.x, r.yMax - 22f * _scale, r.width - 4f * _scale, 18f * _scale), st.count.ToString(), _style);
}
}
GUI.Label(new Rect(r.x + 4f * _scale, r.y + 2f * _scale, r.width, 18f * _scale), (i + 1).ToString(), _style); GUI.Label(new Rect(r.x + 4f * _scale, r.y + 2f * _scale, r.width, 18f * _scale), (i + 1).ToString(), _style);
} }
GUI.color = prev; GUI.color = prev;
} }
/// <summary>Иконка предмета из атласа ItemIconAtlas (как у ячеек игры) - кусок текстуры атласа по данным спрайта.</summary>
static void DrawIcon(Rect _r, ItemValue _iv, float _alpha)
{
ItemClass ic = _iv?.ItemClass;
string name = ic?.GetIconName();
INGUIAtlas atlas = string.IsNullOrEmpty(name) ? null : LocalPlayerUI.GetUIForPrimaryPlayer()?.xui?.GetAtlasByName("ItemIconAtlas", name);
UISpriteData sd = atlas?.GetSprite(name);
Texture tex = atlas?.texture;
if (sd == null || tex == null || tex.width == 0 || tex.height == 0)
{
return;
}
Rect uv = new Rect((float)sd.x / tex.width, 1f - (float)(sd.y + sd.height) / tex.height, (float)sd.width / tex.width, (float)sd.height / tex.height);
// Иконка не растягивается: вписать по большей стороне.
float k = Mathf.Min(_r.width / sd.width, _r.height / sd.height);
Rect fit = new Rect(_r.center.x - sd.width * k / 2f, _r.center.y - sd.height * k / 2f, sd.width * k, sd.height * k);
Color prev = GUI.color;
Color tint = ic.GetIconTint(_iv);
GUI.color = new Color(tint.r, tint.g, tint.b, tint.a * _alpha);
GUI.DrawTextureWithTexCoords(fit, tex, uv);
GUI.color = prev;
}
static void DrawBar(Rect _r, float _frac, Color _fill, string _text) static void DrawBar(Rect _r, float _frac, Color _fill, string _text)
{ {
Color prev = GUI.color; Color prev = GUI.color;
+34 -2
View File
@@ -11,6 +11,8 @@ namespace NecromancerTome
/// - при выходе из одержимости зомби сбрасывает рюкзаком всё, что было в сумке (и на поясе - когда в поясе /// - при выходе из одержимости зомби сбрасывает рюкзаком всё, что было в сумке (и на поясе - когда в поясе
/// появятся предметы); /// появятся предметы);
/// - гибель одержимого - мешок на месте и метка на карте, как у сумки волка. /// - гибель одержимого - мешок на месте и метка на карте, как у сумки волка.
/// ПОЯС (#37, 25.10) - нижний ряд той же сумки: Possession.BeltSlots ячеек под верхним рядом, оружие в руке берётся
/// оттуда (ZombieWeapons). Хранение, выброс мешком и метка - общие с сумкой.
/// Лут (указание 13.10: «раз у них E свободно, подключай лутание») - как у волка: E на ящик или мешок - обыск с /// Лут (указание 13.10: «раз у них E свободно, подключай лутание») - как у волка: E на ящик или мешок - обыск с
/// переносом в сумку, E на предмет на земле - подобрать (WolfBag.Target - сумка зомби, пока она есть). /// переносом в сумку, E на предмет на земле - подобрать (WolfBag.Target - сумка зомби, пока она есть).
/// Только зомби из списка вида от первого лица (FpArms.Allows), не питомцы. Окно - то же, что у сумки волка /// Только зомби из списка вида от первого лица (FpArms.Allows), не питомцы. Окно - то же, что у сумки волка
@@ -28,12 +30,41 @@ namespace NecromancerTome
public const string WaypointName = "necroZombieBagWaypoint"; public const string WaypointName = "necroZombieBagWaypoint";
public static bool WindowOpen; public static bool WindowOpen;
static Bag _bag; static Bag _bag;
/// <summary>Ячеек в ряду: верхний ряд - сумка, нижний - пояс.</summary>
static int _cols;
static string FilePath => Path.Combine(GameIO.GetSaveGameDir(), FileName); static string FilePath => Path.Combine(GameIO.GetSaveGameDir(), FileName);
public static bool Active => _bag != null; public static bool Active => _bag != null;
public static Bag Current => _bag; public static Bag Current => _bag;
/// <summary>Ячейка пояса (0..BeltSlots-1) - нижний ряд сумки; нет сумки - null.</summary>
public static ItemStack Belt(int _i)
{
ItemStack[] items = _bag?.ItemGrid?.items;
int idx = _cols + _i;
return items != null && _i >= 0 && _i < Possession.BeltSlots && idx < items.Length && !items[idx].IsEmpty() ? items[idx] : null;
}
/// <summary>necropossess belt: положить предмет в ячейку пояса (проверка без мыши).</summary>
public static bool DevSetBelt(int _i, ItemStack _s)
{
if (_bag == null || _i < 0 || _i >= Possession.BeltSlots)
{
return false;
}
_bag.SetSlot(_cols + _i, _s);
Save();
return true;
}
/// <summary>Предмет пояса изменился на месте (износ) - окно и файл.</summary>
public static void BeltChanged()
{
_bag?.onBackpackChanged();
Save();
}
/// <summary>Записать после лута (WolfBag.SaveTarget).</summary> /// <summary>Записать после лута (WolfBag.SaveTarget).</summary>
public static void SaveNow() => Save(); public static void SaveNow() => Save();
@@ -64,8 +95,9 @@ namespace NecromancerTome
{ {
Debug.LogWarning("[NecromancerTome] ZombieBag: файл сумки не прочитан (" + e.Message + ") - оставлен как есть"); Debug.LogWarning("[NecromancerTome] ZombieBag: файл сумки не прочитан (" + e.Message + ") - оставлен как есть");
} }
_bag = new Bag(new Vector2i(SlotsFor(_z), 1), XUiC_ItemStack.StackLocationTypes.LootContainer, null); _cols = Mathf.Max(SlotsFor(_z), Possession.BeltSlots);
Debug.Log("[NecromancerTome] ZombieBag: сумка " + _z.EntityName + " - ячеек " + _bag.SlotCount); _bag = new Bag(new Vector2i(_cols, 2), XUiC_ItemStack.StackLocationTypes.LootContainer, null);
Debug.Log("[NecromancerTome] ZombieBag: сумка " + _z.EntityName + " - ячеек " + _bag.SlotCount + ", из них пояс " + Possession.BeltSlots);
} }
/// <summary>Вселение кончилось: всё из сумки - мешком на месте зомби; погиб - ещё и метка на карте.</summary> /// <summary>Вселение кончилось: всё из сумки - мешком на месте зомби; погиб - ещё и метка на карте.</summary>
+339
View File
@@ -0,0 +1,339 @@
using System.Collections.Generic;
using UnityEngine;
namespace NecromancerTome
{
/// <summary>
/// Оружие на поясе одержимого зомби (#37, решения пользователя 25.10). Пояс - две ячейки нижнего ряда сумки зомби
/// (ZombieBag.Belt), туда попадает только то, что зомби нашёл сам или что положили в сумку. Чем зомби владеет -
/// по его типу («зомби строитель сможет пользоваться простыми инструментами: топоры, молотки и т.п. Медсестра -
/// ножами»; список утверждён 25.10, карточка #37). Неумелое или сломанное в выбранной ячейке - кулаки и подсказка.
///
/// Только ближний бой (ItemActionDynamic - всё оружие и инструменты ближнего боя персонажа). Бензопила и бур в игре
/// стрелковые (Ranged, топливо), гвоздомёт - метатель: они придут вместе с огнестрелом (#80).
///
/// Удар - от первого лица, в миг удара рук (FpArms -> Possession.FpImpact): луч из глаз зомби на дальность оружия и
/// ItemActionAttack.Hit, как у персонажа (урон по существам и блокам, кровь, звук, отрубание, износ). Урон - самого
/// предмета с его модами, без перков и баффов зомби (holdingEntity = null): «урон от оружия».
/// </summary>
public static class ZombieWeapons
{
/// <summary>Выключатель (necroarms weapon on|off): выкл - все бьют кулаками, как до #37.</summary>
public static bool Enabled = true;
// Группы предметов: имена групп (не путать с концовками имён предметов, их тоже можно писать в умениях).
const string Knife = "Knife", SmallKnife = "SmallKnife", Club = "Club", Baton = "Baton", Knuckles = "Knuckles", Spear = "Spear",
Sledge = "Sledge", Axe = "Axe", Hammer = "Hammer", Pick = "Pick", Shovel = "Shovel", Wrench = "Wrench", Light = "Light";
static readonly HashSet<string> GroupNames = new HashSet<string>
{ Knife, SmallKnife, Club, Baton, Knuckles, Spear, Sledge, Axe, Hammer, Pick, Shovel, Wrench, Light };
/// <summary>Умения по началу имени класса зомби (как FpArms.Allows; Feral/Radiated/Charged/Infernal - как базовый).
/// Строка - группа или концовка имени предмета игры (StoneAxe подходит и к TazasStoneAxe).</summary>
static readonly Dictionary<string, string[]> Skills = new Dictionary<string, string[]>
{
{ "zombieUtilityWorker", new[] { Hammer, Pick, Shovel, Axe, Sledge, Wrench, "ImpactDriver", "Auger", "Nailgun" } },
{ "zombieLumberjack", new[] { Axe, "Chainsaw", Sledge } },
{ "zombieTomClark", new[] { Shovel, Axe, Spear } },
{ "zombieJanitor", new[] { Wrench, Shovel, Light, Club } },
{ "zombieNurse", new[] { Knife } },
{ "zombieLab", new[] { Knife, "StunBaton", "PlasmaBaton" } },
{ "zombieMaleHazmat", new[] { Shovel, Light, Wrench } },
{ "zombieSoldier", new[] { Knife, Baton, Spear } },
{ "zombieBiker", new[] { Knuckles, Club, "PipeBaton", Wrench } },
{ "zombieInmate", new[] { "BoneKnife", Knuckles, "PipeBaton" } },
{ "zombieYo", new[] { Knuckles, "BaseballBat", "PipeBaton" } },
{ "zombieSkateboarder", new[] { "BaseballBat", "PipeBaton" } },
{ "zombieBowler", new[] { Club, Sledge } },
{ "zombieBoe", new[] { Club, Knuckles, "StoneAxe", "StoneShovel" } },
{ "zombieJoe", new[] { Club, Knuckles, "StoneAxe", "StoneShovel" } },
{ "zombieMoe", new[] { Sledge, Club } },
{ "zombieSteve", new[] { Club, "StoneAxe", "StoneShovel" } },
{ "zombieBusinessMan", new[] { "BaseballBat", SmallKnife } },
{ "zombieBurnt", new[] { "Torch", Club } },
{ "zombieDarlene", new[] { Knife, "WoodenClub" } },
{ "zombieArlene", new[] { Knife, "BaseballBat" } },
{ "zombieMarlene", new[] { Knife, "BaseballBat" } },
{ "zombiePartyGirl", new[] { "CandyKnife", "CandyClub", "BaseballBat" } },
};
static readonly Dictionary<int, HashSet<string>> _groups = new Dictionary<int, HashSet<string>>();
static bool Tag(ItemClass _ic, string _tag) => _ic.ItemTags.Test_AnySet(FastTags<TagGroup.Global>.Parse(_tag));
/// <summary>Группы предмета: у предметов игры - по имени, у предметов других модов - по тегам (как у игры).</summary>
static HashSet<string> Groups(ItemClass _ic)
{
if (_groups.TryGetValue(_ic.Id, out HashSet<string> g))
{
return g;
}
g = new HashSet<string>();
string n = _ic.Name ?? "";
bool motor = Tag(_ic, "motorTool") || Tag(_ic, "nailgun");
if (n.StartsWith("meleeWpnBlade") || Tag(_ic, "knife") || Tag(_ic, "machete"))
{
g.Add(Knife);
if (!n.Contains("Machete") && !Tag(_ic, "machete"))
{
g.Add(SmallKnife);
}
}
if (n.StartsWith("meleeWpnClub") || Tag(_ic, "club"))
{
g.Add(Club);
}
if (n.StartsWith("meleeWpnBaton") || Tag(_ic, "batonJunkie") || Tag(_ic, "stunBaton"))
{
g.Add(Baton);
}
if (n.StartsWith("meleeWpnKnuckles") || Tag(_ic, "knuckleSkill"))
{
g.Add(Knuckles);
}
if (n.StartsWith("meleeWpnSpear") || Tag(_ic, "spearSkill"))
{
g.Add(Spear);
}
if (n.StartsWith("meleeWpnSledge") || Tag(_ic, "sledge"))
{
g.Add(Sledge);
}
if (!motor && (n.StartsWith("meleeToolAxe") || Tag(_ic, "axe")))
{
g.Add(Axe);
}
if (n.Contains("ClawHammer") || !motor && Tag(_ic, "repairTool") && !Tag(_ic, "axe"))
{
g.Add(Hammer);
}
if (n.StartsWith("meleeToolShovel") || Tag(_ic, "shovel"))
{
g.Add(Shovel);
}
else if (!motor && (n.StartsWith("meleeToolPick") || Tag(_ic, "miningTool") && !Tag(_ic, "axe")))
{
g.Add(Pick);
}
if (!n.Contains("ImpactDriver") && (n.StartsWith("meleeToolSalvage") || Tag(_ic, "salvageTool")))
{
g.Add(Wrench);
}
if (n.Contains("Flashlight") || n.Contains("Torch") || Tag(_ic, "heldTorch"))
{
g.Add(Light);
}
_groups[_ic.Id] = g;
return g;
}
static string[] SkillsOf(EntityAlive _z)
{
if (_z == null || !EntityClass.list.TryGetValue(_z.entityClass, out EntityClass cls) || cls?.entityClassName == null)
{
return null;
}
foreach (KeyValuePair<string, string[]> kv in Skills)
{
if (cls.entityClassName.StartsWith(kv.Key))
{
return kv.Value;
}
}
return null;
}
/// <summary>Зомби этого типа владеет предметом.</summary>
public static bool Knows(EntityAlive _z, ItemClass _ic)
{
string[] skills = SkillsOf(_z);
if (skills == null || _ic == null)
{
return false;
}
HashSet<string> g = Groups(_ic);
string n = _ic.Name ?? "";
foreach (string s in skills)
{
if (GroupNames.Contains(s) ? g.Contains(s) : n.EndsWith(s))
{
return true;
}
}
return false;
}
public enum Fit { Fists, Ok, Unknown, Broken }
/// <summary>Чем бьёт зомби с этим предметом в выбранной ячейке.</summary>
public static Fit Check(EntityAlive _z, ItemValue _iv)
{
if (_iv == null || _iv.IsEmpty() || _iv.ItemClass == null)
{
return Fit.Fists;
}
ItemClass ic = _iv.ItemClass;
if (!(ic.Actions != null && ic.Actions.Length > 0 && ic.Actions[0] is ItemActionDynamic) || !Knows(_z, ic))
{
return Fit.Unknown;
}
if (_iv.MaxUseTimes > 0 && _iv.UseTimes >= _iv.MaxUseTimes)
{
return Fit.Broken;
}
return Fit.Ok;
}
/// <summary>Оружие в руке одержимого сейчас: выбранная ячейка пояса, если зомби им владеет и оно цело; иначе null.</summary>
public static ItemValue Current(EntityAlive _z)
{
if (!Enabled || !ZombieBag.Active)
{
return null;
}
ItemStack s = ZombieBag.Belt(Possession.BeltSlot);
return s != null && Check(_z, s.itemValue) == Fit.Ok ? s.itemValue : null;
}
/// <summary>Пауза между ударами этим оружием - как у персонажа (удары в минуту).</summary>
public static float SwingTime(ItemValue _iv)
{
float apm = EffectManager.GetValue(PassiveEffects.AttacksPerMinute, _iv, 60f, null, null, _iv.ItemClass.ItemTags);
return apm > 1f ? 60f / apm : 1f;
}
/// <summary>Удар оружием в миг удара рук: луч из глаз зомби (камера одержимости) на дальность оружия. Мимо - false
/// (звука нет, как у персонажа - замах в воздух).</summary>
public static bool Strike(World _world, EntityAlive _z, Ray _ray)
{
ItemStack stack = ZombieBag.Belt(Possession.BeltSlot);
ItemValue iv = stack?.itemValue;
if (iv == null || Check(_z, iv) != Fit.Ok || !(iv.ItemClass.Actions[0] is ItemActionDynamic act))
{
return false;
}
FastTags<TagGroup.Global> tags = ItemActionAttack.PrimaryTag | iv.ItemClass.ItemTags;
float range = EffectManager.GetValue(PassiveEffects.MaxRange, iv, act.RangeDefault, null, null, tags);
float blockRange = EffectManager.GetValue(PassiveEffects.BlockRange, iv, range, null, null, tags);
float sphere = act.SphereRadius;
float dist = Mathf.Max(range, blockRange) + sphere;
_ray.origin -= sphere * _ray.direction;
// Свои коллайдеры зомби - мимо луча (как GetExecuteActionTarget у игры). Камера одержимости - в голове зомби:
// если луч всё же задел его самого (тест 25.10: все удары «мимо» вплотную), - продолжить луч за ним.
int layer = _z.GetModelLayer();
_z.SetModelLayer(2);
WorldRayHitInfo hit = null;
float passed = 0f;
try
{
for (int i = 0; i < 4 && hit == null; i++)
{
Ray ray = new Ray(_ray.origin + _ray.direction * passed, _ray.direction);
EntityAlive target = null;
bool any = Voxel.Raycast(_world, ray, dist - passed, -538767389, 128, sphere);
if (any)
{
target = ItemActionAttack.GetEntityFromHit(Voxel.voxelRayHitInfo) as EntityAlive;
}
if (target == null)
{
any = Voxel.Raycast(_world, ray, dist - passed, -538488845, 128, sphere);
target = any ? ItemActionAttack.GetEntityFromHit(Voxel.voxelRayHitInfo) as EntityAlive : null;
}
if (!any)
{
break;
}
if (target == _z)
{
passed += Mathf.Sqrt(Voxel.voxelRayHitInfo.hit.distanceSq) + 0.05f;
continue;
}
hit = Voxel.voxelRayHitInfo.Clone();
}
}
finally
{
_z.SetModelLayer(layer);
}
if (hit?.tag == null)
{
string phys = "ничего";
if (Physics.Raycast(_ray.origin - Origin.position, _ray.direction, out RaycastHit ph, 5f, -5, QueryTriggerInteraction.Ignore))
{
phys = ph.transform.name + " слой " + LayerMask.LayerToName(ph.transform.gameObject.layer) + " в " + ph.distance.ToString("F2");
}
Debug.Log("[NecromancerTome] ZombieWeapons: луч ничего не задел (дальность " + dist.ToString("F1") + (passed > 0f ? ", сквозь себя " + passed.ToString("F1") : "") +
"), луч " + _ray.origin.ToString("F2") + " -> " + _ray.direction.ToString("F2") + ", голова " + _z.getHeadPosition().ToString("F2") + ", физика: " + phys);
return false;
}
bool entity = hit.tag.StartsWith("E_");
float d = Vector3.Distance(_ray.origin, hit.hit.pos);
if (entity ? d > range + sphere : d > blockRange + sphere)
{
Debug.Log("[NecromancerTome] ZombieWeapons: далеко - " + hit.tag + " в " + d.ToString("F1") + " м, достаёт " + (entity ? range : blockRange).ToString("F1"));
return false;
}
if (entity && ItemActionAttack.GetEntityFromHit(hit) is EntityAlive e && (e == _z || e is EntityPlayer || SnakeControl.IsNecroEntity(e)))
{
Debug.Log("[NecromancerTome] ZombieWeapons: под ударом свой - " + e.EntityName);
return false; // хозяина и его нежить одержимый не бьёт - как кулаками (MeleeAim)
}
BlockValue bv = ItemActionAttack.GetBlockHit(_world, hit);
float blockDamage = entity || bv.isair ? 0f : act.GetDamageBlock(iv, bv, null, 0);
float entityDamage = act.GetDamageEntity(iv, null, 0);
string tagHit = hit.tag;
FastTags<TagGroup.Global> dTags = tags;
if (tagHit == "E_BP_Head")
{
dTags |= FastTags<TagGroup.Global>.Parse("head");
}
float dismember = EffectManager.GetValue(PassiveEffects.DismemberChance, iv, 0f, null, null, dTags);
ItemActionAttack.AttackHitInfo details = new ItemActionAttack.AttackHitInfo { WeaponTypeTag = ItemActionAttack.MeleeTag };
ItemActionAttack.Hit(hit, _z.entityId, act.DamageType, blockDamage, entityDamage, 1f, 1f, 0f, dismember,
iv.ItemClass.MadeOfMaterial.SurfaceCategory, new DamageMultiplier(), new List<string>(), details, 1, 0, 0f, null,
act.ToolBonuses, ItemActionAttack.EnumAttackMode.RealNoHarvesting, act.HitSoundOverrides, -1, iv);
// Износ - как у персонажа (ItemActionDynamic.hitTarget); сломалось - звук, дальше кулаки.
if (iv.MaxUseTimes > 0)
{
iv.UseTimes += EffectManager.GetValue(PassiveEffects.DegradationPerUse, iv, 1f, null, null, tags) * ItemAction.ItemDegradationModifier;
if (iv.UseTimes >= iv.MaxUseTimes)
{
Audio.Manager.BroadcastPlay(_z, "itembreak");
Debug.Log("[NecromancerTome] ZombieWeapons: " + iv.ItemClass.Name + " сломалось");
}
ZombieBag.BeltChanged();
}
Debug.Log("[NecromancerTome] ZombieWeapons: удар " + iv.ItemClass.Name + " по " + (entity ? tagHit : bv.Block?.GetBlockName()) +
", урон " + (entity ? entityDamage : blockDamage).ToString("F0") + (details.bKilled ? ", убит" : ""));
return true;
}
/// <summary>Подсказка при смене выбранного предмета: неумелое или сломанное - кулаки.</summary>
public static void Hint(EntityPlayerLocal _player, EntityAlive _z, ItemValue _iv)
{
Fit f = Enabled ? Check(_z, _iv) : Fit.Fists;
if (f == Fit.Unknown)
{
GameManager.ShowTooltip(_player, string.Format(Localization.Get("necroZombieWeaponUnknown"), _iv.ItemClass.GetLocalizedItemName()));
}
else if (f == Fit.Broken)
{
GameManager.ShowTooltip(_player, string.Format(Localization.Get("necroZombieWeaponBroken"), _iv.ItemClass.GetLocalizedItemName()));
}
}
/// <summary>necroarms weapon: что умеет текущий одержимый и что в его поясе.</summary>
public static string Dump(EntityAlive _z)
{
string[] sk = SkillsOf(_z);
string s = "оружие " + (Enabled ? "вкл" : "ВЫКЛ") + ", умения: " + (sk != null ? string.Join(", ", sk) : "нет");
for (int i = 0; i < Possession.BeltSlots; i++)
{
ItemStack st = ZombieBag.Belt(i);
s += "\n ячейка " + (i + 1) + (i == Possession.BeltSlot ? " (выбрана)" : "") + ": " +
(st == null || st.IsEmpty() ? "пусто" : st.itemValue.ItemClass.Name + " [" + string.Join(",", Groups(st.itemValue.ItemClass)) + "] - " + Check(_z, st.itemValue));
}
return s;
}
}
}
Binary file not shown.
Binary file not shown.