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