В этой теме предлагаю постить "странности" скриптов, различные тонкости эвентов, функций и т.п.
Локальные переменные на итемах из инвентаря игрока сохраняются в файле
Для меня это было откровением и нашел я это случайно. Если выставить локальную переменную на итем - она сохраняется в .bic файле игрока, что иногда очень удобно (особенно вместе с настоящими nodrop-итемами, многие вещи так делать гораздо проще, чем юзать БД)
Событие модуля OnAcquireItem вызывается при _входе_ игрока на шард для _каждой_ вещи из его инвентаря
Тоже самое и с событием OnPlayerEquipItem - оно вызывается при входе игрока на шард для каждой одетой на персонажа вещи
В событие OnClientLeave не работают некоторые функции для работы с объектом игрока
GetPlayername, GetPCIPAddress и некоторые другие. Но локальные переменные читать можно, так что если вам нужны эти данные - занесите их в переменные при входе игрока на шард
Событие OnUnAcquireItem вызывается при _попытке_ продажи итема в магазин, даже если эта вещь не покупается
Если нет денег у НПС-покупателся, возможно и на plot\stolen то же самое будет. Вероятно что и OnAcquireItem будет вызываться.
Бага функции TakeGoldFromCreature()
Надо дополнительно проверять текущее действие, if (GetCurrentAction(oPC) == ACTION_DROPITEM) {чит}
Т.е. можно задать действие "выложить деньги на землю", некоторое время сервер будет считать, что все деньги по прежнему у вас. если сервер в это время отнимает деньги у игрока функцией TakeGoldFromCreature() - при небольшом навыке на землю упадет _вся_ сумма, не учитывая те, что забрали функцией. Баг особо опасен для банков (так делались первые миллионы на Эксизе, когда только появился первый банк)
Иногда полезно читать лексикон
При работе функции DestroyObject()объект уничтожится только когда завершится скрипт, вызвавший эту функцию, а до этого момента объект все еще существует. Т.е. получается функция только помечает объект на удаление, а удаляется он сборщиком мусора после завершения скрипта.
Скорее не баг, а недодуманность скриптеров в Стандартном Крафте.
Проверял на версии 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 ); |
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 а потом наоборот.
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; } |
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; } |
Интересная фишка тут мне вечерком подвернулась, хотя может и старо, но пока нигде не видел.
Сначала маялся чтобы на "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... |
Аваддон
Да ну нафиг!!! Это мега фича!! Сенки!
стоп..а если игроков много...те у всех одинаковый ХБ?
Лекс не понял твоего вопроса... У каждого игрока на HB будет включаться этот скрипт
все, те одинаковый хб. Но для сингла это не важно. Отличная фича!
Зато интересно для шардостроителей.
я имел ввиду что не важно, что для всех одинаковый. :-) А синглам эта штука тоже будет полезна.. :-)
QUOTE |
Если очень нужно хранить - ... задавайте уникальные теги локациям (не проверял) и с ними работайте. |
QUOTE |
Вообще легче всего сохранять локу на какой нить плот-итем в инвентаре, который невозможно выкинуть и т.п. Или же делать LocationToString а потом наоборот. |
QUOTE |
Да, если еще написать обратную функцию StringToLocation и следить за уникальностью тегов локаций в модуле - то достаточно просто и надежно получится. |
В поздних версиях тулсета ввели функцию - поиск по тексту (предметов, существ, плейсов). Но в тайлах такого поиска не сделали. Я работая с большими тайлами, порой забываю в какой группе какой объект находится. Но найти нужный тайл через его название можно.
Заходите на закладку там где этот поиск возможен. Набираете текст, переходите в закладку тайлов и нажимаем 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 игрока. Проверил - работает... Чудеса да и только, товарищи... |
QUOTE (_kaa_ @ Jun 24 2004, 18:14) |
Локальные переменные на итемах из инвентаря игрока сохраняются в файле Для меня это было откровением и нашел я это случайно. Если выставить локальную переменную на итем - она сохраняется в .bic файле игрока, что иногда очень удобно (особенно вместе с настоящими nodrop-итемами, многие вещи так делать гораздо проще, чем юзать БД) |
Еще одна, на этот раз неприятная, фича:
После вызова SetItemStackSize стираются локальные переменные на предмете.
QUOTE (virusman @ Sep 10 2005, 10:15) |
Еще одна, на этот раз неприятная, фича: После вызова SetItemStackSize стираются локальные переменные на предмете. |
угу...таким образом единственная инфа, которую можно сохранить у stackable итемов при обьеденении\разделении, и которую при нужде скриптами можно менять ( в разумных пределах ), это чардж...
QUOTE (_kaa_ @ Jun 26 2004, 10:44) |
При работе функции DestroyObject()объект уничтожится только когда завершится скрипт, вызвавший эту функцию, а до этого момента объект все еще существует. Т.е. получается функция только помечает объект на удаление, а удаляется он сборщиком мусора после завершения скрипта. |
у меня лут падает всегда и везде, но с подобной проблемой не сталкивался - скрипт дропа лута всегда до конча срабатывал...
единственный косяк случался, если игрок умирал в момент перехода из локи в локу... потому единственный фикс, который я делал - проверка if(oDropBag!=OBJECT_INVALID)...
такая проверка не только фиксит возможный дюп, но и отучает игроков убегать от мобов в переходы
_kaa_
Да, точно, это у меня глюк был.
типа того:
Neverwinter Script Source |
copy = CopyItem(item,..., copy vars = true!); SetLocalInt(item, "deleted", 1); DestroyObject(item, 0.1f); |
наковырял вот неприятность, но она уже из прошлого - до версии 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 } |
To: dumbo
Я ж говорил. В чейнджлисте 1.66 как раз был написано про этот фикс.
В функциях Action(Force)MoveToLocation и ActionMoveAwayFromLocation существует утечка памяти. Если ваш модуль "течёт" - обратите внимание на вызовы этих функций.
Веселые грабельки в StartNewModule().
Функция срабатывает только при наличии игроков на сервере (есть мысль, что с опцией restart module then empty работает всегда, но опция популярностью не пользуется). Т.е. единственное ее применение в pw - рестарт с дм-ванда. А мне так хотелось не извращаться со сменой модулей "снаружи" (их два), а подгружать их друг из друга
Но самое веселое тут то, что однажды запущенная без игроков функция "ломается" совсем и отказывается работать даже с игроками. Просто ничего не делает, без ошибок в логе.
P.S. То, что работает не всегда как минимум на двух разных конфигурациях, - 100%. Саму закономерность вылавливал двое суток, но там все так хитро, что мог чуть ошибиться.
У меня в тестовом модуле StartNewModule() работать отказалась, когда игрок нажал на рычаг рестарта и вышел, а функция вызывалась секунды через 3 после юза рычага. Что подтверждает вышеизложенное
Грабли понятные и логичные, он, тем не менее, я на них в своё время наступил
Функция GetGoldPieceValue чувствительна к флагам на итеме. Если Выставлен флаг plot - вернёт 0, если нет, но не выставлен identified - вернёт цену базового итема. Нормальное значение функция возвращает только, если убран plot и выставлен identified.
Забавная и не очень приятная штука, с которой я столкнулся первый раз. Если менять свойства оружия при его надевании (onEquipItem), движок упорно использует частично старые свойства. Пример: не так давно я писал систему, которая реализует выставление свойств OnHitSpellCast Greater Dispelling & Enhancement Bonus +5 и убирает свойство Holy Avenger (поскольку биовари в 1.69 холик убили). Самое весёлое, что движок игнорит +5, но остаётся дамаг 1д6 дивайном от зла, который встроен в Holy Avenger. Заделеивание добавления свойств ничего не даёт.
Неприятная фича - добавленная в версии 1.69 функция SetDescription плохо работает в комбинации с CopyItem[AndModify]. Если сначала вызвать SetDescription(), а потом скопировать предмет - описание станет дефолтным для данного объекта, как будто мы вызвали SetDescription с пустой строкой. Для готового модуля я написал небольшой Java-класс, который заменяет все вхождения вызовов функций копирования во всех модульных скриптах на вызовы кастомных вида:
Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)