bank_screens.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. #ifndef __BANK_SCREENS_H
  2. #define __BANK_SCREENS_H
  3. inline bool purchase_protection(Player& p, GameState& state, double cost, const Terrain::banking_t& bank) {
  4. double deflation = finance::supply().get_rate();
  5. double stat_bonus = bank.stat_bonus * deflation * cost;
  6. double money_curse = bank.money_curse * cost;
  7. double sstat = p.stats.gets(bank.bonus_stat);
  8. double ssmax = stats().get(bank.bonus_stat).max;
  9. if (stat_bonus <= 0 || sstat >= ssmax)
  10. return true;
  11. items::Item money;
  12. if (!p.inv.take(constants().money_slot, money))
  13. return true;
  14. double xcost = cost;
  15. if (sstat + stat_bonus >= ssmax) {
  16. stat_bonus = ssmax - sstat;
  17. xcost = stat_bonus / (bank.stat_bonus * deflation);
  18. money_curse = bank.money_curse * xcost;
  19. }
  20. if (stat_bonus > 0) {
  21. if (p.stats.sinc(bank.bonus_stat, stat_bonus))
  22. p.dead = true;
  23. state.render.do_message("Your body glows with a shiny gold aura."_m);
  24. ++(state.ticks);
  25. }
  26. if (money_curse > 0) {
  27. p.money_curse -= money_curse;
  28. }
  29. if (give_change(state, p.px, p.py, cost - xcost)) {
  30. state.render.do_message("Please keep the change."_m);
  31. }
  32. bool valid = true;
  33. finance::supply().purchase(tag_t(), xcost, valid);
  34. if (!valid) {
  35. state.render.do_message("Thank you for your patronage. We are now closed for business."_m, true);
  36. }
  37. return valid;
  38. }
  39. inline bool purchase_item(Player& p, GameState& state) {
  40. tag_t pitem = p.banking.item;
  41. if (p.banking.assets < p.banking.item_price || pitem.null())
  42. return true;
  43. unsigned int trucount = state.designs_counts.take(pitem);
  44. if (trucount == 0) {
  45. state.render.do_message("Strange. Nothing happened."_m);
  46. return true;
  47. }
  48. items::Item money;
  49. if (!p.inv.take(constants().money_slot, money))
  50. return true;
  51. items::Item made(pitem, items::pt(p.px, p.py), p.banking.item_count);
  52. state.items.place(p.px, p.py, made, state.render);
  53. ++(state.ticks);
  54. if (give_change(state, p.px, p.py, p.banking.assets - p.banking.item_price)) {
  55. state.render.do_message("Please keep the change."_m);
  56. }
  57. bool valid = true;
  58. if (constants().money.count(money.tag) == 0) {
  59. const Design& md = designs().get(money.tag);
  60. unsigned int count = (md.count_is_only_one ? 1 : money.count);
  61. finance::supply().purchase(money.tag, 0, valid, count);
  62. }
  63. if (valid) {
  64. finance::supply().purchase(pitem, p.banking.item_price, valid);
  65. }
  66. if (!valid) {
  67. state.render.do_message("Thank you for your patronage. We are now closed for business."_m, true);
  68. }
  69. return valid;
  70. }
  71. inline bool account_deposit(Player& p, GameState& state, unsigned int account) {
  72. if (p.banking.assets < constants().min_money_value) {
  73. return true;
  74. }
  75. items::Item money;
  76. if (!p.inv.take(constants().money_slot, money))
  77. return true;
  78. bool valid = true;
  79. double b = finance::supply().deposit(account, p.banking.assets, valid);
  80. if (!valid) {
  81. state.render.do_message("Thank you for your patronage. We are now closed for business."_m, true);
  82. } else {
  83. state.render.do_message(nlp::message("Thank you for your patronage. Your account balance: %f $ZM."_m, b));
  84. }
  85. ++(state.ticks);
  86. return valid;
  87. }
  88. inline bool account_withdraw(Player& p, GameState& state, unsigned int account) {
  89. bool valid = true;
  90. double sum = finance::supply().withdraw(account, valid);
  91. if (give_change(state, p.px, p.py, sum) && valid) {
  92. state.render.do_message(nlp::message("%f $ZM withdrawn. Enjoy your newfound wealth."_m, sum));
  93. } else {
  94. state.render.do_message("This account is empty, sorry."_m);
  95. }
  96. ++(state.ticks);
  97. return valid;
  98. }
  99. inline void give_small_change(Player& p, GameState& state, double increments) {
  100. if (increments <= constants().min_money_value) {
  101. return;
  102. }
  103. items::Item money;
  104. if (!p.inv.take(constants().money_slot, money))
  105. return;
  106. double assets = p.banking.assets;
  107. while (assets > constants().min_money_value) {
  108. double x = std::min(increments, assets);
  109. give_change(state, p.px, p.py, x);
  110. assets -= x;
  111. }
  112. }
  113. inline bool handle_input_pincode(Player& p, GameState& state, maudit::keypress k) {
  114. if (k.letter >= '0' && k.letter <= '9') {
  115. if (p.input.s.size() < 3) {
  116. p.input.s += k.letter;
  117. state.window_stack.back().message += '*';
  118. }
  119. } else if (k.letter == '\x7F' || k.letter == '\x08' || k.key == maudit::keycode::del) {
  120. if (p.input.s.size() > 0) {
  121. p.input.s.pop_back();
  122. state.window_stack.back().message.pop_back();
  123. }
  124. } else if (k.letter == '\n' && p.input.s.size() == 3) {
  125. return true;
  126. }
  127. return false;
  128. }
  129. inline bool handle_input_text(Player& p, GameState& state, maudit::keypress k) {
  130. if (k.letter >= ' ' && k.letter <= '~') {
  131. p.input.s += k.letter;
  132. state.window_stack.back().message += k.letter;
  133. } else if (k.letter == '\x7F' || k.letter == '\x08' || k.key == maudit::keycode::del) {
  134. if (p.input.s.size() > 0) {
  135. p.input.s.pop_back();
  136. state.window_stack.back().message.pop_back();
  137. }
  138. } else if (k.letter == '\n') {
  139. return true;
  140. }
  141. return false;
  142. }
  143. inline void show_banking_buy_item_menu(Player& p, GameState& state, const Terrain::banking_t& bank) {
  144. tag_t item = find_existing_item_search(state, p.input.s, false, true);
  145. if (item.null()) {
  146. //state.window_stack.clear();
  147. state.window_stack.pop_back();
  148. state.push_window("No such item exists. Please try again."_m, screens_t::messages);
  149. return;
  150. }
  151. const Design& d = designs().get(item);
  152. std::string nums;
  153. for (unsigned char c : p.input.s) {
  154. if (c >= '0' && c <= '9') {
  155. nums += c;
  156. } else if (nums.size() > 0) {
  157. break;
  158. }
  159. }
  160. unsigned int count = 1;
  161. if (nums.size() > 0) {
  162. count = std::max(count, std::min((unsigned int)std::stoul(nums), d.stackrange));
  163. }
  164. double price = finance::supply().get_price(d) * count * bank.sell_margin;
  165. std::string msg;
  166. if (price < constants().min_money_value) {
  167. msg = nlp::message("\n"
  168. "Sorry, but \3%s\1 is not currently for sale."_m,
  169. nlp::count(), d, count);
  170. //state.window_stack.clear();
  171. state.window_stack.pop_back();
  172. state.push_window(msg, screens_t::messages);
  173. return;
  174. } else {
  175. msg = nlp::message("\n"
  176. "Your assets: \2%f\1 $ZM.\n"
  177. "Quote for \3%s\1: \2%f\1 $ZM.\n\n"_m,
  178. p.banking.assets, nlp::count(), d, count, price);
  179. }
  180. if (p.banking.assets < price) {
  181. msg += "You cannot afford this item, sorry."_m;
  182. //state.window_stack.clear();
  183. state.window_stack.pop_back();
  184. state.push_window(msg, screens_t::messages);
  185. } else {
  186. msg += " \2y\1) Yes, I agree to buy this item.\n"_m;
  187. p.banking.item = item;
  188. p.banking.item_price = price;
  189. p.banking.item_count = count;
  190. state.window_stack.pop_back();
  191. state.push_window(msg, screens_t::bank_buy_confirm);
  192. }
  193. }
  194. inline bool handle_input_banking_main(Player& p, GameState& state, maudit::keypress k, const Terrain::banking_t& bank) {
  195. switch (k.letter) {
  196. case 'w':
  197. p.input.s.clear();
  198. state.push_window("\2Input your account's PIN code (three digits)\1: \3"_m,
  199. screens_t::bank_withdrawal);
  200. break;
  201. case 'd':
  202. if (p.banking.assets < constants().min_money_value) {
  203. state.window_stack.pop_back();
  204. } else {
  205. p.input.s.clear();
  206. state.push_window("\2Choose a PIN code for your account (three digits)\1: \3"_m,
  207. screens_t::bank_deposit);
  208. }
  209. break;
  210. case 'p':
  211. state.window_stack.pop_back();
  212. return true;
  213. case 'i':
  214. case 'b':
  215. if (p.banking.assets < constants().min_money_value) {
  216. state.window_stack.pop_back();
  217. } else {
  218. p.input.s.clear();
  219. state.push_window("\2Buy what (enter the name)\1: \3"_m, screens_t::bank_buy);
  220. }
  221. break;
  222. case 'c':
  223. give_small_change(p, state, bank.gives_change);
  224. state.window_stack.clear();
  225. break;
  226. default:
  227. state.window_stack.pop_back();
  228. break;
  229. }
  230. return false;
  231. }
  232. inline std::string show_banking_menu(Player& p, GameState& state, tag_t terrain, const Terrain::banking_t& bank) {
  233. const auto& money = constants().money;
  234. if (money.empty()) {
  235. return "Sorry, money doesn't exist yet."_m;
  236. }
  237. tag_t money_slot = constants().money_slot;
  238. p.banking.terrain = terrain;
  239. double& assets = p.banking.assets;
  240. assets = 0;
  241. std::string msg = "\2Welcome to Frobozz Bank.\n\n"_m;
  242. items::Item vi;
  243. if (p.inv.get(money_slot, vi)) {
  244. const Design& liq = designs().get(vi.tag);
  245. unsigned int count = (liq.count_is_only_one ? 1 : vi.count);
  246. if (money.count(vi.tag) != 0) {
  247. assets = liq.worth * count;
  248. msg += nlp::message("Your liquid assets: \2%f\1 $ZM.\n"_m, assets);
  249. } else {
  250. assets = finance::supply().get_price(liq, true) * count * bank.buy_margin;
  251. if (assets < 0) {
  252. assets = 0;
  253. msg += nlp::message("You have no liquid assets. (Suspected countefeit goods: %s)\n"_m,
  254. nlp::count(), liq, vi.count);
  255. } else if (assets == 0) {
  256. msg += nlp::message("You have no liquid assets.\n"_m);
  257. } else {
  258. msg += nlp::message("Your liquid assets: \2%f\1 $ZM. (%S)\n"_m,
  259. assets, nlp::count(), liq, vi.count);
  260. }
  261. }
  262. } else {
  263. msg += nlp::message("You have no liquid assets.\n"_m);
  264. }
  265. msg += "\nPlease choose:\n\n"_m;
  266. msg += " \2w\1) Withdraw from an account.\n"_m;
  267. if (assets >= constants().min_money_value) {
  268. msg += " \2d\1) Deposit to an account.\n"_m;
  269. if (bank.stat_bonus > 0) {
  270. if (p.stats.gets(bank.bonus_stat) < stats().get(bank.bonus_stat).max) {
  271. msg += " \2p\1) Purchase divine protection.\n"_m;
  272. }
  273. }
  274. if (bank.sell_margin > 0) {
  275. msg += " \2i\1) Buy an item.\n"_m;
  276. }
  277. if (bank.gives_change > 0) {
  278. msg += " \2c\1) Exchange assets for coins.\n"_m;
  279. }
  280. }
  281. return msg;
  282. }
  283. inline void handle_input_banking(Player& p, GameState& state, maudit::keypress k) {
  284. const Terrain::banking_t& bank = terrain().get(p.banking.terrain).banking;
  285. bool valid = true;
  286. switch ((screens_t)state.window_stack.back().type) {
  287. case screens_t::bank_main:
  288. if (handle_input_banking_main(p, state, k, bank)) {
  289. valid = purchase_protection(p, state, p.banking.assets, bank);
  290. }
  291. break;
  292. case screens_t::bank_withdrawal:
  293. if (handle_input_pincode(p, state, k)) {
  294. valid = account_withdraw(p, state, std::stoul(p.input.s));
  295. state.window_stack.clear();
  296. }
  297. break;
  298. case screens_t::bank_deposit:
  299. if (handle_input_pincode(p, state, k)) {
  300. valid = account_deposit(p, state, std::stoul(p.input.s));
  301. state.window_stack.clear();
  302. }
  303. break;
  304. case screens_t::bank_buy:
  305. if (handle_input_text(p, state, k)) {
  306. show_banking_buy_item_menu(p, state, bank);
  307. }
  308. break;
  309. case screens_t::bank_buy_confirm:
  310. if (k.letter == 'y') {
  311. valid = purchase_item(p, state);
  312. state.window_stack.clear();
  313. } else {
  314. state.window_stack.pop_back();
  315. }
  316. break;
  317. default:
  318. break;
  319. }
  320. // HACK
  321. if (!valid) {
  322. features::Feature feat;
  323. if (state.features.get(p.px, p.py, feat) && p.banking.terrain == feat.tag) {
  324. state.features.x_unset(p.px, p.py, feat.tag, state.render);
  325. }
  326. }
  327. }
  328. #endif