Как-то давно мне пришла в голову интересная мысль и захотелось ее реализовать - деньги в нвн не просто золотые, а монеты с весом. На ваулте лежало несколько реализаций, но меня они не устраивали, в виду некоторых причин их реализации.
Для работы системы нужен NWNX и 2 плагина nwnx_events и nwnx_fixes
Суть:
2 типа магазина: скупщик и продающий
Зацепляем событие на Еxamine item в nwnx_events
Берем голд-стоимость итема, вычисляем. В мое случае это 1 электрум = 100 золото = 10000 серебро
3 вида вычислений стоимости:
1). Когда еxamine item производиться просто в инвентаре, без открытого магазина.
-выдается стоимость без всяких надбавок + экономическое изменение (можно регулировать общее подорожание или удешевление)
2). С открытым магазине типа продающий
-производиться оценка, обоюдный бросок – в зависимости от этого скидки или наценка + экономическое изменение
3). в инвентаре при открытом магазине типа скупщик
-производиться оценка, обоюдный бросок – в зависимости от этого скидки или наценка + экономическое изменение
Надо добавить, что при реализации идеи использовались наработки КЛ, в частности - по поломке предметов, так что его (предмета) состояние непосредственно влияло на стоимость продажи/покупки
Результат выводиться виде сообщения игроку, когда он смотрит свойства предмета.
Продажа/Покупка осуществляется через REMOVE_DISTURBED_ITEM, простым перетаскиванием, без всяких диалогов. На магазин вешается переменная с ДЦ ОЦЕНКИ и типом магазина.
Всего два действия по сути: первое вы смотрите стоимость во время просмотра свойств, второе – покупка/продажа =)
Значит, обычный плэйс, с инвентарем. На нем 2 локалки типа int
TD_MERCANT_DC - ДЦ для ОЦЕНКИ
и
TD_MERCANT - тип магазина 1 и 2
Название валют можно придумать свои, для простоты ориентации в скрипте:
MonE - это сама дорогая (Электрумовая) = 100 золотым = 10000 серебра
MonG - средняя (Золотая) = 100 серебрянным
MonS - нифиля (Сребрянная)
Т.е. грубо говоря если в разных городах разные валюты (города в разных странах), то стоимость итема будет выдана в той валюте, которая в городе с учетом той экономической ситуации, которая в стране.
Была идея сделать разные наценки в городах, в зависимости от типов предмета. Например, на мечи - такая, на бронь - такая. Что-то типо предложения спроса в городе. Идея не воплотилась)
Скрипты:
nwnx_events
Neverwinter Script
const int TD_SECS_IN_HOUR = 120.0f;
// Melisse: Берет goldvalue предмета и переводит его в валюту и выдает ввиде сообщения игроку
void td_SendMessageItemMoneyCost (object oPC, object oInvent, object oTarget);
// Main
void main( ){
object oPC = OBJECT_SELF;
object oTarget = GetNWNXEventTarget( );
int nEvent = GetNWNXEventType( );
switch( nEvent ){
case EVENT_EXAMINE:
{
if(GetObjectType(oTarget) == OBJECT_TYPE_ITEM)
{
object oInvent = GetItemPossessor(oTarget);
if(GetPlotFlag(oTarget) == FALSE)
td_SendMessageItemMoneyCost (oPC, oInvent, oTarget);
}
else
SendMessageToPC( oPC, "NWNX Knows you just examined "+GetName( oTarget )+"!" );
break;
}
}
}
void td_SendMessageItemMoneyCost (object oPC, object oInvent, object oTarget)
{
int nInvent = (GetObjectType(oInvent) == OBJECT_TYPE_PLACEABLE);
int nCost = GetGoldPieceValue(oTarget);
SendMessageToPC (oPC, "=============================================
====");
//SendMessageToPC (oPC, "Value = "+IntToString(nCost));
SendMessageToPC (oPC, td_ColorText(GetName(oTarget),TD_TXT_COLOR_GRAY));
/*
Закоментровано. Это относитсья к системе поломки, которую тут не выкладываю.
// Вычислим стоимость, если итем сломан
struct strItemDur strMyItemDur = td_GetItemDur(oTarget);
int nDurCur = strMyItemDur.nDurCur;
int nDurMax = strMyItemDur.nDurMax;
int nDurResult = nDurMax - nDurCur;
if(nDurResult > 0)
{
float fBroken = IntToFloat(nCost)/100.0*IntToFloat(nDurResult);
nCost -= FloatToInt(fBroken);
SendMessageToPC (oPC, "Сломан! = -"+IntToString(nDurResult)+ "% от полной цены");
}*/
// Если просмотр производиться в чьем-то инвентаре
if(GetIsObjectValid(oInvent))
{
// Магазин
// При просмотре описания с открытым магазином
// стоимость товара учитывает со скидкой/наценкой
if(GetLocalInt(oInvent, "TD_MERCANT"))
{
int nSell = FALSE;
object oStore;
if (oPC == oInvent)
{ // Игрок продает
oStore = GetLocalObject(oPC, "TD_MERCANT_OBJ");
nSell = TRUE;
}
else if(nInvent)
{ // Игрок покупает
oStore = oInvent;
}
else
return;
string sCharID = IntToString(GetLocalInt (oPC, "nCharID"));
int nDiscount = GetLocalInt (oStore, "TD_DISCOUNT"+sCharID);
int nTime = GetLocalInt (oStore, "TD_DISCOUNT_TIME"+sCharID);
int nEcoKingdom = GetLocalInt (GetModule(), "TD_MOD_ECO"
+GetSubString(GetTag(GetArea(oPC)), 2, 1));
SendMessageToPC (oPC, "Экономическая ситуация = "+IntToString(nEcoKingdom)+"%");
// Рачитаем на сколько поднимиться цена в этой стране на этот предмет
nEcoKingdom = FloatToInt(IntToFloat(nCost)/100.0*IntToFloat(nEcoKingdom));
// Скидки нет или время пришло (1 раз в сутки)
if(!nDiscount || nTime < td_Uptime())
{
int nDC = GetLocalInt(oStore, "TD_MERCANT_DC");
int nSkill = GetSkillRank(SKILL_APPRAISE, oPC);
nDiscount = nDC - nSkill;
if(nDiscount < 0)
nDiscount *= -1;
if(nDiscount == 0)
nDiscount = 1;
if(GetIsSkillSuccessful(oPC, SKILL_APPRAISE, nDC))
{
if(!nSell)
{
nDiscount += 100;
}
SetLocalInt (oStore, "TD_DISCOUNT"+sCharID, nDiscount);
}
else
{
if(nSell)
{
nDiscount += 100;
}
SetLocalInt (oStore, "TD_DISCOUNT"+sCharID, nDiscount);
}
SetLocalInt (oStore, "TD_DISCOUNT_TIME"+sCharID,
td_Uptime ()+24*TD_SECS_IN_HOUR );
}
// Скидка есть - расчитаем
if(nDiscount >= 100)
{
nDiscount -= 100;
float fDiscount = IntToFloat(nCost)/100.0*IntToFloat(nDiscount);
if(nSell)
{
SendMessageToPC (oPC, "Продажа: цена = +"+IntToString(nDiscount)+"%");
nCost += FloatToInt(fDiscount)-nEcoKingdom; // С учетом общего экономического состояния в стране
}
else
{
nCost -= FloatToInt(fDiscount)+nEcoKingdom; // С учетом общего экономического состояния в стране
SendMessageToPC (oPC, "Покупка: скидка = -"+IntToString(nDiscount)+"%");
}
}
else
{
float fDiscount = IntToFloat(nCost)/100.0*IntToFloat(nDiscount);
if(nSell)
{
SendMessageToPC (oPC, "Продажа: цена = -"+IntToString(nDiscount)+"%");
nCost -= FloatToInt(fDiscount)-nEcoKingdom; // С учетом общего экономического состояния в стране
}
else
{
nCost += FloatToInt(fDiscount)+nEcoKingdom; // С учетом общего экономического состояния в стране
SendMessageToPC (oPC, "Покупка: цена = +"+IntToString(nDiscount)+ "%");
}
}
//SendMessageToPC (oPC, "Игрок = "+GetName(oPC));
//SendMessageToPC (oPC, "Магазин = "+GetName(oStore));
}
}
// Переведем в денежынй эквивалент
int nMonE = nCost/10000; // Электрумовые
int nMonG = (nCost - nMonE*10000)/100; // Золотые
int nMonS = nCost - (nMonG*100+nMonE*10000); // Серебряные
// Покажем игроку результаты
SendMessageToPC (oPC, td_ColorText("*СТОИМОСТЬ:",TD_TXT_COLOR_GRAY));
SendMessageToPC (oPC, td_MoneyName(GetArea(oPC),nMonS,nMonG,nMonE));
}
на OnOpen на плэйсе
Neverwinter Script
void main()
{
object oPC = GetLastOpenedBy();
// Магазин, который скупает
if(GetIsPC(oPC) && GetLocalInt(OBJECT_SELF, "WS_MERCANT") == 2)
{
SetLocalInt(oPC, "WS_MERCANT", TRUE);
SetLocalObject(oPC, "WS_MERCANT_OBJ", OBJECT_SELF);
}
// Запускается проверка на "Опознание" предметов в инвентаре магазина
// 1 раз за рестарт, при 1-вом открытии
if(!GetLocalInt(OBJECT_SELF, "nActiveted"))
{
object oItem = GetFirstItemInInventory(OBJECT_SELF);
while (GetIsObjectValid(oItem))
{
if(!GetIdentified(oItem))
SetIdentified(oItem, TRUE);
int nBaseType = GetBaseItemType(oItem);
// Выставляем на итемы со стэком > 1
// Это необходимо для того, чтобы они не стыкались с теми что в инвентаре
// nwnx_fixed убирает баг, при котором стэкались итемы, когда у них локалки
if(nBaseType == BASE_ITEM_ARROW ||
nBaseType == BASE_ITEM_BOLT ||
nBaseType == BASE_ITEM_BULLET ||
nBaseType == BASE_ITEM_DART ||
nBaseType == BASE_ITEM_POTIONS ||
nBaseType == BASE_ITEM_THROWINGAXE)
{
SetLocalInt(oItem, "WS_MERCANT_ITEM", TRUE);
}
oItem = GetNextItemInInventory(OBJECT_SELF);
}
SetLocalInt(OBJECT_SELF, "nActiveted", TRUE);
}
}
на OnDisturbed на плэйсе
Neverwinter Script
/**************************************\
td_pds_shop
--------------------------
Author : Melisse
E-mail :
ICQ :
Started :
--------------------------
Скрипт магазина
\**************************************/
//#include "td_inc_craft"
void td_ReturnItem (object oSubject, object oItem);
void main()
{
object oMerchant = OBJECT_SELF;
object oPC = GetLastDisturbed();
object oItem = GetInventoryDisturbItem();
object oArea = GetArea(oPC);
string sMonE = td_GetTagMoney(GetTag(oArea), "sMonE");
string sMonG = td_GetTagMoney(GetTag(oArea), "sMonG");
string sMonS = td_GetTagMoney(GetTag(oArea), "sMonS");
string sTradeType = GetLocalString(oMerchant, "sTradeType");
int nDistType = GetInventoryDisturbType();
int nMoneyPC = td_GetCountAllMoney (oPC, sMonE, sMonG, sMonS);
int nCost = GetGoldPieceValue(oItem);
/*
Закоментровано. Это относитсья к системе поломки, которую тут не выкладываю.
// Вычислим стоимость, если итем сломан
struct strItemDur strMyItemDur = td_GetItemDur(oItem);
int nDurCur = strMyItemDur.nDurCur;
int nDurMax = strMyItemDur.nDurMax;
if((nDurMax - nDurCur) > 0)
{
float fCost = IntToFloat(nCost)/100.0*IntToFloat(nDurCur);
nCost -= FloatToInt(fCost);
}
*/
// стоимость товара учитывает со скидкой/наценкой
string sCharID = IntToString(GetLocalInt (oPC, "nCharID"));
int nDiscount = GetLocalInt (oMerchant, "TD_DISCOUNT"+sCharID);
int nTime = GetLocalInt (oMerchant, "TD_DISCOUNT_TIME"+sCharID);
int nSell = FALSE;
int nEcoKingdom = GetLocalInt (GetModule(), "TD_MOD_ECO_"
+GetSubString(GetTag(GetArea(oPC)), 2, 1));
if(GetLocalInt(oMerchant, "TD_MERCANT") == 2)
nSell = TRUE;
// Рачитаем на сколько поднимиться цена в этой стране на этот предмет
nEcoKingdom = FloatToInt(IntToFloat(nCost)/100.0*IntToFloat(nEcoKingdom));
// ----
// Вычисление скидки/наценки
// ----
if(!nDiscount || nTime < td_Uptime())
{
int nDC = GetLocalInt(oMerchant, "TD_MERCANT_DC");
int nSkill = GetSkillRank(SKILL_APPRAISE, oPC);
nDiscount = nDC - nSkill;
if(nDiscount < 0)
nDiscount *= -1;
if(nDiscount == 0)
nDiscount = 1;
if(GetIsSkillSuccessful(oPC, SKILL_APPRAISE, nDC))
{
if(!nSell)
{
nDiscount += 100;
}
SetLocalInt (oMerchant, "TD_DISCOUNT"+sCharID, nDiscount);
}
else
{
if(nSell)
{
nDiscount += 100;
}
SetLocalInt (oMerchant, "TD_DISCOUNT"+sCharID, nDiscount);
}
SetLocalInt (oMerchant, "TD_DISCOUNT_TIME"+sCharID,td_Uptime()+24*TD_SECS_IN_HOUR );
}
// Скидка есть - расчитаем
if(nDiscount >= 100)
{
nDiscount -= 100;
float fDiscount = IntToFloat(nCost)/100.0*IntToFloat(nDiscount);
if(nSell)
{
//SendMessageToPC (oPC, "Повышение цены = "+IntToString(nDiscount)+ "%");
nCost += FloatToInt(fDiscount)-nEcoKingdom; // С учетом общего экономического состояния в стране
}
else
{
nCost -= FloatToInt(fDiscount)+nEcoKingdom; // С учетом общего экономического состояния в стране
//SendMessageToPC (oPC, "Скидка = "+IntToString(nDiscount)+ "%");
}
}
else
{
float fDiscount = IntToFloat(nCost)/100.0*IntToFloat(nDiscount);
if(nSell)
{
//SendMessageToPC (oPC, "Снижение цены = "+IntToString(nDiscount)+ "%");
nCost -= FloatToInt(fDiscount)-nEcoKingdom; // С учетом общего экономического состояния в стране
}
else
{
nCost += FloatToInt(fDiscount)+nEcoKingdom; // С учетом общего экономического состояния в стране
//SendMessageToPC (oPC, "Наценка = "+IntToString(nDiscount)+ "%");
}
}
// ----
// Процесс Покупка/Продажа
// ----
if(nDistType == INVENTORY_DISTURB_TYPE_REMOVED)
{
if(GetPlotFlag(oItem) || GetLocalInt(oMerchant, "TD_MERCANT") != 1)
{
td_ReturnItem (oMerchant, oItem);
FloatingTextStringOnCreature ("Этот предмет не продается.", oPC, FALSE);
}
else if(nMoneyPC < nCost)
{
td_ReturnItem (oMerchant, oItem);
FloatingTextStringOnCreature ("Не хватает денег для покупки этой вещи.", oPC, FALSE);
}
else
{
// -------
// Транзакция денег и выдача предмета
// -------
td_MoneyTransaction (oPC, TRUE, nMoneyPC, nCost, sMonE, sMonG, sMonS);
CopyItem (oItem, oMerchant, TRUE);
DeleteLocalInt(oItem, "TD_MERCANT_ITEM");
}
AssignCommand(oPC, ActionInteractObject(oMerchant));
DelayCommand(0.5f, AssignCommand(oPC, ActionInteractObject(oMerchant)));
}
else if (nDistType == INVENTORY_DISTURB_TYPE_ADDED)
{
if(GetPlotFlag(oItem) || GetLocalInt(oMerchant, "TD_MERCANT") != 2)
{
td_ReturnItem (oPC, oItem);
FloatingTextStringOnCreature ("*Вы не можете продать этот предмет здесь.", oPC, FALSE);
}
// Нельзя продать не идентифицированый итем или продать предмет который не покупает магазин
else if (!GetIdentified (oItem) || FindSubString(sTradeType, IntToString(GetBaseItemType (oItem))) == -1)
{
td_ReturnItem (oPC, oItem);
FloatingTextStringOnCreature ("*Вы не можете продать этот предмет здесь.", oPC, FALSE);
}
else
{
// -------
// Транзакция денег и удаление проданого предмета
// -------
td_MoneyTransaction (oPC, FALSE, nMoneyPC, nCost, sMonE, sMonG, sMonS);
SetLocalInt(oItem, "TD_DESTROY",TRUE);
DestroyObject(oItem,0.5f);
}
AssignCommand(oPC, ActionInteractObject(oMerchant));
DelayCommand(0.5f, AssignCommand(oPC, ActionInteractObject(oMerchant)));
}
}
void td_ReturnItem (object oSubject, object oItem)
{
CopyItem (oItem, oSubject, TRUE);
DestroyObject (oItem);
}
Далее выкладываю просто функции и их прототипы:
Neverwinter Script
// Возвращает текстовую строку со стоимость и названиями валют
// Можно устанавливать разные валюты для разных городов и тут высталять названия
string ws_MoneyName(int nMoneyS, int nMoneyG = 0, int nMoneyE = 0);
string ws_MoneyName(int nMoneyS, int nMoneyG = 0, int nMoneyE = 0)
{
sText = ws_ColorText(IntToString(nMoneyE)+" Хан ",WS_TXT_COLOR_WHITE)
+ ws_ColorText(IntToString(nMoneyG)+" Мон ",WS_TXT_COLOR_YELLOW)
+ ws_ColorText(IntToString(nMoneyS)+ " Шо",WS_TXT_COLOR_CYAN);
return sText;
}
// Возвращает полное количество денег в GoldValue
int ws_GetCountAllMoney (object oSubject, string sMonE, string sMonG, string sMonS);
int ws_GetCountAllMoney (object oSubject, string sMonE, string sMonG, string sMonS)
{
object oMoney = GetFirstItemInInventory(oSubject);
int i;
while (GetIsObjectValid(oMoney))
{
string sMoney = GetTag(oMoney);
if(sMoney == sMonE)
i += GetNumStackedItems(oMoney)*10000;
else if (sMoney == sMonG)
i += GetNumStackedItems(oMoney)*100;
else if (sMoney == sMonS)
i += GetNumStackedItems(oMoney);
oMoney = GetNextItemInInventory(oSubject);
}
return i;
}
// Производит пересоздание денег у игрока
void ws_MoneyTransaction (object oPC, int nType, int nMoneyPC, int nCost, string sMonE, string sMonG, string sMonS);
void ws_MoneyTransaction (object oPC, int nType, int nMoneyPC, int nCost, string sMonE, string sMonG, string sMonS)
{
int nResult;
if(nType) // Покупка предмета
nResult = nMoneyPC - nCost;
else // Продажа предмета
nResult = nMoneyPC + nCost;
SendMessageToPC (oPC, "Было денег = "+IntToString(nMoneyPC)+" Стоимость итема = "+IntToString(nCost)+" Стало денег = "+IntToString(nResult) );
int nMonE = nResult/10000; // Электрумовые
int nMonG = (nResult - nMonE*10000)/100; // Золотые
int nMonS = nResult - (nMonG*100+nMonE*10000); // Серебряные
/* SendMessageToPC (oPC, "Эле = "+IntToString(nMonE)+" Золо = "+IntToString(nMonG)+" Сере = "+IntToString(nMonS) );*/
object oItem = GetFirstItemInInventory(oPC);
while (GetIsObjectValid(oItem))
{
string sTag = GetTag(oItem);
if (sTag == sMonE ||
sTag == sMonG ||
sTag == sMonS)
{
SetLocalInt(oItem, "WS_DESTROY",TRUE);
DestroyObject(oItem);
}
oItem = GetNextItemInInventory(oPC);
}
object oNew;
if(nMonS)
{
oNew = CreateItemOnObject(sMonS, oPC);
SetItemStackSize (oNew, nMonS);
}
if(nMonG)
{
oNew = CreateItemOnObject(sMonG, oPC);
SetItemStackSize (oNew, nMonG);
}
if(nMonE)
{
oNew = CreateItemOnObject(sMonE, oPC);
SetItemStackSize (oNew, nMonE);
}
}
// Возвращает тэг валюты sMoneyType используемой в этом городе
string ws_GetTagMoney (string sAreaTag, string sMoneyType);
string ws_GetTagMoney (string sAreaTag, string sMoneyType)
{
string sTown = sAreaTag;
string sMoneyTag;
if(sTown == "Area001")
{
if(sMoneyType == "sMonE")
sMoneyTag = "IT_MON_FE";
else if (sMoneyType == "sMonG")
sMoneyTag = "IT_MON_FG";
else if (sMoneyType == "sMonS")
sMoneyTag = "IT_MON_FS";
}
return sMoneyTag;
}
Скрипты и функции выдернуты из целого комплекса систем, поэтому могут быть какие-то не компиляции, но суть мысли должна быть понятна.