1 /*
2 This file is part of "Avanor, the Land of Mystery" roguelike game
3 Home page: http://www.avanor.com/
4 Copyright (C) 2000-2003 Vadim Gaidukevich
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "skeep_ai.h"
22 #include "gmsg.h"
23 #include "itemf.h"
24 #include "xapi.h"
25 
26 REGISTER_CLASS(XShopKeeperAI);
27 
XShopKeeperAI(XCreature * shopkeeper,XShop * _shop)28 XShopKeeperAI::XShopKeeperAI(XCreature * shopkeeper, XShop * _shop)
29 	 : XStandardAI(shopkeeper)
30 {
31 	SetShop(_shop);
32 	_shop->SetShopkeeper(shopkeeper);
33 
34 	debt.debtor = NULL;
35 	debt.debtor_sum = 0;
36 	debt.turn_count = 0;
37 	debt.debtor_leave_shop = 0;
38 
39 	SetArea(shop->GetArea(), shop->location->ln);
40 	SetAIFlag(AIF_GUARD_AREA);
41 	SetAIFlag(AIF_GUARD_AREA);
42 	SetAIFlag(AIF_COWARD);
43 	SetAIFlag(AIF_RANDOM_MOVE);
44 	SetEnemyClass(CR_NONE);
45 
46 	ResAIFlag(AIF_ALLOW_PICK_UP);
47 }
48 
Invalidate()49 void XShopKeeperAI::Invalidate()
50 {
51 	shop        = NULL;
52 	debt.debtor = NULL;
53 	debt.item_list.KillAll();
54 	XStandardAI::Invalidate();
55 }
56 
Move()57 void XShopKeeperAI::Move()
58 {
59 	if (ai_owner->l->map->GetItemCount(ai_owner->x, ai_owner->y) == 0 && vRand(50) == 0 && shop->GetArea()->PointIn(ai_owner->x, ai_owner->y))
60 	{
61 		XItem * item = ICREATEA(shop->shop_mask);
62 		item->Identify(1);
63 		item->Drop(ai_owner->l, ai_owner->x, ai_owner->y);
64 	}
65 
66 	XStandardAI::Move();
67 
68 	if (debt.debtor)
69 	{
70 		debt.debtor_sum += debt.debtor_add_value;
71 	}
72 }
73 
74 
onAnyonePickItem(XCreature * customer,XItem * item)75 int XShopKeeperAI::onAnyonePickItem(XCreature * customer, XItem * item)
76 {
77 	if (!(customer->im & IM_HERO)) return 0;
78 	if (!(shop->shop_mask & item->im))
79 		return 1;
80 
81 	if (debt.item_list.size() > 0)
82 	{
83 		msgwin.Add("Pay for what you've picked up already before you take anything else!");
84 		return 0;
85 	}
86 	//hack!!!
87 	XItem * titem = (XItem *)(item->MakeCopy());
88 	titem->x = -1;
89 	titem->y = -1;
90 
91 	debt.item_list.Add(titem);
92 	if (debt.debtor_leave_shop == 0) return 1;
93 
94 	if (!isEnemy(customer))
95 		msgwin.Add(GMSG_SHOPKEEPER_ATTACK);
96 	else
97 		msgwin.Add(GMSG_SHOPKEEPER_ATTACK2);
98 
99 	AddPersonalEnemy(customer);
100 
101 	return 1;
102 }
103 
onAnyoneDropItem(XCreature * customer,XItem * item)104 int XShopKeeperAI::onAnyoneDropItem(XCreature * customer, XItem * item)
105 {
106 //	Only the hero is allowed to sell items at present
107 	if (!(customer->im & IM_HERO)) return 0;
108 
109 //	Shopkeeper takes only items of appropriate type
110 	if (!(item->im & shop->shop_mask))
111 	{
112 		msgwin.Add(GMSG_SHOPKEEPER_REJECT_ITEM);
113 		return 0;
114 	}
115 
116 //	Return taken and unpaid items back to the shop
117 	XList<XItem *>::iterator it = debt.item_list.begin();
118 	while (it != debt.item_list.end())
119 	{
120 		if (item->im != it->im || item->Compare(it) != 0) { it++; continue; }
121 		if (it->quantity > item->quantity)
122 		{
123 			it->quantity -= item->quantity;
124 			return 1;
125 		}
126 
127 		if (it->quantity == item->quantity)
128 		{
129 			debt.item_list.Remove(it);//->Invalidate();
130 			return 1;
131 		}
132 
133 		item->quantity -= it->quantity;
134 		XItem * t = it;
135 		it = debt.item_list.erase(it);
136 		t->Invalidate();
137 	}
138 
139 //	Sell the remaining items to the shopkeeper
140 	char buf[256];
141 	char buf1[256];
142 	item->toString(buf);
143 	int price = (item->GetValue() / 4 + 1) * item->quantity;
144 	sprintf(buf1, GMSG_SHOPKEEPER_ASK_PRICE, price, buf);
145 	msgwin.Add(buf1);
146 
147 	if (customer->GetTarget(TR_NO_YES))
148 	{
149 		int money_to_add = price;
150 		if (debt.debtor == customer)
151 		{
152 			if ((int)debt.debtor_sum > price)
153 			{
154 				debt.debtor_sum -= price;
155 				return 1;
156 			} else
157 			{
158 				money_to_add = price - (int)debt.debtor_sum;
159  				RemovePersonalEnemy(debt.debtor);
160 				debt.debtor_sum = 0;
161 				debt.debtor = NULL;
162 				debt.debtor_leave_shop = 0;
163 				if (money_to_add == 0)
164 					return 1;
165 			}
166 		}
167 		assert(money_to_add > 0);
168 		customer->MoneyOp(money_to_add);
169 		return 1;
170 	}
171 	return 0;
172 }
173 
onGiveItem(XCreature * giver,XItem * item)174 int XShopKeeperAI::onGiveItem(XCreature * giver, XItem * item)
175 {
176 //	Attempt to give item to the shopkeeper results in selling
177 //	of that item (by dropping it to the ground)
178 	if (!(item->im & IM_MONEY))
179 	{
180 		int res = onAnyoneDropItem(giver, item);
181 		if (res)
182 		{
183 			ai_owner->ContainItem(item);
184 		}
185 		return res;
186 	}
187 
188 //	Giving the money to the shopkeeper results in reducing or
189 //	completely clearing off a debt
190  	if (debt.debtor_sum == 0 && debt.item_list.empty())
191  	{
192  		msgwin.Add(GMSG_SHOPKEEPER_REJECT_MONEY);
193  		return 0;
194  	}
195 
196 	item->AddRef(); // prevent money object from destroying
197 
198 	if (!debt.item_list.empty())
199 	{
200 
201 		XList<XItem *>::iterator it = debt.item_list.begin();
202 		while (it != debt.item_list.end() && item->quantity > 0)
203 		{
204 			char buf[256];
205 			char buf1[256];
206 			XItem * titem = it;
207 
208 			titem->toString(buf1);
209 			sprintf(buf, GMSG_SHOPKEEPER_ASK_FOR_PAY, titem->GetValue() * titem->quantity, buf1);
210 			msgwin.Add(buf);
211 			XPoint pt(0, vMin(titem->GetValue() * titem->quantity, item->quantity));
212 			int res = giver->GetTarget(TR_HOW_MUCH, &pt, titem->GetValue() * titem->quantity);
213 			if (res <= 0) break;
214 
215 			giver->MoneyOp(-res);
216 			debt.debtor_sum += titem->GetValue() * titem->quantity - res;
217 			XItem * t = it;
218 			it = debt.item_list.erase(it);
219 			t->Invalidate();
220 			msgwin.Add(GMSG_SHOPKEEPER_THANKS);
221 		}
222 	}
223 
224  	if (debt.debtor_sum > 0 && item->isValid() && item->quantity > 0)
225  	{
226  		char buf[256];
227  		sprintf(buf, GMSG_SHOPKEEPER_ASK_FOR_PAY2, (int)debt.debtor_sum);
228  		msgwin.Add(buf);
229  		XPoint pt(0, item->quantity);
230  		int res = giver->GetTarget(TR_HOW_MUCH, &pt, (int)debt.debtor_sum);
231  		if (res > 0)
232  		{
233  			debt.debtor_sum -= res;
234  			if (debt.debtor_sum < 1)
235  			{
236  				RemovePersonalEnemy(debt.debtor);
237  				debt.debtor            = NULL;
238 				debt.debtor_sum        = 0;
239  				debt.debtor_leave_shop = 0;
240  				debt.debtor_add_value  = 0;
241  			}
242  			giver->MoneyOp(-res);
243  			msgwin.Add(GMSG_SHOPKEEPER_THANKS);
244  		}
245  	}
246 	item->Release();
247 	return 0;
248 }
249 
onCreatureEnterShop(XCreature * customer)250 void XShopKeeperAI::onCreatureEnterShop(XCreature * customer)
251 {
252 	if (customer->im & IM_HERO)
253 	{
254 		if (isEnemy(customer))
255 		{
256 			msgwin.Add("Prepare to die!");
257 		} else
258 		{
259 			char buf[256];
260 			if (debt.debtor_sum > 0)
261 				sprintf(buf, GMSG_SHOPKEEPER_TO_CUSTOMER0, ai_owner->name, (int)debt.debtor_sum);
262 			else
263 				sprintf(buf, GMSG_SHOPKEEPER_TO_CUSTOMER1, ai_owner->name, customer->name);
264 			msgwin.Add(buf);
265 		}
266 	}
267 }
268 
onCreatureLeaveShop(XCreature * customer)269 void XShopKeeperAI::onCreatureLeaveShop(XCreature * customer)
270 {
271 	if (!(customer->im & IM_HERO)) return;
272 
273 	XItem * item;
274 	while ((item = static_cast<XItem *>(debt.item_list.RemoveFirst())) != NULL)
275 	{
276 		debt.debtor = customer;
277 		debt.debtor_sum += item->GetValue() * item->quantity;
278 		item->Invalidate();
279 	}
280 	debt.debtor_add_value = debt.debtor_sum * 0.001;
281 
282 	if (isEnemy(customer))
283 	{
284 		msgwin.Add("You will be dead soon!");
285 	} else
286 	{
287 		char buf[256];
288 		if (debt.debtor_sum > 0)
289 		{
290 			sprintf(buf, GMSG_SHOPKEEPER_TO_CUSTOMER2, ai_owner->name, (int)debt.debtor_sum);
291 			debt.debtor_leave_shop = 1;
292 			debt.debtor = customer;
293 			if (debt.debtor_sum > 150)
294 				AddPersonalEnemy(customer);
295 		}
296 		else
297 		{
298 			sprintf(buf, GMSG_SHOPKEEPER_TO_CUSTOMER3, ai_owner->name, customer->name);
299 		}
300 
301 		msgwin.Add(buf);
302 	}
303 }
304 
Store(XFile * f)305 void XShopKeeperAI::Store(XFile * f)
306 {
307 	XStandardAI::Store(f);
308 	f->Write(&debt.debtor_add_value, sizeof(double));
309 	debt.debtor.Store(f);
310 	f->Write(&debt.debtor_leave_shop, sizeof(int));
311 	f->Write(&debt.debtor_sum, sizeof(double));
312 	debt.item_list.StoreList(f);
313 	f->Write(&debt.turn_count, sizeof(int));
314 	shop.Store(f);
315 }
316 
Restore(XFile * f)317 void XShopKeeperAI::Restore(XFile * f)
318 {
319 	XStandardAI::Restore(f);
320 	f->Read(&debt.debtor_add_value, sizeof(double));
321 	debt.debtor.Restore(f);
322 	f->Read(&debt.debtor_leave_shop, sizeof(int));
323 	f->Read(&debt.debtor_sum, sizeof(double));
324 	debt.item_list.RestoreList(f);
325 	f->Read(&debt.turn_count, sizeof(int));
326 	shop.Restore(f);
327 }
328 
329