×
Меню
Индекс

MSFD Работа с вещами в инвентаре

Добавление и удаление вещей из инвентаря
AddItem, "ObjectID", count_enum         
RemoveItem, "ObjectID", count_enum    
         
Actor ID->AddItem, "item_ID", 1
Container ID->RemoveItem, "Item_ID", 5
 
Эти функции достаточно просты, они добавляют и удаляют предметы из инвентаря игрока или других инвентарей, включая контейнеры.
 
RemoveItem может удалить объект из инвентаря навсегда, и он исчезнет.
 
Примечание: Удаление предметов, которые отсутствуют в инвентаре, уменьшит общий вес предметов, даже если их там нет!
 
Так что всегда проверяйте количество предметов в инвентаре, прежде чем будете их удалять. (Спасибо DinkumThinkum за эту инфу)
 
Не пытайтесь, чтобы предмет удалил сам себя через скрипт – это приведет к вылету (см скрипт-пример.)
 
Можно использовать отдельный глобальный или локальный скрипт, чтобы удалить такой предмет.
Однако, если игрок имеет несколько копий одного предмета с скриптом, то RemoveItem может запороть данные на других объектах (вы это увидите в виде непонятных цифр).
Экипировка или использование этого испорченного объекта может привести к вылету игрыНО: если вы при удалении тут же добавите какой-нибудь предмет в инвентарь, то порчи данных не будет. (Прим. Gwathlobal) (DinkumThinkum, Информация с форумов)
 
Чтобы этого избежать:
1. Если объект не имеет скрипта, используйте GetItemCount, чтобы убедиться что игрок имеет хотя бы 1 объект.
2. Если на объекте есть скрипт, используйте GetItemCount, чтобы знать, что у игрока ровно 1 объект, или сделайте объект уникальным.
 
Функция Drop не имеет этого бага.
Однако она может вызвать удвоение, если используется с OnPCEquip / SkipEquip, что может быть устранено добавлением и удалением любого предмета как описано для этих функций.
 
Использование Additem / Removeitem в диалогах:
Эти функции могут принимать глобальные переменные и только в поле результатов диалога, и только если глобальная переменная не была установлена в том же диалоге (Информация с форума / Argent; Согласно нему, максимальное количество, которое ему удалось добавить, равнялось 65534 (используя переменную типа long = 2147483520)). Также убедитесь, что эта переменная не изменяется в этом же диалоге. Например, var_a равняется 3 в момент, когда ответ выбран в диалоге. Если поле результата выглядит так:
 
Notes on using Additem / Removeitem in dialogue:
These functions can accept global variables, but only in dialogue results, and only if you don't set the global variable in the same dialog result (Forum info / Argent; According to Argent, the maximum amount he has been able to add using AddItem, var was 65534 (using a long var=2147483520)). In addition to Argent's info about AddItem/RemoveItem accepting variables in dialog action boxes: make sure that the variable in question doesn't change in the same box. Say, var_a equals 3 at the moment when the line is selected in dialogue. If the result field looks like this:
 
set var_a to var_a + 10 ;
AddItem gold_001, var_a
 
То только три дрейка будут добавлено, а нет 13.
Если возникнет необходимость, то все вычисления объедините в одном ответе, добавьте оператор choice, ведущий на следующий ответ, и добавляйте уже там.
Также, даже используя переменные типа long, я не смог добавлять предметов больше 32767. (Тестировалось на Морре без дополнений) (Информация с форумов / Kir)
 
Примечание.
Проще вызывать несколько скриптов к ряду (через опцию диалога в поле result).
StartScript 0001Script
 
В котором идут команды:
begin 0001Script
(делаем что-то)
StartScript 0002Script
StopScript 0001Script ;обязательно после Старт следующего! Т.е. СтопСкрипт сразу прекратит выполнение указанного скрипта!
End
 
И так далее в скрипте 0002Script.
 
Такое может быть полезно если требуется раскидать большое кол-во предметов по известному кол-ву целей.
Например, если вы не хотите задевать, в своем плагине, на прямую Селлуса Гравиуса, или Фаргота; то сделайте скрипт который выдаст им Даэдрическую Броню!
 
И точно не стоит пытаться давать большое кол-во объектов через поле Result (хотя это также возможно, но если кол-во предметов не значительно).
Либо, можно использовать несколько строк с AddItem в одном скрипте.
Например, если надо дать (кому-то) 1000000 gold_001 (и да, нельзя дать gold_10 gold_100), то следует написать:
Player->AddItem "gold_001" 10000
Player->AddItem "gold_001" 10000
Player->AddItem "gold_001" 10000
Player->AddItem "gold_001" 5000
 
Примечание по использованию контейнеров:
Утверждалось, что только первая функция AddItem всегда работает с контейнерами, но после этого, игрок должен вручную получить доступ к сундуку (напр. добавить предмет), прежде чем AddItem будет иметь какой-то эффект (непроверенная информация с форумов).
Если вы хотите добавить что-то в контейнер, который был опустошен, единственный способ, похоже, это удалить старый пустой контейнер и добавить новый вместо него (новый будет работать, пока в него что-то не положили, а затем вытащили. После этого его также нужно заменить.)
К сожалению, единственный способ убедится, пуст ли контейнер – это узнать, есть ли там каждый предмет в игре (Информация с форумов / ThePal).
 
Пример:
Следующий скрипт обсуждался на форумах (извините, не помню, кто сделал его первым).
Он должен спрашивать игрока, не желает ли он «переработать» предмет, который надет на нем, а затем заменить этот предмет «переработанным».
 
Begin scr_thing
 
short button
short OnPcEquip
short state
 
if ( MenuMode == 1 )
     return
endif
 
if ( OnPCEquip == 1 )      ; когда предмет надет
     set state to 1
     set OnPCEquip to 0     ; делать это один раз после каждого одевания
endif
if ( state ==1 )
     MessageBox "recycle?" , "yes", "no"
     set state to 2
elseif ( state == 2 )
     set button to GetButtonPressed
     if ( button == 0 )
          PlaySound "mysticism cast"
          player->RemoveItem "item_a", 1 ;эта строка обрушит игру!!!
          player->AddItem "item_b", 1
          set state to 0
     elseif ( button == 1 )
          set state to 0        ; все сделано, сбрасываем в ноль
     endif
endif
 
end
 
Он отлично работает без функции RemoveItem в нем, но с этой строкой игра рушится.
Причина в том, что скрипт присоединен к item_a, а так как данный скрипт удалит этот предмет и должен удалить себя самого, игра вылетает.
Так что эта идея должна реализоваться посредством глобального скрипта.
 
Не должна.
Достаточно сделать:
          ;player->RemoveItem "item_a", 1 ;удалить, или залочить это строку.
          player->AddItem "item_b", 1 ;дать объект Б... на котором уже будет скрипт на удаление объекта А!
 
Т.е. первый скрипт на объекте А:
 
Begin A_Object
 
short button
short OnPcEquip
short state
 
if ( MenuMode == 1 ) ;проверка на меню позволяет выполнить скрипт только после закрытия инвентаря, это предотвращает некоторые проблемы в т.ч.
     return
endif
 
if ( state ==1 )
     MessageBox "recycle?" , "yes", "no"
     set state to 2
elseif ( state == 2 )
     set button to GetButtonPressed
     if ( button == 0 )
          PlaySound "mysticism cast"
          player->AddItem "item_b", 1
          player->Eqiup "item_b", 1 ;т.е. можно сразу нацепить объект Б, так что бы объект А ушел в инвентарь. Разумеется объект должен быть "одеваемого" типа. Броня, оружие, одежда. Если нет, тогда не следует использовать Equip, конечно.
          set state to 0
     elseif ( button == 1 )
          set state to 0        ; все сделано, сбрасываем в ноль
     endif
endif
 
if ( OnPCEquip == 1 )     ; когда предмет надет
     set state to 1
     set OnPCEquip to 0     ; делать это один раз после каждого одевания
endif
 
end
 
И второй скрипт, на Б объекте.
 
Begin B_Object
 
Short DoOnce ;это позволяет выполнить скрипт только 1 раз.
 
if ( MenuMode )
     return
endif
 
If ( DoOnce == 0 )
If ( GetItemCount, "item_a" > 0 )
player->RemoveItem "item_a", 1
Set DoOnce to 1
Endif
Endif
End
 
End
 
Либо использовать такой вариант:
Begin 2VerScript
 
short button
short OnPcEquip
short state
 
if ( MenuMode == 1 )
     return
endif
 
if ( OnPCEquip == 1 )     ; когда предмет надет
     set state to 1
     set OnPCEquip to 0    ; делать это один раз после каждого одевания
endif
if ( state ==1 )
     MessageBox "recycle?" , "yes", "no"
     set state to 2
elseif ( state == 2 )
     set button to GetButtonPressed
     if ( button == 0 )
PlaceAtPc "Remover_Object" 1 1 1 1 ;разумеется сначала надо создать такой объект в разделе активаторов используя "editor_marker" объекты.
          set state to 0
     elseif ( button == 1 )
          set state to 0        ; все сделано, сбрасываем в ноль
     endif
endif
 
end
 
На Remover_Object помещаем такой скрипт:

Begin 2verRemoverScript ;называем скрипт сходным с первым именем! это упростит навигацию и назначение.
 
Short DoOnce
 
;обратите внимание! Здесь нет опроса на меню мод! Это позволит удалить объект, даже если игрок успеет вызвать меню, чтобы "выкинуть" объект А и все же получить объект Б.
 
If ( GetDisabled == 1 ) ;удаляем объект с концами только после проверки на его "исчезновение", иначе будет Вылет!
SetDelete 1
Return
Endif
 
If ( Doonce == 1 )
Disable ;обязательно скрываем объект прежде чем его удалить!
Set Doonce to 2
Return
Endif
 
If ( DoOnce == 0 ) ;будем считать, что объект А точно есть в инвентаре раз он вызвал "Удалятеля", т.е. не проводим проверку на наличие объекта А.
player->RemoveItem "item_a", 1 ;здесь требуется указать ID цели, поскольку скрипт направленный. Таким образом можно удалять объекты вообще у любой цели..
           player->AddItem "item_b", 1
           PlaySound "mysticism cast"
Set DoOnce to 1
Endif
Endif
 
End
 

Notes on AddItem (by DinkumThinkum):
If you add items to an NPC's inventory when the inventory screen is already open, the display won't be updated with the new items, unless the player manually adds or removes displayed items (which forces the game to refresh the inventory display).
 
The early versions of my Potion Saver coded used 'If MenuMode == 1' to trigger putting potions back into the companion's inventory.
This worked fine when the companion was alive and conscious, since the script would add the potions while in the dialogue window, before the companion share inventory window was opened.
 
However, if the companion was dead or unconscious, clicking on the companion would open the inventory window immediately, and the potions would be added after the window was already open. End result, the potions wouldn't show up in the inventory window.
 
To avoid the problem, add items to an NPC's inventory as soon as they die, as soon as they fall unconscious, or when the game goes into Menu Mode (check Menu Mode after checking death and unconsciousness). That way, the items will be added before the inventory window opens.
 
Here's the code I used for this in the Potion Saver:
If ( GetHealth < 1 )
Set DTNPS_HandlePotions to 0
ElseIf ( GetFatigue < 1 )
Set DTNPS_HandlePotions to 0
ElseIf ( MenuMode == 1 )
Set DTNPS_HandlePotions to 0
Else
Set DTNPS_HandlePotions to 1
EndIf
 
Usage with containers:
If a container has been emptied by the player (in game), AddItem will no longer work.
One workaround is to replace the container with a new one each time it's accessed by the player, but this may not be practical as there's no way to detect when the container is empty.
Aragon suggested a solution:
 
The workaround is to attach a script to the container that always adds and removes a dummy item in the same frame that the player activated it.
So, my ingredients chest has a script that looks like:
; Open the chest
Activate
 
; Always add and remove a dummy (this hack keeps the "AddItem" calls working)
AddItem, "misc_de_cloth10", 1
RemoveItem, "misc_de_cloth10", 1
 
Furthermore, we make the chest with "references persist". Now we can reference it from an ingredients sorter using code like:
set count to ( player->GetItemCount, "ingred_dreugh_wax_01" )
while ( count > 0 )
     set count to ( count - 1 )
     player->RemoveItem, "ingred_dreugh_wax_01", 1
     "_tm_ingredients_chest"->AddItem, "ingred_dreugh_wax_01", 1
endwhile
 
Another (probably better) workaround suggested by Enmesharra:
Add a non-carryable light with no world or inventory art associated with it to the container. It won't show in inventory and thus the container can't be emptied (not tested).
 
Notes on RemoveItem:
Removing items that are not present in the inventory does not crash the game, but the 'RemoveItem' function will subtract the removed item's weight from the character's encumbrance, EVEN IF the item is NOT in the character's inventory. So if a script uses 'RemoveItem' to remove a 4 lb. item that the character doesn't have, the character's encumbrance will wind up 4 lbs. lower than it should be. The workaround for this bug is to always check for the presence of an item before using 'RemoveItem' to delete it (Thanks DinkumThinkum for this info).
 
Don't remove an item that is executing a script, from the item's own script: that will crash the game. See the example script.
 
A workaround for this is to use a separate global or local script to remove the item. However, if the player has two or more copies of an object with an attached script in their inventory, using RemoveItem on that Object ID will frequently corrupt data for one of the remaining copies (you may e.g. see corrupted health or count data). Equipping or using that corrupt object may cause the game to crash (Forum info / DinkumThinkum).