Огромное спасибо. Давно хотел сделать такую штуку, но было лень.
Поковырял компанию, немного переработал оригинальные скрипты.
В дальнейшем собираюсь переделать работу с предметами и сам скрипт под мультиплеер с записью свойств в MySQL
Смысл таков:
На локацию вешается локалинт ITEM_KEY = 0 как счетчик.
В недоступном месте стоит вейпоинт backup_waypoint.
При заходе в локацию, на игрока вешается эффект провала заклинания и прочищаются все эффекты кроме естественных.
С предметами сделано довольно странно, но для сингла сойдет:
1) Предметы копируются к вейпоинту с новыми тегами
2) На оригинальных предметах прочищаются все свойства и устанавливается локалстринг с тегом копии
При выходе из локации с игрока снимаются эффект спеллфейла и полиморфа(иначе нельзя будет восстановить свойства на заполиморфимшеся друле), а потом с предметами происходит следующее:
1) Изымается локалстринг с тегом буферного предмета
2) Копируются все свойства с буферного на оригинальный предмет
3) Буферный предмет уничтожается
Раскомментировал, как мог.
На onEnter локации:Neverwinter Script
#include "x2_inc_itemprop"
int GetIsAlcohol(object oItem)
{
itemproperty ip;
ip = GetFirstItemProperty(oItem);
if(GetIsItemPropertyValid(GetNextItemProperty(oItem)
))
return FALSE;
if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL)
{
if(GetItemPropertySubType(ip) == IP_CONST_CASTSPELL_SPECIAL_ALCOHOL_BEER ||
GetItemPropertySubType(ip) == IP_CONST_CASTSPELL_SPECIAL_ALCOHOL_SPIRITS ||
GetItemPropertySubType(ip) == IP_CONST_CASTSPELL_SPECIAL_ALCOHOL_WINE)
return TRUE;
}
return FALSE;
}
int GetIsPoisonAmmo(object oItem)
{
itemproperty ip;
ip = GetFirstItemProperty(oItem);
if(GetIsItemPropertyValid(GetNextItemProperty(oItem)
))
return FALSE;
if(IPGetItemHasItemOnHitPropertySubType(oItem, IP_CONST_ONHIT_ITEMPOISON))
return TRUE;
return FALSE;
}
int GetIsDyeKit(object oItem)
{
if(GetBaseItemType(oItem) == BASE_ITEM_MISCSMALL)
{
itemproperty ip = GetFirstItemProperty(oItem);
if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL)
{
int nSubType = GetItemPropertySubType(ip);
return (nSubType >= 490 && nSubType <= 497);
}
return FALSE;
}
return FALSE;
}
//Копирует предмет
void RemoveAllProperties(object oItem, object oPC, int nSlot = -1)
{
if(oPC != OBJECT_INVALID)
{
if(oItem == OBJECT_INVALID)//Если не передается предмет, но передается слот, то работать с предетом на слоте
{
oItem = GetItemInSlot(nSlot, oPC);
if(oItem == OBJECT_INVALID)
return;
}
else
{
int nType = GetBaseItemType(oItem);
if(nType == BASE_ITEM_TORCH ||
nType == BASE_ITEM_TRAPKIT ||
nType == BASE_ITEM_HEALERSKIT ||
nType == BASE_ITEM_GRENADE ||
nType == BASE_ITEM_THIEVESTOOLS ||
nType == 109 || // crafting stuff
nType == 110 ||
nType == 112)
return;
}
}
if(GetIsAlcohol(oItem) || GetIsPoisonAmmo(oItem) || GetIsDyeKit(oItem))
return;
if(oItem == OBJECT_INVALID)
return;
// вейпоинт, куда складываются копии предметов
//Должен быть недоступен игроку
object oWP = GetWaypointByTag("backup_waypoint");
//счетчик предметов, должен быть установлен для объекта выполнения заранее и не должен сбрасываться
int nKey = GetLocalInt(OBJECT_SELF, "ITEM_KEY");
nKey++;
SetLocalInt(OBJECT_SELF, "ITEM_KEY", nKey);
string sKey = IntToString(nKey);
object oCopy = CopyObject(oItem, GetLocation(oWP), OBJECT_INVALID, "back_item" + sKey);
//После копирования на предмет записывается его ключ
SetLocalString(oItem, "ITEM_KEY", sKey);
itemproperty ip = GetFirstItemProperty(oItem);
while(GetIsItemPropertyValid(ip))
{
RemoveItemProperty(oItem, ip);
ip = GetNextItemProperty(oItem);
}
}
//Убирает эффекты с существа, оставляя естественные эффекты, например EFFECT_TYPE_POISON
void RemoveEffects(object oObject)
{
effect eEff = GetFirstEffect(oObject);
while(GetIsEffectValid(eEff))
{
int nType = GetEffectType(eEff);
if(GetEffectSubType(eEff) != SUBTYPE_EXTRAORDINARY &&
(nType == EFFECT_TYPE_ABILITY_INCREASE ||
nType == EFFECT_TYPE_AC_INCREASE ||
nType == EFFECT_TYPE_ATTACK_INCREASE ||
nType == EFFECT_TYPE_BLINDNESS ||
nType == EFFECT_TYPE_CHARMED ||
nType == EFFECT_TYPE_CONCEALMENT ||
nType == EFFECT_TYPE_CONFUSED ||
nType == EFFECT_TYPE_CURSE ||
nType == EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE ||
nType == EFFECT_TYPE_DAMAGE_INCREASE ||
nType == EFFECT_TYPE_DAMAGE_REDUCTION ||
nType == EFFECT_TYPE_DAMAGE_RESISTANCE ||
nType == EFFECT_TYPE_DAZED ||
nType == EFFECT_TYPE_DEAF ||
nType == EFFECT_TYPE_DOMINATED ||
nType == EFFECT_TYPE_ELEMENTALSHIELD ||
nType == EFFECT_TYPE_ETHEREAL ||
nType == EFFECT_TYPE_FRIGHTENED ||
nType == EFFECT_TYPE_HASTE ||
nType == EFFECT_TYPE_IMMUNITY ||
nType == EFFECT_TYPE_IMPROVEDINVISIBILITY ||
nType == EFFECT_TYPE_INVISIBILITY ||
nType == EFFECT_TYPE_INVULNERABLE ||
nType == EFFECT_TYPE_ABILITY_INCREASE ||
nType == EFFECT_TYPE_NEGATIVELEVEL ||
nType == EFFECT_TYPE_PARALYZE ||
nType == EFFECT_TYPE_POLYMORPH ||
nType == EFFECT_TYPE_REGENERATE ||
nType == EFFECT_TYPE_SANCTUARY ||
nType == EFFECT_TYPE_SAVING_THROW_INCREASE ||
nType == EFFECT_TYPE_SEEINVISIBLE ||
nType == EFFECT_TYPE_SILENCE ||
nType == EFFECT_TYPE_SKILL_INCREASE ||
nType == EFFECT_TYPE_SLOW ||
nType == EFFECT_TYPE_SPELL_IMMUNITY ||
nType == EFFECT_TYPE_SPELL_RESISTANCE_INCREASE ||
nType == EFFECT_TYPE_SPELLLEVELABSORPTION ||
nType == EFFECT_TYPE_TEMPORARY_HITPOINTS ||
nType == EFFECT_TYPE_TRUESEEING ||
nType == EFFECT_TYPE_ULTRAVISION ||
nType == EFFECT_TYPE_INVULNERABLE))
RemoveEffect(oObject, eEff);
eEff = GetNextEffect(oObject);
}
}
void main()
{
object oEnter = GetEnteringObject();
//Если игрок сохранился, вышел из игры и загрузился потом, ничего с ним не делать
//INSIDE - Локальная переменная для избежания глюков с save\load
int nInside = GetLocalInt(oEnter, "INSIDE");
if(nInside == 1) return;
SetLocalInt(oEnter, "INSIDE", 1);
RemoveEffects(oEnter);
effect eSpellFailure = EffectSpellFailure(100, SPELL_SCHOOL_GENERAL);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSpellFailure, oEnter);
object oItem = GetFirstItemInInventory(oEnter);
while(oItem != OBJECT_INVALID)
{
RemoveAllProperties(oItem, oEnter, -1);
oItem = GetNextItemInInventory(oEnter);
}
//Убираем свойства с предметов в слотах экиперовки
//Если лексикон не врет, то искомые слоты с 0(INVENTORY_SLOT_HEAD) по 13(INVENTORY_SLOT_BOLTS)
int iSlot = 0;
while(iSlot<14)
{
RemoveAllProperties(OBJECT_INVALID, oEnter, iSlot);
iSlot++;
}
}
На onExit:Neverwinter Script
#include "x2_inc_itemprop"
// Восстанавливаем свойства предметов
void RestoreAllProperties(object oItem, object oPC, int nSlot = -1)
{
if(oPC != OBJECT_INVALID)
{
if(oItem == OBJECT_INVALID)//Если не передается предмет, но передается слот, то работать с предетом на слоте
oItem = GetItemInSlot(nSlot, oPC);
if(oItem == OBJECT_INVALID)
return;
}
string sKey = GetLocalString(oItem, "ITEM_KEY");
object oOriginalItem = GetObjectByTag("back_item" + sKey);
if(oOriginalItem != OBJECT_INVALID)
{
//Копировать все свойства предмета на новый
IPCopyItemProperties(oOriginalItem, oItem);
DestroyObject(oOriginalItem);
DeleteLocalString(oItem, "ITEM_KEY");
}
}
//Убирает свойства с создания
void RemoveEffects(object oObject)
{
effect eEff = GetFirstEffect(oObject);
while(GetIsEffectValid(eEff))
{
if(GetEffectType(eEff) == EFFECT_TYPE_SPELL_FAILURE || GetEffectType(eEff) == EFFECT_TYPE_POLYMORPH)
RemoveEffect(oObject, eEff);
eEff = GetNextEffect(oObject);
}
}
void main()
{
object oExit = GetExitingObject();
SetLocalInt(oExit, "INSIDE", 0); // Локальная переменная для избежания глюков с save\load
if(GetObjectType(oExit) == OBJECT_TYPE_CREATURE)
{
RemoveEffects(oExit);
object oItem = GetFirstItemInInventory(oExit);
float fDelay = 4.0; //Задержка для избежания глюков с прогрузкой, можно увеличить
while(oItem != OBJECT_INVALID)
{
DelayCommand(fDelay, RestoreAllProperties(oItem, oExit, -1));
oItem = GetNextItemInInventory(oExit);
}
//Убираем свойства с предметов в слотах экиперовки
//Если лексикон не врет, то искомые слоты с 0(INVENTORY_SLOT_HEAD) по 13(INVENTORY_SLOT_BOLTS)
int iSlot = 0;
while(iSlot<14)
{
DelayCommand(fDelay, RestoreAllProperties(OBJECT_INVALID, oExit, iSlot));
iSlot++;
}
}
}
}