1 /**
2 * @file
3 * @brief Level markers (annotations).
4 **/
5
6 #include "AppHdr.h"
7
8 #include "mapmark.h"
9
10 #include <algorithm>
11
12 #include "beh-type.h"
13 #include "cluautil.h"
14 #include "coordit.h"
15 #include "dlua.h"
16 #include "end.h"
17 #include "env.h"
18 #include "libutil.h"
19 #include "l-libs.h"
20 #include "map-marker-type.h"
21 #include "mpr.h"
22 #include "stringutil.h"
23 #include "tag-version.h"
24 #include "terrain.h"
25 #include "unwind.h"
26
27 ////////////////////////////////////////////////////////////////////////
28 // Dungeon markers
29
30 map_marker::marker_reader map_marker::readers[NUM_MAP_MARKER_TYPES] =
31 {
32 &map_feature_marker::read,
33 &map_lua_marker::read,
34 &map_corruption_marker::read,
35 &map_wiz_props_marker::read,
36 &map_tomb_marker::read,
37 &map_malign_gateway_marker::read,
38 #if TAG_MAJOR_VERSION == 34
39 &map_phoenix_marker::read,
40 #endif
41 &map_position_marker::read,
42 #if TAG_MAJOR_VERSION == 34
43 &map_door_seal_marker::read,
44 #endif
45 &map_terrain_change_marker::read,
46 &map_cloud_spreader_marker::read
47 };
48
49 map_marker::marker_parser map_marker::parsers[NUM_MAP_MARKER_TYPES] =
50 {
51 &map_feature_marker::parse,
52 &map_lua_marker::parse,
53 nullptr,
54 nullptr,
55 nullptr
56 };
57
map_marker(map_marker_type t,const coord_def & p)58 map_marker::map_marker(map_marker_type t, const coord_def &p)
59 : pos(p), type(t)
60 {
61 }
62
~map_marker()63 map_marker::~map_marker()
64 {
65 }
66
activate(bool)67 void map_marker::activate(bool)
68 {
69 }
70
init()71 void map_marker::init()
72 {
73 }
74
write(writer & outf) const75 void map_marker::write(writer &outf) const
76 {
77 marshallShort(outf, type);
78 marshallCoord(outf, pos);
79 }
80
read(reader & inf)81 void map_marker::read(reader &inf)
82 {
83 // Don't read type! The type has to be read by someone who knows how
84 // to look up the unmarshall function.
85 pos = unmarshallCoord(inf);
86 }
87
property(const string & pname) const88 string map_marker::property(const string &pname) const
89 {
90 UNUSED(pname);
91 return "";
92 }
93
read_marker(reader & inf)94 map_marker *map_marker::read_marker(reader &inf)
95 {
96 const map_marker_type mtype =
97 static_cast<map_marker_type>(unmarshallShort(inf));
98 return readers[mtype]? (*readers[mtype])(inf, mtype) : nullptr;
99 }
100
parse_marker(const string & s,const string & ctx)101 map_marker *map_marker::parse_marker(const string &s, const string &ctx)
102 {
103 for (int i = 0; i < NUM_MAP_MARKER_TYPES; ++i)
104 {
105 if (parsers[i])
106 {
107 if (map_marker *m = parsers[i](s, ctx))
108 return m;
109 }
110 }
111 return nullptr;
112 }
113
114 ////////////////////////////////////////////////////////////////////////////
115 // map_feature_marker
116
map_feature_marker(const coord_def & p,dungeon_feature_type _feat)117 map_feature_marker::map_feature_marker(
118 const coord_def &p,
119 dungeon_feature_type _feat)
120 : map_marker(MAT_FEATURE, p), feat(_feat)
121 {
122 }
123
map_feature_marker(const map_feature_marker & other)124 map_feature_marker::map_feature_marker(
125 const map_feature_marker &other)
126 : map_marker(MAT_FEATURE, other.pos), feat(other.feat)
127 {
128 }
129
write(writer & outf) const130 void map_feature_marker::write(writer &outf) const
131 {
132 map_marker::write(outf);
133 marshallShort(outf, feat);
134 }
135
read(reader & inf)136 void map_feature_marker::read(reader &inf)
137 {
138 map_marker::read(inf);
139 feat = static_cast<dungeon_feature_type>(unmarshallShort(inf));
140 }
141
clone() const142 map_marker *map_feature_marker::clone() const
143 {
144 return new map_feature_marker(pos, feat);
145 }
146
read(reader & inf,map_marker_type)147 map_marker *map_feature_marker::read(reader &inf, map_marker_type)
148 {
149 map_marker *mapf = new map_feature_marker();
150 mapf->read(inf);
151 return mapf;
152 }
153
parse(const string & s,const string &)154 map_marker *map_feature_marker::parse(const string &s, const string &)
155 {
156 if (!starts_with(s, "feat:"))
157 return nullptr;
158 string raw = s;
159 strip_tag(raw, "feat:", true);
160
161 const dungeon_feature_type ft = dungeon_feature_by_name(raw);
162 if (ft == DNGN_UNSEEN)
163 {
164 throw bad_map_marker_f("Bad feature marker: %s (unknown feature '%s')",
165 s.c_str(), raw.c_str());
166 }
167 return new map_feature_marker(coord_def(0, 0), ft);
168 }
169
debug_describe() const170 string map_feature_marker::debug_describe() const
171 {
172 return make_stringf("feature (%s)", dungeon_feature_name(feat));
173 }
174
175 ////////////////////////////////////////////////////////////////////////////
176 // map_lua_marker
177
map_lua_marker()178 map_lua_marker::map_lua_marker()
179 : map_marker(MAT_LUA_MARKER, coord_def()), initialised(false)
180 {
181 }
182
map_lua_marker(const lua_datum & fn)183 map_lua_marker::map_lua_marker(const lua_datum &fn)
184 : map_marker(MAT_LUA_MARKER, coord_def())
185 {
186 lua_stack_cleaner clean(dlua);
187 fn.push();
188 if (fn.is_function() && !dlua.callfn("dgn_run_map", 1, 1))
189 mprf(MSGCH_ERROR, "lua_marker exec error: %s", dlua.error.c_str());
190 else
191 check_register_table();
192 }
193
map_lua_marker(const string & s,const string &,bool mapdef_marker)194 map_lua_marker::map_lua_marker(const string &s, const string &,
195 bool mapdef_marker)
196 : map_marker(MAT_LUA_MARKER, coord_def()), initialised(false)
197 {
198 lua_stack_cleaner clean(dlua);
199 if (mapdef_marker)
200 {
201 if (dlua.loadstring(("return " + s).c_str(), "lua_marker"))
202 mprf(MSGCH_ERROR, "lua_marker load error: %s", dlua.error.c_str());
203 if (!dlua.callfn("dgn_run_map", 1, 1))
204 mprf(MSGCH_ERROR, "lua_marker exec error: %s", dlua.error.c_str());
205 }
206 else
207 {
208 if (dlua.execstring(("return " + s).c_str(), "lua_marker_mapless", 1))
209 {
210 mprf(MSGCH_ERROR, "lua_marker_mapless exec error: %s",
211 dlua.error.c_str());
212 }
213 }
214 check_register_table();
215 }
216
~map_lua_marker()217 map_lua_marker::~map_lua_marker()
218 {
219 }
220
clone() const221 map_marker *map_lua_marker::clone() const
222 {
223 map_lua_marker *copy = new map_lua_marker();
224 copy->pos = pos;
225 if (get_table())
226 copy->check_register_table();
227 return copy;
228 }
229
check_register_table()230 void map_lua_marker::check_register_table()
231 {
232 if (!lua_istable(dlua, -1))
233 {
234 mprf(MSGCH_ERROR, "lua_marker: Expected table, didn't get it.");
235 initialised = false;
236 return;
237 }
238
239 // Got a table. Save it in the registry.
240 marker_table.reset(new lua_datum(dlua));
241 initialised = true;
242 }
243
get_table() const244 bool map_lua_marker::get_table() const
245 {
246 if (marker_table)
247 {
248 marker_table->push();
249 return lua_istable(dlua, -1);
250 }
251 else
252 return false;
253 }
254
write(writer & outf) const255 void map_lua_marker::write(writer &outf) const
256 {
257 map_marker::write(outf);
258
259 lua_stack_cleaner clean(dlua);
260 bool lua_init = initialised;
261 if (!get_table())
262 {
263 mprf(MSGCH_ERROR, "Couldn't find table.");
264 lua_init = false;
265 }
266
267 marshallByte(outf, lua_init);
268 if (!lua_init)
269 return;
270
271 // Call dlua_marker_function(table, 'read')
272 lua_pushstring(dlua, "read");
273 if (!dlua.callfn("dlua_marker_reader_name", 2, 1))
274 end(1, false, "lua_marker: write error: %s", dlua.error.c_str());
275
276 // Right, what's on top should be a table name. Save it.
277 if (!lua_isstring(dlua, -1))
278 {
279 end(1, false, "Expected marker class name (string) to save, got %s",
280 lua_typename(dlua, lua_type(dlua, -1)));
281 }
282
283 const string marker_class(lua_tostring(dlua, -1));
284 marshallString(outf, marker_class);
285
286 // Okay, saved the marker's class. Now ask the writer to do its thing.
287
288 // Call: dlua_marker_method(table, fname, marker)
289 get_table();
290 lua_pushstring(dlua, "write");
291 lua_pushlightuserdata(dlua, const_cast<map_lua_marker*>(this));
292 lua_pushlightuserdata(dlua, &outf);
293
294 if (!dlua.callfn("dlua_marker_method", 4))
295 end(1, false, "lua_marker::write error: %s", dlua.error.c_str());
296 }
297
read(reader & inf)298 void map_lua_marker::read(reader &inf)
299 {
300 map_marker::read(inf);
301
302 if (!(initialised = unmarshallByte(inf)))
303 return;
304
305 const string marker_class = unmarshallString(inf);
306 lua_pushstring(dlua, marker_class.c_str());
307 dlua_push_userdata(dlua, this, MAPMARK_METATABLE);
308 lua_pushlightuserdata(dlua, &inf);
309 if (!dlua.callfn("dlua_marker_read", 3, 1))
310 end(1, false, "lua_marker::read error: %s", dlua.error.c_str());
311
312 // Right, what's on top had better be a table.
313 check_register_table();
314 }
315
read(reader & inf,map_marker_type)316 map_marker *map_lua_marker::read(reader &inf, map_marker_type)
317 {
318 map_marker *marker = new map_lua_marker;
319 marker->read(inf);
320 return marker;
321 }
322
push_fn_args(const char * fn) const323 void map_lua_marker::push_fn_args(const char *fn) const
324 {
325 get_table();
326 lua_pushstring(dlua, fn);
327 dlua_push_userdata(dlua, this, MAPMARK_METATABLE);
328 }
329
callfn(const char * fn,bool warn_err,int args) const330 bool map_lua_marker::callfn(const char *fn, bool warn_err, int args) const
331 {
332 if (args == -1)
333 {
334 const int top = lua_gettop(dlua);
335 push_fn_args(fn);
336 args = lua_gettop(dlua) - top;
337 }
338 const bool res = dlua.callfn("dlua_marker_method", args, 1);
339 if (!res && warn_err)
340 mprf(MSGCH_ERROR, "mlua error: %s", dlua.error.c_str());
341 return res;
342 }
343
init()344 void map_lua_marker::init()
345 {
346 lua_stack_cleaner clean(dlua);
347 push_fn_args("init");
348 callfn("init", true, 3);
349 }
350
activate(bool verbose)351 void map_lua_marker::activate(bool verbose)
352 {
353 lua_stack_cleaner clean(dlua);
354 push_fn_args("activate");
355 lua_pushboolean(dlua, verbose);
356 callfn("activate", true, 4);
357 }
358
notify_dgn_event(const dgn_event & e)359 bool map_lua_marker::notify_dgn_event(const dgn_event &e)
360 {
361 lua_stack_cleaner clean(dlua);
362 push_fn_args("event");
363 clua_push_dgn_event(dlua, &e);
364 if (!dlua.callfn("dlua_marker_method", 4, 1))
365 {
366 mprf(MSGCH_ERROR, "notify_dgn_event: Lua error: %s",
367 dlua.error.c_str());
368
369 // Lua error prevents veto if the event is vetoable.
370 return true;
371 }
372
373 bool accepted = true;
374
375 // We accept only a real boolean false as a veto.
376 if (lua_isboolean(dlua, -1))
377 accepted = lua_toboolean(dlua, -1);
378
379 return accepted;
380 }
381
call_str_fn(const char * fn) const382 string map_lua_marker::call_str_fn(const char *fn) const
383 {
384 lua_stack_cleaner cln(dlua);
385 if (!callfn(fn))
386 return make_stringf("error (%s): %s", fn, dlua.error.c_str());
387
388 string result;
389 if (lua_isstring(dlua, -1))
390 result = lua_tostring(dlua, -1);
391 return result;
392 }
393
debug_describe() const394 string map_lua_marker::debug_describe() const
395 {
396 return call_str_fn("describe");
397 }
398
property(const string & pname) const399 string map_lua_marker::property(const string &pname) const
400 {
401 lua_stack_cleaner cln(dlua);
402 push_fn_args("property");
403 lua_pushstring(dlua, pname.c_str());
404 if (!callfn("property", false, 4))
405 {
406 mprf(MSGCH_ERROR, "Lua marker property (%s) error: %s",
407 pname.c_str(), dlua.error.c_str());
408 return make_stringf("error (prop:%s): %s",
409 pname.c_str(), dlua.error.c_str());
410 }
411 string result;
412 if (lua_isstring(dlua, -1))
413 result = lua_tostring(dlua, -1);
414 return result;
415 }
416
debug_to_string() const417 string map_lua_marker::debug_to_string() const
418 {
419 lua_stack_cleaner cln(dlua);
420
421 if (!get_table())
422 return "Unable to get table for lua marker.";
423
424 if (!dlua.callfn("table_to_string", 1, 1))
425 return make_stringf("error (table_to_string): %s", dlua.error.c_str());
426
427 string result;
428 if (lua_isstring(dlua, -1))
429 result = lua_tostring(dlua, -1);
430 else
431 result = "table_to_string() returned nothing";
432 return result;
433 }
434
parse(const string & s,const string & ctx)435 map_marker *map_lua_marker::parse(const string &s, const string &ctx)
436 {
437 string raw = s;
438 bool mapdef_marker = true;
439
440 if (starts_with(s, "lua:"))
441 strip_tag(raw, "lua:", true);
442 else if (starts_with(s, "lua_mapless:"))
443 {
444 strip_tag(raw, "lua_mapless:", true);
445 mapdef_marker = false;
446 }
447 else
448 return nullptr;
449
450 map_lua_marker *mark = new map_lua_marker(raw, ctx, mapdef_marker);
451 if (!mark->initialised)
452 {
453 delete mark;
454 throw bad_map_marker_f("Unable to initialise Lua marker from '%s'",
455 raw.c_str());
456 }
457 return mark;
458 }
459
460 //////////////////////////////////////////////////////////////////////////
461 // map_corruption_marker
462
map_corruption_marker(const coord_def & p,int dur)463 map_corruption_marker::map_corruption_marker(const coord_def &p,
464 int dur)
465 : map_marker(MAT_CORRUPTION_NEXUS, p), duration(dur)
466 {
467 }
468
write(writer & out) const469 void map_corruption_marker::write(writer &out) const
470 {
471 map_marker::write(out);
472 marshallShort(out, duration);
473 }
474
read(reader & in)475 void map_corruption_marker::read(reader &in)
476 {
477 map_marker::read(in);
478 duration = unmarshallShort(in);
479 }
480
read(reader & in,map_marker_type)481 map_marker *map_corruption_marker::read(reader &in, map_marker_type)
482 {
483 map_corruption_marker *mc = new map_corruption_marker();
484 mc->read(in);
485 return mc;
486 }
487
clone() const488 map_marker *map_corruption_marker::clone() const
489 {
490 map_corruption_marker *mark = new map_corruption_marker(pos, duration);
491 return mark;
492 }
493
debug_describe() const494 string map_corruption_marker::debug_describe() const
495 {
496 return make_stringf("Lugonu corrupt (%d)", duration);
497 }
498
499 ////////////////////////////////////////////////////////////////////////////
500 // map_feature_marker
501
map_wiz_props_marker(const coord_def & p)502 map_wiz_props_marker::map_wiz_props_marker(
503 const coord_def &p)
504 : map_marker(MAT_WIZ_PROPS, p)
505 {
506 }
507
map_wiz_props_marker(const map_wiz_props_marker & other)508 map_wiz_props_marker::map_wiz_props_marker(
509 const map_wiz_props_marker &other)
510 : map_marker(MAT_WIZ_PROPS, other.pos), properties(other.properties)
511 {
512 }
513
write(writer & outf) const514 void map_wiz_props_marker::write(writer &outf) const
515 {
516 map_marker::write(outf);
517 marshallShort(outf, properties.size());
518 for (const auto &entry : properties)
519 {
520 marshallString(outf, entry.first);
521 marshallString(outf, entry.second);
522 }
523 }
524
read(reader & inf)525 void map_wiz_props_marker::read(reader &inf)
526 {
527 map_marker::read(inf);
528
529 short numPairs = unmarshallShort(inf);
530 for (short i = 0; i < numPairs; i++)
531 {
532 const string key = unmarshallString(inf);
533 const string val = unmarshallString(inf);
534
535 set_property(key, val);
536 }
537 }
538
property(const string & pname) const539 string map_wiz_props_marker::property(const string &pname) const
540 {
541 if (pname == "desc")
542 return property("feature_description");
543
544 return lookup(properties, pname, "");
545 }
546
set_property(const string & key,const string & val)547 string map_wiz_props_marker::set_property(const string &key, const string &val)
548 {
549 string old_val = properties[key];
550 properties[key] = val;
551 return old_val;
552 }
553
clone() const554 map_marker *map_wiz_props_marker::clone() const
555 {
556 return new map_wiz_props_marker(*this);
557 }
558
read(reader & inf,map_marker_type)559 map_marker *map_wiz_props_marker::read(reader &inf, map_marker_type)
560 {
561 map_marker *mapf = new map_wiz_props_marker();
562 mapf->read(inf);
563 return mapf;
564 }
565
parse(const string &,const string &)566 map_marker *map_wiz_props_marker::parse(const string &, const string &)
567 {
568 throw bad_map_marker("map_wiz_props_marker::parse() not implemented");
569 }
570
debug_describe() const571 string map_wiz_props_marker::debug_describe() const
572 {
573 return "Wizard props: " + property("feature_description");
574 }
575
576 //////////////////////////////////////////////////////////////////////////
577 // map_tomb_marker
578
map_tomb_marker(const coord_def & p,int dur,int src,int targ)579 map_tomb_marker::map_tomb_marker(const coord_def &p,
580 int dur, int src, int targ)
581 : map_marker(MAT_TOMB, p), duration(dur), source(src), target(targ)
582 {
583 }
584
write(writer & out) const585 void map_tomb_marker::write(writer &out) const
586 {
587 map_marker::write(out);
588 marshallShort(out, duration);
589 marshallShort(out, source);
590 marshallShort(out, target);
591 }
592
read(reader & in)593 void map_tomb_marker::read(reader &in)
594 {
595 map_marker::read(in);
596 duration = unmarshallShort(in);
597 source = unmarshallShort(in);
598 target = unmarshallShort(in);
599 }
600
read(reader & in,map_marker_type)601 map_marker *map_tomb_marker::read(reader &in, map_marker_type)
602 {
603 map_tomb_marker *mc = new map_tomb_marker();
604 mc->read(in);
605 return mc;
606 }
607
clone() const608 map_marker *map_tomb_marker::clone() const
609 {
610 map_tomb_marker *mark = new map_tomb_marker(pos, duration,
611 source, target);
612 return mark;
613 }
614
debug_describe() const615 string map_tomb_marker::debug_describe() const
616 {
617 return make_stringf("Tomb (%d, %d, %d)", duration,
618 source, target);
619 }
620
621 //////////////////////////////////////////////////////////////////////////
622 // map_malign_gateway_marker
623
map_malign_gateway_marker(const coord_def & p,int dur,bool ip,string sum,beh_type b,god_type gd,int pow)624 map_malign_gateway_marker::map_malign_gateway_marker(const coord_def &p,
625 int dur, bool ip, string sum, beh_type b,
626 god_type gd, int pow)
627 : map_marker(MAT_MALIGN, p), duration(dur), is_player(ip), monster_summoned(false),
628 summoner_string(sum), behaviour(b), god(gd), power(pow)
629 {
630 }
631
write(writer & out) const632 void map_malign_gateway_marker::write(writer &out) const
633 {
634 map_marker::write(out);
635 marshallShort(out, duration);
636 marshallBoolean(out, is_player);
637 marshallBoolean(out, monster_summoned);
638 marshallString(out, summoner_string);
639 marshallUByte(out, behaviour);
640 marshallUByte(out, god);
641 marshallShort(out, power);
642 }
643
read(reader & in)644 void map_malign_gateway_marker::read(reader &in)
645 {
646 map_marker::read(in);
647 duration = unmarshallShort(in);
648 is_player = unmarshallBoolean(in);
649
650 monster_summoned = unmarshallBoolean(in);
651 summoner_string = unmarshallString(in);
652 behaviour = static_cast<beh_type>(unmarshallUByte(in));
653
654 god = static_cast<god_type>(unmarshallByte(in));
655 power = unmarshallShort(in);
656 }
657
read(reader & in,map_marker_type)658 map_marker *map_malign_gateway_marker::read(reader &in, map_marker_type)
659 {
660 map_malign_gateway_marker *mc = new map_malign_gateway_marker();
661 mc->read(in);
662 return mc;
663 }
664
clone() const665 map_marker *map_malign_gateway_marker::clone() const
666 {
667 map_malign_gateway_marker *mark = new map_malign_gateway_marker(pos,
668 duration, is_player, summoner_string, behaviour, god, power);
669 return mark;
670 }
671
debug_describe() const672 string map_malign_gateway_marker::debug_describe() const
673 {
674 return make_stringf("Malign gateway (%d, %s)", duration,
675 is_player ? "player" : "monster");
676 }
677 #if TAG_MAJOR_VERSION == 34
678
679 //////////////////////////////////////////////////////////////////////////
680 // map_phoenix_marker
681
map_phoenix_marker(const coord_def & p,int dur,int mnum,beh_type bh,mon_attitude_type at,god_type gd,coord_def cp)682 map_phoenix_marker::map_phoenix_marker(const coord_def& p,
683 int dur, int mnum, beh_type bh, mon_attitude_type at,
684 god_type gd, coord_def cp)
685 : map_marker(MAT_PHOENIX, p), duration(dur), mon_num(mnum),
686 behaviour(bh), attitude(at), god(gd), corpse_pos(cp)
687 {
688 }
689
write(writer & out) const690 void map_phoenix_marker::write(writer &out) const
691 {
692 map_marker::write(out);
693 marshallShort(out, duration);
694 marshallShort(out, mon_num);
695 marshallUByte(out, behaviour);
696 marshallUByte(out, attitude);
697 marshallUByte(out, god);
698 marshallCoord(out, corpse_pos);
699 }
700
read(reader & in)701 void map_phoenix_marker::read(reader &in)
702 {
703 map_marker::read(in);
704
705 duration = unmarshallShort(in);
706 mon_num = unmarshallShort(in);
707 behaviour = static_cast<beh_type>(unmarshallUByte(in));
708 attitude = static_cast<mon_attitude_type>(unmarshallUByte(in));
709 god = static_cast<god_type>(unmarshallByte(in));
710 corpse_pos = unmarshallCoord(in);
711 }
712
read(reader & in,map_marker_type)713 map_marker *map_phoenix_marker::read(reader &in, map_marker_type)
714 {
715 map_phoenix_marker *mc = new map_phoenix_marker();
716 mc->read(in);
717 return mc;
718 }
719
clone() const720 map_marker *map_phoenix_marker::clone() const
721 {
722 map_phoenix_marker *mark = new map_phoenix_marker(pos, duration, mon_num,
723 behaviour, attitude, god, corpse_pos);
724 return mark;
725 }
726
debug_describe() const727 string map_phoenix_marker::debug_describe() const
728 {
729 return make_stringf("Phoenix marker (%d, %d)", duration, mon_num);
730 }
731
732 ////////////////////////////////////////////////////////////////////////////
733 // map_door_seal_marker
734
map_door_seal_marker(const coord_def & p,int dur,int mnum,dungeon_feature_type oldfeat)735 map_door_seal_marker::map_door_seal_marker(const coord_def& p,
736 int dur, int mnum, dungeon_feature_type oldfeat)
737 : map_marker(MAT_DOOR_SEAL, p), duration(dur), mon_num(mnum),
738 old_feature(oldfeat)
739 {
740 }
741
write(writer & out) const742 void map_door_seal_marker::write(writer &out) const
743 {
744 map_marker::write(out);
745 marshallShort(out, duration);
746 marshallShort(out, mon_num);
747 marshallUByte(out, old_feature);
748 }
749
read(reader & in)750 void map_door_seal_marker::read(reader &in)
751 {
752 map_marker::read(in);
753
754 duration = unmarshallShort(in);
755 mon_num = unmarshallShort(in);
756 old_feature = static_cast<dungeon_feature_type>(unmarshallUByte(in));
757 }
758
read(reader & in,map_marker_type)759 map_marker *map_door_seal_marker::read(reader &in, map_marker_type)
760 {
761 map_door_seal_marker *mc = new map_door_seal_marker();
762 mc->read(in);
763 return mc;
764 }
765
clone() const766 map_marker *map_door_seal_marker::clone() const
767 {
768 map_door_seal_marker *mark = new map_door_seal_marker(pos, duration, mon_num, old_feature);
769 return mark;
770 }
771
debug_describe() const772 string map_door_seal_marker::debug_describe() const
773 {
774 return make_stringf("Door seal marker (%d, %d)", duration, mon_num);
775 }
776 #endif
777
778 ////////////////////////////////////////////////////////////////////////////
779 // map_terrain_change_marker
780
map_terrain_change_marker(const coord_def & p,dungeon_feature_type oldfeat,dungeon_feature_type newfeat,int dur,terrain_change_type ctype,int mnum,int oldcol)781 map_terrain_change_marker::map_terrain_change_marker (const coord_def& p,
782 dungeon_feature_type oldfeat, dungeon_feature_type newfeat,
783 int dur, terrain_change_type ctype, int mnum, int oldcol)
784 : map_marker(MAT_TERRAIN_CHANGE, p), duration(dur), mon_num(mnum),
785 old_feature(oldfeat), new_feature(newfeat), change_type(ctype),
786 colour(oldcol)
787 {
788 }
789
write(writer & out) const790 void map_terrain_change_marker::write(writer &out) const
791 {
792 map_marker::write(out);
793 marshallShort(out, duration);
794 marshallUByte(out, old_feature);
795 marshallUByte(out, new_feature);
796 marshallUByte(out, change_type);
797 marshallShort(out, mon_num);
798 marshallUByte(out, colour);
799 }
800
read(reader & in)801 void map_terrain_change_marker::read(reader &in)
802 {
803 map_marker::read(in);
804
805 duration = unmarshallShort(in);
806 old_feature = static_cast<dungeon_feature_type>(unmarshallUByte(in));
807 new_feature = static_cast<dungeon_feature_type>(unmarshallUByte(in));
808 change_type = static_cast<terrain_change_type>(unmarshallUByte(in));
809 mon_num = unmarshallShort(in);
810 #if TAG_MAJOR_VERSION == 34
811 if (in.getMinorVersion() < TAG_MINOR_SAVE_TERRAIN_COLOUR)
812 colour = BLACK;
813 else
814 #endif
815 colour = unmarshallUByte(in);
816 }
817
read(reader & in,map_marker_type)818 map_marker *map_terrain_change_marker::read(reader &in, map_marker_type)
819 {
820 map_terrain_change_marker *mc = new map_terrain_change_marker();
821 mc->read(in);
822 return mc;
823 }
824
clone() const825 map_marker *map_terrain_change_marker::clone() const
826 {
827 map_terrain_change_marker *mark =
828 new map_terrain_change_marker(pos, old_feature, new_feature, duration,
829 change_type, mon_num, colour);
830 return mark;
831 }
832
debug_describe() const833 string map_terrain_change_marker::debug_describe() const
834 {
835 return make_stringf("Terrain change marker (%d->%d, %d)",
836 old_feature, new_feature, duration);
837 }
838
839 ////////////////////////////////////////////////////////////////////////////
840 // map_cloud_spreader_marker
841
map_cloud_spreader_marker(const coord_def & p,cloud_type cloud,int spd,int amt,int max_radius,int dur,actor * agent)842 map_cloud_spreader_marker::map_cloud_spreader_marker(const coord_def &p,
843 cloud_type cloud, int spd, int amt,
844 int max_radius, int dur, actor* agent)
845 : map_marker(MAT_CLOUD_SPREADER, p), ctype(cloud), speed(spd),
846 remaining(amt), max_rad(max_radius), duration(dur), speed_increment(0)
847 {
848 if (agent)
849 {
850 agent_mid = agent->mid;
851 if (agent->is_monster())
852 kcat = (agent->as_monster()->friendly() ? KC_FRIENDLY : KC_OTHER);
853 else
854 kcat = KC_YOU;
855 }
856 else
857 {
858 agent_mid = 0;
859 kcat = KC_OTHER;
860 }
861 }
862
write(writer & out) const863 void map_cloud_spreader_marker::write(writer &out) const
864 {
865 map_marker::write(out);
866 marshallByte(out, ctype);
867 marshallShort(out, speed);
868 marshallShort(out, duration);
869 marshallByte(out, max_rad);
870 marshallShort(out, remaining);
871 marshallShort(out, speed_increment);
872 marshallInt(out, agent_mid);
873 marshallByte(out, kcat);
874 }
875
read(reader & in)876 void map_cloud_spreader_marker::read(reader &in)
877 {
878 map_marker::read(in);
879
880 ctype = static_cast<cloud_type>(unmarshallByte(in));
881 speed = unmarshallShort(in);
882 duration = unmarshallShort(in);
883 max_rad = unmarshallByte(in);
884 remaining = unmarshallShort(in);
885 speed_increment = unmarshallShort(in);
886 agent_mid = static_cast<mid_t>(unmarshallInt(in));
887 kcat = static_cast<kill_category>(unmarshallByte(in));
888 }
889
read(reader & in,map_marker_type)890 map_marker *map_cloud_spreader_marker::read(reader &in, map_marker_type)
891 {
892 map_cloud_spreader_marker *mc = new map_cloud_spreader_marker();
893 mc->read(in);
894 return mc;
895 }
896
clone() const897 map_marker *map_cloud_spreader_marker::clone() const
898 {
899 map_cloud_spreader_marker *mark =
900 new map_cloud_spreader_marker(pos, ctype, speed, remaining, max_rad,
901 duration);
902 mark->agent_mid = agent_mid;
903 mark->kcat = kcat;
904 return mark;
905 }
906
debug_describe() const907 string map_cloud_spreader_marker::debug_describe() const
908 {
909 return make_stringf("Cloud spreader marker (%d)", ctype);
910 }
911
912 ////////////////////////////////////////////////////////////////////////////
913 // map_position_marker
914
map_position_marker(const coord_def & p,dungeon_feature_type _feat,const coord_def _dest)915 map_position_marker::map_position_marker(
916 const coord_def &p,
917 dungeon_feature_type _feat,
918 const coord_def _dest)
919 : map_marker(MAT_POSITION, p), feat(_feat), dest(_dest)
920 {
921 }
922
map_position_marker(const map_position_marker & other)923 map_position_marker::map_position_marker(
924 const map_position_marker &other)
925 : map_marker(MAT_POSITION, other.pos), feat(other.feat), dest(other.dest)
926 {
927 }
928
write(writer & outf) const929 void map_position_marker::write(writer &outf) const
930 {
931 map_marker::write(outf);
932 marshallUByte(outf, feat);
933 marshallCoord(outf, dest);
934 }
935
read(reader & inf)936 void map_position_marker::read(reader &inf)
937 {
938 map_marker::read(inf);
939 #if TAG_MAJOR_VERSION == 34
940 if (inf.getMinorVersion() < TAG_MINOR_TRANSPORTERS)
941 feat = DNGN_UNSEEN;
942 else
943 #endif
944 feat = unmarshallFeatureType(inf);
945 dest = (unmarshallCoord(inf));
946 }
947
clone() const948 map_marker *map_position_marker::clone() const
949 {
950 return new map_position_marker(pos, feat, dest);
951 }
952
read(reader & inf,map_marker_type)953 map_marker *map_position_marker::read(reader &inf, map_marker_type)
954 {
955 map_marker *mapf = new map_position_marker();
956 mapf->read(inf);
957 return mapf;
958 }
959
debug_describe() const960 string map_position_marker::debug_describe() const
961 {
962 return make_stringf("position (%d,%d)", dest.x, dest.y);
963 }
964
965 //////////////////////////////////////////////////////////////////////////
966 // Map markers in env.
967
map_markers()968 map_markers::map_markers() : markers(), have_inactive_markers(false)
969 {
970 }
971
map_markers(const map_markers & c)972 map_markers::map_markers(const map_markers &c)
973 : markers(), have_inactive_markers(false)
974 {
975 init_from(c);
976 }
977
operator =(const map_markers & c)978 map_markers &map_markers::operator = (const map_markers &c)
979 {
980 if (this != &c)
981 {
982 clear();
983 init_from(c);
984 }
985 return *this;
986 }
987
~map_markers()988 map_markers::~map_markers()
989 {
990 clear();
991 }
992
init_from(const map_markers & c)993 void map_markers::init_from(const map_markers &c)
994 {
995 for (const auto &entry : c.markers)
996 add(entry.second->clone());
997 have_inactive_markers = c.have_inactive_markers;
998 }
999
clear_need_activate()1000 void map_markers::clear_need_activate()
1001 {
1002 have_inactive_markers = false;
1003 }
1004
init_all()1005 void map_markers::init_all()
1006 {
1007 // called when a level is generated, but not yet entered
1008 for (auto i = markers.begin(); i != markers.end();)
1009 {
1010 map_marker *marker = i->second;
1011 ++i;
1012 marker->init();
1013 }
1014
1015 for (auto i = markers.begin(); i != markers.end();)
1016 {
1017 map_marker *marker = i->second;
1018 ++i;
1019 if (!marker->property("post_init_remove").empty())
1020 remove(marker);
1021 }
1022 }
1023
activate_all(bool verbose)1024 void map_markers::activate_all(bool verbose)
1025 {
1026 // called when a level is entered
1027 for (auto i = markers.begin(); i != markers.end();)
1028 {
1029 map_marker *marker = i->second;
1030 ++i;
1031 marker->activate(verbose);
1032 }
1033
1034 for (auto i = markers.begin(); i != markers.end();)
1035 {
1036 map_marker *marker = i->second;
1037 ++i;
1038 if (!marker->property("post_activate_remove").empty())
1039 remove(marker);
1040 }
1041
1042 have_inactive_markers = false;
1043 }
1044
activate_markers_at(coord_def p)1045 void map_markers::activate_markers_at(coord_def p)
1046 {
1047 for (map_marker *activatee : get_markers_at(p))
1048 activatee->activate();
1049
1050 for (map_marker *active : get_markers_at(p))
1051 {
1052 const string prop = active->property("post_activate_remove");
1053 if (!prop.empty())
1054 remove(active);
1055 }
1056 }
1057
add(map_marker * marker)1058 void map_markers::add(map_marker *marker)
1059 {
1060 markers.insert(dgn_pos_marker(marker->pos, marker));
1061 have_inactive_markers = true;
1062 }
1063
unlink_marker(const map_marker * marker)1064 void map_markers::unlink_marker(const map_marker *marker)
1065 {
1066 auto els = markers.equal_range(marker->pos);
1067 for (auto i = els.first; i != els.second; ++i)
1068 {
1069 if (i->second == marker)
1070 {
1071 markers.erase(i);
1072 break;
1073 }
1074 }
1075 }
1076
check_empty()1077 void map_markers::check_empty()
1078 {
1079 if (markers.empty())
1080 have_inactive_markers = false;
1081 }
1082
remove(map_marker * marker)1083 void map_markers::remove(map_marker *marker)
1084 {
1085 unlink_marker(marker);
1086 delete marker;
1087 check_empty();
1088 }
1089
remove_markers_at(const coord_def & c,map_marker_type type)1090 void map_markers::remove_markers_at(const coord_def &c,
1091 map_marker_type type)
1092 {
1093 auto els = markers.equal_range(c);
1094 for (auto i = els.first; i != els.second;)
1095 {
1096 auto todel = i++;
1097 if (type == MAT_ANY || todel->second->get_type() == type)
1098 {
1099 delete todel->second;
1100 markers.erase(todel);
1101 }
1102 }
1103 check_empty();
1104 }
1105
find(const coord_def & c,map_marker_type type)1106 map_marker *map_markers::find(const coord_def &c, map_marker_type type)
1107 {
1108 auto els = markers.equal_range(c);
1109 for (auto i = els.first; i != els.second; ++i)
1110 if (type == MAT_ANY || i->second->get_type() == type)
1111 return i->second;
1112 return nullptr;
1113 }
1114
find(map_marker_type type)1115 map_marker *map_markers::find(map_marker_type type)
1116 {
1117 for (const auto &entry : markers)
1118 if (type == MAT_ANY || entry.second->get_type() == type)
1119 return entry.second;
1120
1121 return nullptr;
1122 }
1123
move(const coord_def & from,const coord_def & to)1124 void map_markers::move(const coord_def &from, const coord_def &to)
1125 {
1126 unwind_bool inactive(have_inactive_markers);
1127 auto els = markers.equal_range(from);
1128
1129 list<map_marker*> tmarkers;
1130 for (auto i = els.first; i != els.second;)
1131 {
1132 auto curr = i++;
1133 tmarkers.push_back(curr->second);
1134 markers.erase(curr);
1135 }
1136
1137 for (auto mark : tmarkers)
1138 {
1139 mark->pos = to;
1140 add(mark);
1141 }
1142 }
1143
move_marker(map_marker * marker,const coord_def & to)1144 void map_markers::move_marker(map_marker *marker, const coord_def &to)
1145 {
1146 unwind_bool inactive(have_inactive_markers);
1147 unlink_marker(marker);
1148 marker->pos = to;
1149 add(marker);
1150 }
1151
get_all(map_marker_type mat)1152 vector<map_marker*> map_markers::get_all(map_marker_type mat)
1153 {
1154 vector<map_marker*> rmarkers;
1155 for (const auto &entry : markers)
1156 {
1157 if (mat == MAT_ANY || entry.second->get_type() == mat)
1158 rmarkers.push_back(entry.second);
1159 }
1160 return rmarkers;
1161 }
1162
get_all(const string & key,const string & val)1163 vector<map_marker*> map_markers::get_all(const string &key, const string &val)
1164 {
1165 vector<map_marker*> rmarkers;
1166
1167 for (const auto &entry : markers)
1168 {
1169 map_marker* marker = entry.second;
1170 const string prop = marker->property(key);
1171
1172 if (val.empty() && !prop.empty() || !val.empty() && val == prop)
1173 rmarkers.push_back(marker);
1174 }
1175
1176 return rmarkers;
1177 }
1178
get_markers_at(const coord_def & c)1179 vector<map_marker*> map_markers::get_markers_at(const coord_def &c)
1180 {
1181 auto els = markers.equal_range(c);
1182 vector<map_marker*> rmarkers;
1183 for (auto i = els.first; i != els.second; ++i)
1184 rmarkers.push_back(i->second);
1185 return rmarkers;
1186 }
1187
property_at(const coord_def & c,map_marker_type type,const string & key)1188 string map_markers::property_at(const coord_def &c, map_marker_type type,
1189 const string &key)
1190 {
1191 UNUSED(type);
1192 auto els = markers.equal_range(c);
1193 for (auto i = els.first; i != els.second;)
1194 {
1195 // this ugly sequencing is necessary in case the call to property
1196 // removes the marker, invalidating i.
1197 auto marker = i->second;
1198 i++;
1199 const string &prop = marker->property(key);
1200 if (!prop.empty())
1201 return prop;
1202 }
1203 return "";
1204 }
1205
clear()1206 void map_markers::clear()
1207 {
1208 for (auto &entry : markers)
1209 delete entry.second;
1210 markers.clear();
1211 check_empty();
1212 }
1213
1214 static const long MARKERS_COOKY = 0x17742C32;
write(writer & outf) const1215 void map_markers::write(writer &outf) const
1216 {
1217 marshallInt(outf, MARKERS_COOKY);
1218
1219 vector<unsigned char> buf;
1220
1221 marshallShort(outf, markers.size());
1222 for (const auto &entry : markers)
1223 {
1224 buf.clear();
1225 writer tmp_outf(&buf);
1226 entry.second->write(tmp_outf);
1227
1228 // Write the marker data, prefixed by a size
1229 marshallInt(outf, buf.size());
1230 for (auto byte : buf)
1231 outf.writeByte(byte);
1232 }
1233 }
1234
read(reader & inf)1235 void map_markers::read(reader &inf)
1236 {
1237 clear();
1238
1239 const int cooky = unmarshallInt(inf);
1240 ASSERT(cooky == MARKERS_COOKY);
1241 UNUSED(cooky);
1242
1243 const int nmarkers = unmarshallShort(inf);
1244 for (int i = 0; i < nmarkers; ++i)
1245 {
1246 // used by tools
1247 unmarshallInt(inf);
1248 if (map_marker *mark = map_marker::read_marker(inf))
1249 add(mark);
1250 }
1251 }
1252
1253 /////////////////////////////////////////////////////////////////////////
1254
marker_vetoes_operation(const char * op)1255 bool marker_vetoes_operation(const char *op)
1256 {
1257 return env.markers.property_at(you.pos(), MAT_ANY, op) == "veto";
1258 }
1259
find_marker_position_by_prop(const string & prop,const string & expected)1260 coord_def find_marker_position_by_prop(const string &prop,
1261 const string &expected)
1262 {
1263 const vector<coord_def> markers =
1264 find_marker_positions_by_prop(prop, expected, 1);
1265 if (markers.empty())
1266 {
1267 const coord_def nowhere(-1, -1);
1268 return nowhere;
1269 }
1270 return markers[0];
1271 }
1272
find_marker_positions_by_prop(const string & prop,const string & expected,unsigned maxresults)1273 vector<coord_def> find_marker_positions_by_prop(const string &prop,
1274 const string &expected,
1275 unsigned maxresults)
1276 {
1277 vector<coord_def> marker_positions;
1278 for (rectangle_iterator i(0, 0); i; ++i)
1279 {
1280 const string value = env.markers.property_at(*i, MAT_ANY, prop);
1281 if (!value.empty() && (expected.empty() || value == expected))
1282 {
1283 marker_positions.push_back(*i);
1284 if (maxresults && marker_positions.size() >= maxresults)
1285 return marker_positions;
1286 }
1287 }
1288 return marker_positions;
1289 }
1290
find_markers_by_prop(const string & prop,const string & expected,unsigned maxresults)1291 vector<map_marker*> find_markers_by_prop(const string &prop,
1292 const string &expected,
1293 unsigned maxresults)
1294 {
1295 vector<map_marker*> markers;
1296 for (rectangle_iterator pos(0, 0); pos; ++pos)
1297 {
1298 for (map_marker *mark : env.markers.get_markers_at(*pos))
1299 {
1300 const string value(mark->property(prop));
1301 if (!value.empty() && (expected.empty() || value == expected))
1302 {
1303 markers.push_back(mark);
1304 if (maxresults && markers.size() >= maxresults)
1305 return markers;
1306 }
1307 }
1308 }
1309 return markers;
1310 }
1311
1312 ///////////////////////////////////////////////////////////////////
1313
1314 // Safely remove all markers and dungeon listeners at the given square.
remove_markers_and_listeners_at(coord_def p)1315 void remove_markers_and_listeners_at(coord_def p)
1316 {
1317 // Look for Lua markers on this square that are listening for
1318 // non-positional events, (such as bazaar portals listening for
1319 // turncount changes) and detach them manually from the dungeon
1320 // event dispatcher.
1321 for (map_marker *marker : env.markers.get_markers_at(p))
1322 {
1323 if (marker->get_type() == MAT_LUA_MARKER)
1324 {
1325 dungeon_events.remove_listener(
1326 dynamic_cast<map_lua_marker*>(marker));
1327 }
1328 }
1329
1330 env.markers.remove_markers_at(p);
1331 dungeon_events.clear_listeners_at(p);
1332 }
1333
1334 /**
1335 * Get any position marker at the given location for the given type of feature.
1336 * Multiple position markers can exist at a given location, so the `feat`
1337 * argument is used to select the desired one.
1338 *
1339 * @param pos The position to check.
1340 * @param feat The type of feature for which we want a position marker.
1341 * @returns The position marker if one exists, otherwise nullptr.
1342 **/
get_position_marker_at(const coord_def & pos,dungeon_feature_type feat)1343 map_position_marker *get_position_marker_at(const coord_def &pos,
1344 dungeon_feature_type feat)
1345 {
1346 for (map_marker *m : env.markers.get_markers_at(pos))
1347 {
1348 if (m->get_type() != MAT_POSITION)
1349 continue;
1350
1351 map_position_marker *posm = dynamic_cast<map_position_marker*>(m);
1352 #if TAG_MAJOR_VERSION == 34
1353 // Escape hatches were the only feature type using position markers
1354 // before TAG_MINOR_TRANSPORTERS.
1355 if (posm->feat == DNGN_UNSEEN && feat_is_escape_hatch(feat))
1356 posm->feat = feat;
1357 #endif
1358 if (posm->feat == feat)
1359 return posm;
1360 }
1361
1362 return nullptr;
1363 }
1364
1365 /**
1366 * Get the destination of the transporter at the given pos.
1367 *
1368 * @param pos The position of the transporter.
1369 * @returns The destination of the transporter. This will be
1370 * INVALID_COORD if no valid destination exists.
1371 **/
get_transporter_dest(const coord_def & pos)1372 coord_def get_transporter_dest(const coord_def &pos)
1373 {
1374 ASSERT(env.grid(pos) == DNGN_TRANSPORTER);
1375
1376 map_position_marker *marker
1377 = get_position_marker_at(pos, DNGN_TRANSPORTER);
1378 coord_def dest = INVALID_COORD;
1379
1380 if (marker)
1381 dest = marker->dest;
1382 return dest;
1383 }
1384