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