В этой теме предлагаю постить "странности" скриптов, различные тонкости эвентов, функций и т.п.
Локальные переменные на итемах из инвентаря игрока сохраняются в файлеДля меня это было откровением и нашел я это случайно. Если выставить локальную переменную на итем - она сохраняется в .bic файле игрока, что иногда очень удобно (особенно вместе с настоящими nodrop-итемами, многие вещи так делать гораздо проще, чем юзать БД)
Событие модуля OnAcquireItem вызывается при _входе_ игрока на шард для _каждой_ вещи из его инвентаря Тоже самое и с событием OnPlayerEquipItem - оно вызывается при входе игрока на шард для каждой одетой на персонажа вещи
В событие OnClientLeave не работают некоторые функции для работы с объектом игрокаGetPlayername, GetPCIPAddress и некоторые другие. Но локальные переменные читать можно, так что если вам нужны эти данные - занесите их в переменные при входе игрока на шард
Событие OnUnAcquireItem вызывается при _попытке_ продажи итема в магазин, даже если эта вещь не покупаетсяЕсли нет денег у НПС-покупателся, возможно и на plot\stolen то же самое будет. Вероятно что и OnAcquireItem будет вызываться.
Бага функции TakeGoldFromCreature() Надо дополнительно проверять текущее действие, if (GetCurrentAction(oPC) == ACTION_DROPITEM) {чит}
Т.е. можно задать действие "выложить деньги на землю", некоторое время сервер будет считать, что все деньги по прежнему у вас. если сервер в это время отнимает деньги у игрока функцией TakeGoldFromCreature() - при небольшом навыке на землю упадет _вся_ сумма, не учитывая те, что забрали функцией. Баг особо опасен для банков

(так делались первые миллионы на Эксизе, когда только появился первый банк)
Lazy Ranma
Jun 25 2004, 08:35
Цитата
В событие OnClientLeave не работают некоторые функции для работы с объектом игрока
Самое интересное, что при этом GetIsPC() возвращает FALSE. Еще состояние "не до конца вышедшего" игрока можно застать не только из OnClientLeave, но из любого другого скрипта, выполняющегося примерно в то же время.
Иногда полезно читать лексикон
При работе функции DestroyObject()объект уничтожится только когда завершится скрипт, вызвавший эту функцию, а до этого момента объект все еще существует. Т.е. получается функция только помечает объект на удаление, а удаляется он сборщиком мусора после завершения скрипта.
Аваддон
Jul 5 2004, 20:12
Скорее не баг, а недодуманность скриптеров в Стандартном Крафте.
Проверял на версии 1.62.
Когда у вас есть материал для ловушек, напрмер сустав скелета(Skeleton's Knuckle)
Жмем на Crafting Skills, в диалоге выбираете Craft Traps. В следующем диалоге доступна маленькая ловушка с негативной энергией. Не закрывая диалог открываем инвентарь и выкладываем сустав скелета. Жмем - сделать маленькую негативную ловуку - вуаля: Сустав лежит рядом с вами на земле а у вас в инвентаре (если был прокачан скилл crafting traps) свежая ловушечка. Ай да раздолье плутам

))
З.Ы. С оружием и броней такое уже не прокатило
Это легко лечится. Ставить повторную проверку компонентов на фразе, что перед крафтом.
Очень веселая недокументировання багафича со встроенной БД.
Был баг непонятный, долго копал - что за фигня, в БД одно значение, а читает и возвращает другое.
Стал разбираться.
Вот что оказалось:
SetCampaignInt(string, string, int, object)
Neverwinter Script Source |
void SetCampaignInt( string sCampaignName, string sVarName, int nInt, object oPlayer = OBJECT_INVALID ); |
Переменная sVarName - это имя поля в базе данных.
Но если оно оканчивается на цифру - начинаются сюрпризы.
Скажем если мы читаем из БД три переменных:
`xp_1`
`xp_11`
`xp_111`
а в самой базе есть только одна запись - `xp_11`= 10.
При чтение `xp_1` вернет значение переменой `xp_11`, 10,
При чтение `xp_11` тоже вернет значение переменой `xp_11`, 10,
При чтение `xp_111` вернет 0.
Блеск, правда?

Если добавить в конце переменных любой символ - все работает нормально.
Вот такие веселые грабли.
Цитата(Valleo @ Jul 27 2004, 22:05)
Очень интерестная заметка.
Иногда нам нужно, чтобы из функции возвращался стек значений.
Например функция, которая подсчитывает количество членов в партии, находит средний уровень партии. Нам бы нужно и то число, и то, но мы недолго думая создаем функцию, которая заново пересчитает всех членов партии.
Я же нашел более интерестный способ =)
Функции умеют возвращать структуры:
Neverwinter Script Source |
struct stTest1 { int i1,i2; float f1,f2; string s1,s2; vector v1,v2; };
struct stTest1 TestFunction(int Param1) { struct stTest1 swTestResult; swTestResult.i1 = 1111; swTestResult.f1 = 111.111f; swTestResult.s1 = "dsfsafsf"; return swTestResult; } |
Пользуйся, гораздно удобнее.
Интересный прикол в событии onPhysicalDamage. Есть функция, GetLastWeaponUsed().
Если в руках оружие - возвращает оружие.
Если нет - возвращает наручни или перчатки (!)
Если и их нет - то возвращает OBJECT_INVALID
Логически - все верно, но не ожидал я такого от биоваре
Не сохраняйте location в БД.
Сохраняется она "правильно" даже в стандартной БД, т.е. ее перезапись не ведет к увеличению размера БД.
НО, сам формат...
Запись выглядит примерно так:
AREA_ID, X,Y,Z,Facing
Проблема с AREA_ID. Он выставляется то ли еще в тулсете, то ли при распаковке и часто меняется при добавлении\удалении локаций. Т.е. после этого location указывает ХЗ куда.
Если очень нужно хранить - задавайте руками (в тулсете, кнопка variable) локальную переменную для локации с номером или же задавайте уникальные теги локациям (не проверял) и с ними работайте.
Если есть возможность обойтись вейпоинтом - так и делайте.
Вообще легче всего сохранять локу на какой нить плот-итем в инвентаре, который невозможно выкинуть и т.п.
Или же делать LocationToString а потом наоборот.
Цитата(Valleo @ Aug 9 2004, 15:24)
Вообще легче всего сохранять локу на какой нить плот-итем в инвентаре, который невозможно выкинуть и т.п.
Или же делать LocationToString а потом наоборот.
Да, если еще написать обратную функцию StringToLocation и следить за уникальностью тегов локаций в модуле - то достаточно просто и надежно получится.
Valleo
Aug 10 2004, 08:15
StringToLocation
Neverwinter Script Source |
location StringToLocation(string sLocation) { location lReturnValue; object oArea; vector vPosition; float fOrientation, fX, fY, fZ;
int iPos, iCount; int iLen = GetStringLength(sLocation);
if (iLen > 0) { iPos = FindSubString(sLocation, "#AREA#") + 6; iCount = FindSubString(GetSubString(sLocation, iPos, iLen - iPos), "#"); oArea = GetObjectByTag(GetSubString(sLocation, iPos, iCount));
iPos = FindSubString(sLocation, "#POSITION_X#") + 12; iCount = FindSubString(GetSubString(sLocation, iPos, iLen - iPos), "#"); fX = StringToFloat(GetSubString(sLocation, iPos, iCount));
iPos = FindSubString(sLocation, "#POSITION_Y#") + 12; iCount = FindSubString(GetSubString(sLocation, iPos, iLen - iPos), "#"); fY = StringToFloat(GetSubString(sLocation, iPos, iCount));
iPos = FindSubString(sLocation, "#POSITION_Z#") + 12; iCount = FindSubString(GetSubString(sLocation, iPos, iLen - iPos), "#"); fZ = StringToFloat(GetSubString(sLocation, iPos, iCount));
vPosition = Vector(fX, fY, fZ);
iPos = FindSubString(sLocation, "#ORIENTATION#") + 13; iCount = FindSubString(GetSubString(sLocation, iPos, iLen - iPos), "#"); fOrientation = StringToFloat(GetSubString(sLocation, iPos, iCount));
lReturnValue = Location(oArea, vPosition, fOrientation); }
return lReturnValue; } |
LocationToString
Neverwinter Script Source |
string LocationToString(location lLocation) { object oArea = GetAreaFromLocation(lLocation); vector vPosition = GetPositionFromLocation(lLocation); float fOrientation = GetFacingFromLocation(lLocation); string sReturnValue;
if (GetIsObjectValid(oArea)) sReturnValue = "#AREA#" + GetTag(oArea) + "#POSITION_X#" + FloatToString(vPosition.x) + "#POSITION_Y#" + FloatToString(vPosition.y) + "#POSITION_Z#" + FloatToString(vPosition.z) + "#ORIENTATION#" + FloatToString(fOrientation) + "#END#";
return sReturnValue; } |
Аваддон
Dec 5 2004, 23:42
Интересная фишка тут мне вечерком подвернулась, хотя может и старо, но пока нигде не видел.
Сначала маялся чтобы на "OnHeartbeat" в модуле на игрока выполнялся определенный скрипт. Можно конечно замутить GetFirstPC и GetNextPC, в таком случае сервер будет перебирать всех игроков.
Почитал Lексикон и вот что нашел в заметках:
QUOTE |
...the PC doesn't have an OnHeartbeat event, if you create a script called "default", it will automatically trigger every PC heartbeat... |
Первожу на русский. Дело в том, что у игрока нет эвента OnHearbeat, НО(!) если вы создадите скрипт и назовете его "
default" он будет автоматически вписан в OnHeartBeat игрока.
Проверил - работает...
Чудеса да и только, товарищи...
Аваддон Да ну нафиг!!! Это мега фича!! Сенки!
стоп..а если игроков много...те у всех одинаковый ХБ?
Аваддон
Dec 7 2004, 16:42
Лекс не понял твоего вопроса... У каждого игрока на HB будет включаться этот скрипт
все, те одинаковый хб. Но для сингла это не важно. Отличная фича!
Lexey aka Hawk
Dec 8 2004, 14:12
Зато интересно для шардостроителей.
я имел ввиду что не важно, что для всех одинаковый. :-) А синглам эта штука тоже будет полезна.. :-)
QUOTE |
Если очень нужно хранить - ... задавайте уникальные теги локациям (не проверял) и с ними работайте. |
QUOTE |
Вообще легче всего сохранять локу на какой нить плот-итем в инвентаре, который невозможно выкинуть и т.п. Или же делать LocationToString а потом наоборот. |
QUOTE |
Да, если еще написать обратную функцию StringToLocation и следить за уникальностью тегов локаций в модуле - то достаточно просто и надежно получится. |
Именно так и сделано на Сиале - так что способ проверенный
Аваддон
Dec 12 2004, 23:37
В поздних версиях тулсета ввели функцию - поиск по тексту (предметов, существ, плейсов). Но в тайлах такого поиска не сделали. Я работая с большими тайлами, порой забываю в какой группе какой объект находится. Но найти нужный тайл через его название можно.
Заходите на закладку там где этот поиск возможен. Набираете текст, переходите в закладку тайлов и нажимаем F3
QUOTE (Аваддон @ Dec 5 2004, 23:42) |
Интересная фишка тут мне вечерком подвернулась, хотя может и старо, но пока нигде не видел. Сначала маялся чтобы на "OnHeartbeat" в модуле на игрока выполнялся определенный скрипт. Можно конечно замутить GetFirstPC и GetNextPC, в таком случае сервер будет перебирать всех игроков. Почитал Lексикон и вот что нашел в заметках:
Первожу на русский. Дело в том, что у игрока нет эвента OnHearbeat, НО(!) если вы создадите скрипт и назовете его "default" он будет автоматически вписан в OnHeartBeat игрока. Проверил - работает... Чудеса да и только, товарищи... |
Супер, весьма полезно, хотя тут главное не злоупотреблять
QUOTE (Аваддон @ Dec 5 2004, 23:42) |
Интересная фишка тут мне вечерком подвернулась, хотя может и старо, но пока нигде не видел. Сначала маялся чтобы на "OnHeartbeat" в модуле на игрока выполнялся определенный скрипт. Можно конечно замутить GetFirstPC и GetNextPC, в таком случае сервер будет перебирать всех игроков. Почитал Lексикон и вот что нашел в заметках:
Первожу на русский. Дело в том, что у игрока нет эвента OnHearbeat, НО(!) если вы создадите скрипт и назовете его default он будет автоматически вписан в OnHeartBeat игрока. Проверил - работает... Чудеса да и только, товарищи...  |
Еще интереснее, если открыть в какой-нибудь программе файл игрока, например, в nwn explorer.
Там есть поля ScriptHeartbeat, ScriptOnNotice, ScriptSpellAt, ..Attacked, ..Damaged, ..Disturbed, ..EndRound, ..Dialogue, ..Spawn, ..Rested, ..Death, ..UserDefine, ..OnBlocked. И напротив всех них стоит (что бы вы думали?) - default ! Учитывая наличия разных плагинов к nwnx типа leto и подобных, а так же возможности редактировать эти поля вручную ..

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

Возможно, конечно, что эти значения ни на что не влияют, и нвн все равно будет привязывать только скрипт с именем default
Добавлено Проверил, эти поля появляются только если зайти в модуль (т.е. не сразу после создания), и перезаписываются нвн, так что их подредактировать не удастся. А скрипт default исполняется еще как минимум при смерти игрока.
QUOTE (_kaa_ @ Jun 24 2004, 18:14) |
Локальные переменные на итемах из инвентаря игрока сохраняются в файле Для меня это было откровением и нашел я это случайно. Если выставить локальную переменную на итем - она сохраняется в .bic файле игрока, что иногда очень удобно (особенно вместе с настоящими nodrop-итемами, многие вещи так делать гораздо проще, чем юзать БД) |
не смог добиться такого эффекта

Вешаю в игре локалку на айтем, сохраняю игрока, выхожу. Захожу сохраненным, локалки нету. (делал в сингл-моде)
Или это только под сервером работает?
----------
разобрался: работает только под нвнсервером.
virusman
Sep 10 2005, 10:15
Еще одна, на этот раз неприятная, фича:
После вызова SetItemStackSize стираются локальные переменные на предмете.
QUOTE (virusman @ Sep 10 2005, 10:15) |
Еще одна, на этот раз неприятная, фича: После вызова SetItemStackSize стираются локальные переменные на предмете. |
Ничего подобного не обнаружил, специально сделал тест-модуль и проверил.
Даже если вещь stackble, локальные переменные нормально живут. Может ты в сингле проверял?
С локальными переменнымы есть одна багафича, если они stackable. Если их разделить в игре на меньшие части, то, что останется - будет со старыми локальными переменными, а отделенная часть потеряет все переменные. И это очень неприятно
угу...таким образом единственная инфа, которую можно сохранить у stackable итемов при обьеденении\разделении, и которую при нужде скриптами можно менять ( в разумных пределах

), это чардж...
QUOTE (_kaa_ @ Jun 26 2004, 10:44) |
При работе функции DestroyObject()объект уничтожится только когда завершится скрипт, вызвавший эту функцию, а до этого момента объект все еще существует. Т.е. получается функция только помечает объект на удаление, а удаляется он сборщиком мусора после завершения скрипта. |
С этим есть большая проблема, выпадение лута игрока.
Обычный механизм работы скрипта с выпадением лута:
1. пройтись по всему инвентарю
2. скопировать все, что должно упасть туда, куда должно упасть (copyitem с сохранением локальных переменных если нужно)
3. удалить все, что скопировали
...
Если вещей много, сервер нагружен, а игрок злобный читер или просто не повезло - вполне может быть ситуация, когда при выходе игрока в этот момент происходить дублирование вещей. Т.е. вещи скопировались, пометились на удаление, но скрипт до конца не успел дойти, игрок уже вышел.
Возникает вопрос - как с этим бороться?

Можно вместе с удалением вешать на вещь локальную переменную, при входе игрока такие помеченные вещи просто удалять.
Можно попробовать копирование\удаление одной вещи вынести в отдельный скрипт и вызывать его, возможно тогда будет корректно удаляться (надо проверять, только сложно).
Может кто боролся с этим и может что посоветовать?
ps. Хотя я сам полностью за те варианты, где лут не выпадает при смерти от моба и т.п. Что само по себе не исключает смерть от рук ДМ-а в квесте с полной потерей персонажа и вещей
у меня лут падает всегда и везде, но с подобной проблемой не сталкивался - скрипт дропа лута всегда до конча срабатывал...
единственный косяк случался, если игрок умирал в момент перехода из локи в локу... потому единственный фикс, который я делал - проверка if(oDropBag!=OBJECT_INVALID)...
такая проверка не только фиксит возможный дюп, но и отучает игроков убегать от мобов в переходы
virusman
Sep 26 2005, 17:44
_kaa_
Да, точно, это у меня глюк был.
2_advanced
Sep 27 2005, 13:27
типа того:
Neverwinter Script Source |
copy = CopyItem(item,..., copy vars = true!); SetLocalInt(item, "deleted", 1); DestroyObject(item, 0.1f); |
на входе проверять на итемы с "deleted"=1 и удалять их..
наковырял вот неприятность, но она уже из прошлого - до версии 1.66.8074(конкретно у меня баговала 8067). есть предположение, что сие относится только к clcompile.exe. баг из прошлого - он существовал до 1.30 и каким-то образом дожил в clcompile до недавних дней. есть правда отличие - там глючило на локальных переменных - тут на константах.
баг: неправильное восстановление стека скриптовой виртуальной машины на break/continue инструкциях, если в блоке вводилась локальная константа. например:
Neverwinter Script Source |
string sSome = "test "; switch (some) { case 1: sSome += "BUG"; break; // <-- тут скрипт завершается либо с тихой сапой, либо с сообщением STACK UNDERFLOW } |
в версии 1.66.8074, повторюсь, уже все нормально...
virusman
Oct 17 2005, 12:08
To:
dumboЯ ж говорил.

В чейнджлисте 1.66 как раз был написано про этот фикс.
virusman
Mar 14 2008, 01:05
В функциях Action(Force)MoveToLocation и ActionMoveAwayFromLocation существует утечка памяти. Если ваш модуль "течёт" - обратите внимание на вызовы этих функций.
denis0k
May 1 2008, 00:22
Веселые грабельки в StartNewModule().
Функция срабатывает только при наличии игроков на сервере (есть мысль, что с опцией restart module then empty работает всегда, но опция популярностью не пользуется). Т.е. единственное ее применение в pw - рестарт с дм-ванда. А мне так хотелось не извращаться со сменой модулей "снаружи" (их два), а подгружать их друг из друга
Но самое веселое тут то, что однажды запущенная без игроков функция "ломается" совсем и отказывается работать даже с игроками. Просто ничего не делает, без ошибок в логе.
P.S. То, что работает не всегда как минимум на двух разных конфигурациях, - 100%. Саму закономерность вылавливал двое суток, но там все так хитро, что мог чуть ошибиться.
Ilerien
May 1 2008, 11:05
У меня в тестовом модуле StartNewModule() работать отказалась, когда игрок нажал на рычаг рестарта и вышел, а функция вызывалась секунды через 3 после юза рычага. Что подтверждает вышеизложенное

Цитата
А мне так хотелось не извращаться со сменой модулей "снаружи" (их два), а подгружать их друг из друга
А если рестартить на OnClientEnter модуля при определённых условиях? Хотя тогда придётся сильно извращаться со временем...
Функции
ApplyEffectToObject,
ApplyEffectAtLocation не работают, будучи вызванными из невалидного обжекта.
Neverwinter Script
DelayCommand(12.0f, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HARM), GetLastUsedBy()));
DelayCommand(18.0f, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_FIREBALL), GetStartingLocation()));
DestroyObject(OBJECT_SELF);
При юзе обжекта никаких визуальных эффектов не наблюдается.
denis0k
May 1 2008, 12:34
Цитата
А если рестартить на OnClientEnter модуля при определённых условиях?
Изврат. Я написал нехитрую программулинку, которая на основе ряда данных и содержимого логов сама перезапускает сервер с нужным модулем.
Цитата
Функции ApplyEffectToObject, ApplyEffectAtLocation не работают, будучи вызванными из невалидного обжекта.
Не совсем так

Все экшены выполняются каким-то объектом, с которого скрипт и запускается. Если объект уничтожается, то ничего и не сработает. Если скрипт стоит в слоте объекта, то запускается от имени этого объекта, если это спелл - от игрока/непися и т.п. Т.е. это тот объект, который OBJECT_SELF в скрипте.
Достаточно сделать assign на другой объект (который будет существовать 18 сек) и твой скрипт заработает. Кстати и тут есть фишка - внутри assign-а надо аккуратно юзать тот же OBJECT_SELF, т.к. константа подменяется на тот объект, на который ты действие перекинул.
Ilerien
Jul 2 2008, 02:20
Грабли понятные и логичные, он, тем не менее, я на них в своё время наступил

Функция GetGoldPieceValue чувствительна к флагам на итеме. Если Выставлен флаг plot - вернёт 0, если нет, но не выставлен identified - вернёт цену базового итема. Нормальное значение функция возвращает только, если убран plot и выставлен identified.
Ilerien
Jan 4 2009, 15:10
Забавная и не очень приятная штука, с которой я столкнулся первый раз. Если менять свойства оружия при его надевании (onEquipItem), движок упорно использует частично старые свойства. Пример: не так давно я писал систему, которая реализует выставление свойств OnHitSpellCast Greater Dispelling & Enhancement Bonus +5 и убирает свойство Holy Avenger (поскольку биовари в 1.69 холик убили). Самое весёлое, что движок игнорит +5, но остаётся дамаг 1д6 дивайном от зла, который встроен в Holy Avenger. Заделеивание добавления свойств ничего не даёт.
Ilerien
Apr 20 2009, 16:07
Неприятная фича - добавленная в версии 1.69 функция SetDescription плохо работает в комбинации с CopyItem[AndModify]. Если сначала вызвать SetDescription(), а потом скопировать предмет - описание станет дефолтным для данного объекта, как будто мы вызвали SetDescription с пустой строкой. Для готового модуля я написал небольшой Java-класс, который заменяет все вхождения вызовов функций копирования во всех модульных скриптах на вызовы кастомных вида:
Neverwinter Script
object FR_CopyItem(object oItem, object oTargetInventory=OBJECT_INVALID, int bCopyVars=FALSE)
{
object oCopy = CopyItem(oItem, oTargetInventory, bCopyVars);
SetDescription(oCopy, GetDescription(oItem));
return oCopy;
}
Если нужно, могу выложить исходник.
Laajin
Aug 11 2011, 14:35
Цитата
Функция срабатывает только при наличии игроков на сервере (есть мысль, что с опцией restart module then empty работает всегда, но опция популярностью не пользуется)
.
Попробовал поставить эту галочку ради интереса. Сервер перезагружает модуль сам каждый раз при выходе последнего игрока, без использования функций скриптов. Кроме того сервер сам не сбрасывается, а только обновляется содержимое локаций + наверное, стираются данные о игроках и локалки. Процесс очень быстрый раз- и все)
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста,
нажмите сюда.