game_processing.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. #ifndef __GAME_PROCESSING_H
  2. #define __GAME_PROCESSING_H
  3. #include <valarray>
  4. void add_ailments(Player& p, GameState& state) {
  5. const auto& ailments = constants().ailments;
  6. if (ailments.empty())
  7. return;
  8. unsigned int num_ails = p.num_replay_codes / 2;
  9. num_ails = std::min(num_ails, constants().max_ailments);
  10. for (unsigned int i = 0; i < num_ails; ++i) {
  11. size_t na = state.rng.n(ailments.size());
  12. auto a = ailments.begin();
  13. while (na > 0) {
  14. ++a;
  15. if (a->second.triggers == 0) {
  16. if (a == ailments.end())
  17. a = ailments.begin();
  18. continue;
  19. }
  20. --na;
  21. }
  22. const auto& ailment = a->second;
  23. p.add_ailment(state.rng, a->first, ailment.triggers);
  24. }
  25. if (num_ails > 0) {
  26. auto trig = state.triggers.insert(std::make_pair(1, GameState::trigger_t()));
  27. auto& message = trig->second.message;
  28. std::string& msg = message.message;
  29. message.important = true;
  30. switch (num_ails) {
  31. case 1:
  32. msg = "This reincarnation of your body seems frail. (Press '@')"_m;
  33. break;
  34. case 2:
  35. msg = "This reincarnation of your body is very frail."_m;
  36. break;
  37. case 3:
  38. msg = "This reincarnation of your body is extremely fragile."_m;
  39. break;
  40. default:
  41. msg = "This reincarnation of your body is no good at all."_m;
  42. break;
  43. }
  44. }
  45. }
  46. bool process_ailment(tag_t ail, Player& p, GameState& state) {
  47. auto a = constants().ailments.find(ail);
  48. if (a == constants().ailments.end())
  49. throw std::runtime_error("Unknown ailment.");
  50. const auto& ailment = a->second;
  51. int n = defend(p, ailment, state);
  52. for (const auto& z : ailment.inc_stat) {
  53. if (p.stats.sinc(z.first, z.second))
  54. p.dead = true;
  55. }
  56. return (ailment.oneshot && n > 0);
  57. }
  58. void Game::init(GameState& state, unsigned int address, unsigned int seed) {
  59. game_seed = seed;
  60. // Figure out how many replay codes were used.
  61. p.num_replay_codes = bones::bones().get_replay_code_count(address, seed);
  62. // Add some ailments.
  63. add_ailments(p, state);
  64. // Calculate the player's starsign.
  65. // The zero date of July 30 2012 is the mythological date of first git commit in this repo. :)
  66. const auto& starsigns = constants().starsigns;
  67. p.starsign.init(seed, starsigns.zero, starsigns.nday, starsigns.nsign);
  68. // Change item counts based on moon phase, item flavor and starsign.
  69. const auto& fmf = constants().flavor_moon_frequency;
  70. double phase = state.moon.pi.phase_n;
  71. const auto& starsign = p.starsign;
  72. state.designs_counts.change_counts(
  73. [&](tag_t tag, unsigned int _count) {
  74. const Design& d = designs().get(tag);
  75. tag_t flavor = d.flavor;
  76. auto i = fmf.find(flavor);
  77. double mult = 1.0;
  78. if (i != fmf.end()) {
  79. const auto& f = i->second;
  80. mult =
  81. gaussian_function(f.height, f.curve.mean, f.curve.deviation, phase) +
  82. gaussian_function(f.height, f.curve.mean, f.curve.deviation, 1.0 - phase);
  83. }
  84. int count = _count;
  85. if (d.starsign.sign > 0 && d.starsign.sign != starsign.sign) {
  86. count = -1;
  87. } else if (d.starsign.day > 0) {
  88. count -= std::abs((int)d.starsign.day - (int)starsign.day);
  89. }
  90. count = mult * count;
  91. if (count == 0) {
  92. count = 1;
  93. } else if (count < 0) {
  94. count = 0;
  95. }
  96. return count;
  97. });
  98. }
  99. void Game::dispose(GameState& state) {
  100. std::vector<items::Item> vic;
  101. std::map<items::pt,items::Item> safe;
  102. tag_t vic_tag = constants().unique_item;
  103. state.items.dispose(state.designs_counts,
  104. [&](const items::Item& i) {
  105. if (i.tag == vic_tag) {
  106. vic.push_back(i);
  107. return false;
  108. }
  109. features::Feature feat;
  110. if (state.features.get(i.xy.first, i.xy.second, feat)) {
  111. const Terrain& t = terrain().get(feat.tag);
  112. if (t.safebox && safe.count(i.xy) == 0) {
  113. safe[i.xy] = i;
  114. return false;
  115. }
  116. }
  117. return true;
  118. });
  119. state.monsters.dispose(state.species_counts);
  120. if (vic.size() > 0 && !p.uniques_disabled) {
  121. uniques::uniques().put(p.current_wx, p.current_wy, p.current_wz, vic, p.dungeon_unique_series);
  122. }
  123. vic.clear();
  124. for (const auto& i : safe) {
  125. vic.push_back(i.second);
  126. }
  127. if (vic.size() > 0) {
  128. uniques::items().put(p.current_wx, p.current_wy, p.current_wz, vic, 1);
  129. }
  130. }
  131. void Game::endgame(GameState& state, const std::string& name, unsigned int address, unsigned int seed) {
  132. // HACK.
  133. // The special player name of '_' will not leave highscores or bones.
  134. if (name != "_") {
  135. std::string polyform;
  136. if (!p.polymorph.species.null()) {
  137. polyform = nlp::message(" %s", p.get_species());
  138. }
  139. bones::bones().add(name, p, constants().achievements, polyform, address, seed);
  140. }
  141. const Design& d_victory = designs().get(constants().unique_item);
  142. p.inv.inv_to_floor(d_victory.slot, p.px, p.py, state.items, state.render);
  143. dispose(state);
  144. }
  145. bool process_feature(GameState& state, features::Feature& f, const Terrain& t) {
  146. if (f.decay > 0) {
  147. --(f.decay);
  148. if (f.decay == 0)
  149. return false;
  150. }
  151. return true;
  152. }
  153. unsigned int summon_out_of_view(const Player& p, GameState& state, tag_t monster, unsigned int count) {
  154. int radius = get_lightradius(p, state) + 1;
  155. std::unordered_set<monsters::pt> range;
  156. radial_points(p.px, p.py, state, radius, range, player_walkable);
  157. unsigned int res = state.monsters.summon(state.neigh, state.rng, state.grid,
  158. state.species_counts, state.render,
  159. range, p.px, p.py, monster, count, false);
  160. return res;
  161. }
  162. inline bool give_change(GameState& state, unsigned int x, unsigned int y, double amount) {
  163. if (amount <= constants().min_money_value)
  164. return false;
  165. std::map< double, std::pair<double, tag_t> > currencies;
  166. for (const auto& i : constants().money) {
  167. const Design& m = designs().get(i);
  168. currencies[m.worth * m.stackrange] = std::make_pair(m.worth, i);
  169. }
  170. if (currencies.empty())
  171. return false;
  172. for (const auto& i : currencies) {
  173. if (amount <= i.first) {
  174. items::Item zm(i.second.second, items::pt(x, y),
  175. std::max(1u, (unsigned int)(amount / i.second.first)));
  176. state.items.place(x, y, zm, state.render);
  177. return true;
  178. }
  179. }
  180. tag_t maxc = currencies.rbegin()->second.second;
  181. const Design& m = designs().get(maxc);
  182. items::Item zm(maxc, items::pt(x, y), m.stackrange);
  183. state.items.place(x, y, zm, state.render);
  184. return true;
  185. }
  186. void finish_digging(const Player& p, GameState& state, unsigned int x, unsigned int y, double h) {
  187. features::Feature feat;
  188. if (!state.grid.is_walk(x, y)) {
  189. bool water = state.grid.is_water(x, y);
  190. state.grid.set_walk_water(state.neigh, x, y, true, water);
  191. state.render.invalidate(x, y);
  192. permafeats::features().add(p, x, y, true, water);
  193. } else if (state.features.get(x, y, feat) && feat.tag == constants().grave) {
  194. state.features.set(x, y, constants().bad_grave, state.render);
  195. permafeats::features().add(p, x, y, constants().bad_grave);
  196. bones::bone_t bone;
  197. if (!bones::bones().get(p, x, y, bone))
  198. return;
  199. auto trig = state.triggers.insert(std::make_pair(state.ticks, GameState::trigger_t()));
  200. auto& summon = trig->second.summon_genus;
  201. summon.genus = constants().ghost;
  202. summon.level = bone.level;
  203. summon.count = 1;
  204. summon.x = x;
  205. summon.y = y;
  206. give_change(state, x, y, bone.worth);
  207. } else {
  208. state.features.set(x, y, constants().pit, state.render);
  209. permafeats::features().add(p, x, y, constants().pit);
  210. const Levelskin& ls = levelskins().get(p.worldz);
  211. if (!ls.has_treasure)
  212. return;
  213. const auto& tc = constants().treasure_chance;
  214. double lev = state.rng.gauss(h + tc.mean, tc.deviation);
  215. if (lev < 0 || lev + ls.treasure_level < 0)
  216. return;
  217. int tlev = lev + ls.treasure_level;
  218. auto is = state.designs_counts.take(state.rng, tlev);
  219. for (const auto& ii : is) {
  220. items::Item made = state.items.make_item(ii.first, items::pt(x, y), state.rng);
  221. state.items.place(x, y, made, state.render);
  222. state.render.do_message(nlp::message("You found %s!"_m, nlp::count(),
  223. designs().get(made.tag), made.count));
  224. }
  225. }
  226. }
  227. void do_digging_step(Player& p, GameState& state) {
  228. double digspeed = p.inv.get_digging();
  229. float& height = state.grid._get(p.dig.x, p.dig.y);
  230. height -= digspeed;
  231. if (height <= -10) {
  232. height = -10;
  233. p.stats.counts.erase(constants().digging_count);
  234. state.render.do_message("Digging done."_m);
  235. finish_digging(p, state, p.dig.x, p.dig.y, p.dig.h);
  236. }
  237. }
  238. void Game::process_world(GameState& state,
  239. bool& done, bool& dead, bool& regen, bool& need_input, bool& do_draw) {
  240. //
  241. const auto& consts = constants();
  242. // Ailments.
  243. if (p.ailments.size() > 0) {
  244. unsigned int t = state.rng.range(0u, 99u);
  245. auto a = p.ailments.find(t);
  246. if (a != p.ailments.end()) {
  247. if (process_ailment(a->second, p, state)) {
  248. p.ailments.erase(a);
  249. }
  250. }
  251. }
  252. //
  253. //
  254. state.camap.step(
  255. state.neigh,
  256. // Check for cells
  257. [&state](unsigned int x, unsigned int y, const CelAuto& ca) {
  258. if (state.grid.is_walk(x, y) == ca.is_walk &&
  259. state.grid.get_karma(x, y) * ca.karma_scale < 1.0) {
  260. return true;
  261. }
  262. return false;
  263. },
  264. // Cell on
  265. [&state](unsigned int x, unsigned int y, const CelAuto& ca) {
  266. if (ca.make_walk) {
  267. state.grid.set_walk_water(state.neigh, x, y, true, state.grid.is_water(x, y));
  268. state.render.invalidate(x, y);
  269. }
  270. state.features.x_set(x, y, ca.terrain, state.render);
  271. state.grid.get_karma(x, y) += ca.karma_step;
  272. },
  273. // Cell off
  274. [&state](unsigned int x, unsigned int y, const CelAuto& ca) {
  275. state.features.x_unset(x, y, ca.terrain, state.render);
  276. },
  277. // Max number of cells
  278. consts.max_celauto_cells
  279. );
  280. bool terrain_immune = p.get_species().flags.terrain_immune;
  281. features::Feature feat;
  282. if (!terrain_immune && state.features.get(p.px, p.py, feat)) {
  283. const Terrain& t = terrain().get(feat.tag);
  284. if (!t.attacks.empty()) {
  285. damage::defenses_t defenses;
  286. p.get_defense(defenses);
  287. defend(p, defenses, p.get_computed_level(), t, state);
  288. if (t.uncharge.attack) {
  289. state.features.uncharge(p.px, p.py, state.render);
  290. }
  291. }
  292. }
  293. std::vector<summons_t> summons;
  294. if (state.triggers.size() > 0) {
  295. auto i = state.triggers.begin();
  296. while (i != state.triggers.end() && i->first <= state.ticks) {
  297. const auto& trig = i->second;
  298. if (!trig.summon_out_of_view.monster.null()) {
  299. unsigned int n = summon_out_of_view(p, state,
  300. trig.summon_out_of_view.monster,
  301. trig.summon_out_of_view.count);
  302. if (n == 0) {
  303. ++i;
  304. continue;
  305. }
  306. }
  307. if (trig.summon_genus.count > 0) {
  308. const auto& sg = trig.summon_genus;
  309. summons.emplace_back(sg.x, sg.y, summons_t::type_t::GENUS,
  310. sg.genus, sg.level, sg.count, tag_t(), tag_t(), "");
  311. }
  312. if (!trig.summon.species.null()) {
  313. summons.emplace_back(trig.summon.x, trig.summon.y, summons_t::type_t::SPECIFIC,
  314. trig.summon.species, 0, trig.summon.count, tag_t(), trig.summon.ally, "");
  315. }
  316. if (trig.message.message.size() > 0) {
  317. state.render.do_message(trig.message.message, trig.message.important);
  318. }
  319. i = state.triggers.erase(i);
  320. }
  321. }
  322. state.monsters.find_nearest(state.grid.w, state.grid.h, p.px, p.py);
  323. state.monsters.process(state.render,
  324. std::bind(move_monster, std::ref(p), std::ref(state), std::ref(summons),
  325. std::placeholders::_1, std::placeholders::_2,
  326. std::placeholders::_3, std::placeholders::_4,
  327. std::placeholders::_5),
  328. std::bind(conflict_monster, std::ref(p), std::ref(state),
  329. std::placeholders::_1, std::placeholders::_2,
  330. std::placeholders::_3, std::placeholders::_4));
  331. state.features.process(state.render,
  332. std::bind(process_feature, std::ref(state),
  333. std::placeholders::_1, std::placeholders::_2));
  334. for (const auto& i : summons) {
  335. unsigned int nm = 0;
  336. switch (i.type) {
  337. case summons_t::type_t::SPECIFIC:
  338. nm = state.monsters.summon(state.neigh, state.rng, state.grid, state.species_counts, state.render,
  339. i.x, i.y, p.px, p.py, i.summontag, i.count, false, i.ally);
  340. break;
  341. case summons_t::type_t::LEVEL:
  342. nm = state.monsters.summon_any(state.neigh, state.rng, state.grid, state.species_counts, state.render,
  343. i.x, i.y, p.px, p.py, i.level, i.count, i.ally);
  344. break;
  345. case summons_t::type_t::GENUS:
  346. nm = state.monsters.summon_genus(state.neigh, state.rng, state.grid, state.species_counts, state.render,
  347. i.x, i.y, p.px, p.py, i.summontag, i.level, i.count, i.ally);
  348. break;
  349. }
  350. if (nm > 0 && state.render.is_in_fov(i.x, i.y) && i.msg.size() > 0) {
  351. if (i.summontag.null()) {
  352. state.render.do_message(nlp::message(i.msg, species().get(i.summonertag)));
  353. } else {
  354. state.render.do_message(nlp::message(i.msg, species().get(i.summonertag),
  355. nlp::count(), species().get(i.summontag), nm));
  356. }
  357. }
  358. }
  359. for (std::vector<Terrain::spell_t>::iterator si = p.spells.begin(); si != p.spells.end(); ) {
  360. auto& sp = *si;
  361. double k = p.stats.gets(sp.stat);
  362. if (k > sp.stat_max) {
  363. sp.timeout -= (k - sp.stat_max);
  364. } else if (k < sp.stat_min) {
  365. sp.timeout -= (sp.stat_min - k);
  366. }
  367. if (sp.timeout <= 0) {
  368. si = p.spells.erase(si);
  369. state.render.do_message("You feel a sense of arcane foreboding."_m, true);
  370. } else {
  371. ++si;
  372. }
  373. }
  374. std::map<tag_t, double> inc_stat = p.get_species().inc_stat;
  375. p.inv.process_inventory(state.moon.pi.phase_n, inc_stat);
  376. for (const auto& i : inc_stat) {
  377. if (p.stats.sinc(i.first, i.second))
  378. p.dead = true;
  379. }
  380. if (p.dead && !p.stats.crit())
  381. p.dead = false;
  382. {
  383. const Levelskin& ls = levelskins().get(p.worldz);
  384. if (!ls.ailment.null() && !p.dead) {
  385. process_ailment(ls.ailment, p, state);
  386. }
  387. }
  388. for (const auto& i : p.stats.stats) {
  389. const Stat& st = stats().get(i.first);
  390. if (!st.ailment.null() && i.second.val <= st.min && !p.dead) {
  391. process_ailment(st.ailment, p, state);
  392. }
  393. }
  394. p.stats.tick();
  395. p.blind = 0;
  396. p.stun = false;
  397. p.fear = false;
  398. p.sleep = false;
  399. for (const auto& i : p.stats.counts) {
  400. const Count& ct = counts().get(i.first);
  401. if (ct.blind) p.blind = (double)i.second.val / (double)ct.cmax;
  402. if (ct.stun) p.stun = true;
  403. if (ct.fear) p.fear = true;
  404. if (ct.sleep) p.sleep = true;
  405. if (!ct.ailment.null() && !p.dead) {
  406. process_ailment(ct.ailment, p, state);
  407. }
  408. }
  409. if (p.polymorph.turns > 0) {
  410. --(p.polymorph.turns);
  411. if (p.polymorph.turns == 0) {
  412. p.polymorph.species = tag_t();
  413. p.stats = p.get_species().stats;
  414. state.render.invalidate(p.px, p.py);
  415. }
  416. }
  417. if (p.fast.turns > 0) {
  418. --(p.fast.turns);
  419. }
  420. if (p.dead) {
  421. std::string code = rcode::encode(game_seed);
  422. state.render.do_message("You are dead. (Press space to exit.)"_m, true);
  423. state.render.do_message(nlp::message("Replay code: %s"_m, code), true);
  424. dead = true;
  425. done = true;
  426. } else if (p.sleep) {
  427. ++(state.ticks);
  428. do_draw = true;
  429. } else if (p.stats.counts.count(constants().digging_count) != 0) {
  430. ++(state.ticks);
  431. do_draw = true;
  432. do_digging_step(p, state);
  433. }
  434. }
  435. void genocide(GameState& state, tag_t genus) {
  436. state.species_counts.change_counts(
  437. [genus](tag_t sp, unsigned int count) {
  438. if (species().get(sp).genus == genus) {
  439. return 0u;
  440. } else {
  441. return count;
  442. }
  443. });
  444. }
  445. inline size_t longest_common_subsequence(const std::string& a, const std::string& b) {
  446. size_t w = b.size() + 1;
  447. size_t h = a.size() + 1;
  448. std::valarray<size_t> c(w * h);
  449. for (size_t i = 0; i < a.size(); ++i) {
  450. for (size_t j = 0; j < b.size(); ++j) {
  451. size_t ix = w * (i + 1) + (j + 1);
  452. if (a[i] == b[j]) {
  453. c[ix] = c[w * i + j] + 1;
  454. } else {
  455. c[ix] = std::max(c[w * (i + 1) + j], c[w * i + (j + 1)]);
  456. }
  457. }
  458. }
  459. return c[w * h - 1];
  460. }
  461. inline tag_t find_existing_item_search(GameState& state, const std::string& name, bool wished, bool bought) {
  462. const auto& data = state.designs_counts.data;
  463. size_t maxlcs = 0;
  464. std::vector<double> counts;
  465. std::vector<tag_t> desgns;
  466. for (const auto& i : data) {
  467. for (const auto& j : i.second) {
  468. const Design& _design = designs().get(j.first);
  469. // HACK
  470. if (bought && _design.forbid_buy)
  471. continue;
  472. if (wished && _design.forbid_wish)
  473. continue;
  474. size_t lcs = longest_common_subsequence(_design.name, name);
  475. if (lcs < maxlcs)
  476. continue;
  477. if (lcs > maxlcs) {
  478. maxlcs = lcs;
  479. counts.clear();
  480. desgns.clear();
  481. }
  482. counts.push_back(j.second);
  483. desgns.push_back(j.first);
  484. }
  485. }
  486. while (1) {
  487. if (counts.empty()) {
  488. return tag_t();
  489. }
  490. std::discrete_distribution<unsigned int> d(counts.begin(), counts.end());
  491. unsigned int ntag = d(state.rng.gen);
  492. tag_t design = desgns[ntag];
  493. return design;
  494. }
  495. }
  496. inline void find_existing_item_make(GameState& state, unsigned int px, unsigned int py, tag_t design) {
  497. unsigned int trucount = state.designs_counts.take(design);
  498. if (trucount == 0) {
  499. state.render.do_message("Strange. Nothing happened."_m);
  500. return;
  501. }
  502. const Design& _design = designs().get(design);
  503. items::Item made = state.items.make_item(design, items::pt(px, py), state.rng);
  504. state.items.place(px, py, made, state.render);
  505. state.render.do_message(nlp::message("You see %s."_m, nlp::count(), _design, made.count));
  506. }
  507. inline bool find_existing_item(GameState& state, unsigned int px, unsigned int py, const std::string& name) {
  508. tag_t d = find_existing_item_search(state, name, true, false);
  509. if (d.null()) {
  510. state.render.do_message("Strange. Nothing happened."_m);
  511. return false;
  512. }
  513. find_existing_item_make(state, px, py, d);
  514. return true;
  515. }
  516. inline bool find_any_item(GameState& state, Player& p, unsigned int px, unsigned int py, const std::string& name) {
  517. const auto& d = designs();
  518. size_t maxlcs = 0;
  519. std::vector<tag_t> desgns;
  520. for (const auto& i : d.bank) {
  521. const Design& _design = designs().get(i.first);
  522. if (_design.wishing)
  523. continue;
  524. size_t lcs = longest_common_subsequence(_design.name, name);
  525. if (lcs < maxlcs)
  526. continue;
  527. if (lcs > maxlcs) {
  528. maxlcs = lcs;
  529. desgns.clear();
  530. }
  531. desgns.push_back(i.first);
  532. }
  533. if (desgns.empty()) {
  534. state.render.do_message("Strange. Nothing happened."_m);
  535. return false;
  536. }
  537. tag_t design = desgns[state.rng.n(desgns.size())];
  538. const Design& _design = designs().get(design);
  539. items::Item made = state.items.make_item(design, items::pt(px, py), state.rng);
  540. // HACK
  541. if (design == constants().unique_item) {
  542. p.uniques_disabled = true;
  543. // TODO Really this is just a way to hardcode a value.
  544. made.count = 10000 * _design.change_count;
  545. }
  546. state.items.place(px, py, made, state.render);
  547. state.render.do_message(nlp::message("You see %s."_m, nlp::count(), _design, made.count));
  548. return true;
  549. }
  550. inline bool simple_wish(GameState& state, Player& p, const std::string& wish) {
  551. if (wish.empty())
  552. return true;
  553. return find_existing_item(state, p.px, p.py, wish);
  554. }
  555. inline bool special_wish(GameState& state, Player& p, const std::string& wish) {
  556. return find_any_item(state, p, p.px, p.py, wish);
  557. }
  558. #endif