blob: d0ead0b6e91017736ab32cb7124ce340bc5f71b5 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// room/exits.c -- room exits handling
4//
5// $Id: exits.c 9497 2016-02-21 14:20:03Z Zesstra $
6
7/*
8 * Exits of the room (obvious ones, doors, and special ones)
9 * we define the following function for easy reference:
10 * GetExits() - return a string containing an "Obvious Exits" Statement
11 *
12 * The exits are implemented as properties P_EXITS
13 * They are stored locally (_set_xx, _query_xx)
14 * as mapping to speed up the routines in this module.
15 *
16 */
17
18#pragma strong_types
19#pragma save_types
20#pragma pedantic
21#pragma range_check
22#pragma no_clone
23
24#define NEED_PROTOTYPES
25#include <thing/properties.h>
26#include <moving.h>
27#include <room/exits.h>
28#include <hook.h>
29#include <exploration.h>
30#undef NEED_PROTOTYPES
31
32#include <sys_debug.h>
33#include <config.h>
34#include <properties.h>
35#include <defines.h>
36#include <daemon.h>
37#include <doorroom.h>
38#include <routingd.h>
39
40#define NUMBERS ({ "zwei", "drei", "vier", "fuenf", "sechs", "sieben", "acht" })
41
42
43// Hilfsfunktion, die bei kaputten Exits eine Notrettung betreibt, aber
44// trotzdem auf Debug eine Meldung macht.
45static mapping rescueExit()
46{
47 catch(raise_error(sprintf(
48 "room/exits.c: Forgotten ::create()? "
49 "P_EXITS in %O is 0!\n", this_object()));publish);
50
51 return ([:2]);
52}
53
54
55static mapping _set_exits( mapping map_ldfied )
56{
57 if( mappingp(map_ldfied) )
58 return Set( P_EXITS, map_ldfied );
59 return 0;
60}
61
62
63static mapping _query_exits()
64{
65 if( (!previous_object() || object_name(previous_object()) != DOOR_MASTER)
66 && QueryProp(P_DOOR_INFOS) )
67 call_other( DOOR_MASTER, "init_doors" );
68
69 mapping exits = Query(P_EXITS) || rescueExit();
70
71 return filter(exits, function int (string key, mixed val)
72 {return stringp(val[0]);} );
73}
74
75
76static int _set_special_exits( mapping map_ldfied )
77{
78 return -1;
79}
80
81
82static mapping _query_special_exits()
83{
84 mapping exits = Query(P_EXITS) || rescueExit();
85
86 return filter(exits, function int (string key, mixed val)
87 {return closurep(val[0]);} );
88}
89
90
91void reset()
92{}
93
94
95protected void create()
96{
97 offerHook(H_HOOK_EXIT_USE, 1);
98 SetProp( P_EXITS, ([:2]) );
Zesstra179db0d2016-11-26 13:13:41 +010099 // Falls dieser Raum ein Haltepunkt von Transportern ist, werden diese
100 // Transporter informiert, dass dieser Raum neugeladen wurde - evtl. will
101 // der Transporter jetzt diesen Raum ansteuern, wenn er pausiert war.
102 object *trans = TRAVELD->HasTransporter(this_object());
103 if (pointerp(trans))
104 {
105 foreach (object t : trans)
106 {
107 t->Continue();
108 }
109 }
MG Mud User88f12472016-06-24 23:31:02 +0200110}
111
112protected void create_super() {
113 set_next_reset(-1);
114}
115
116protected void _AddExit(string|string* cmd, string|closure room,
117 string message)
118{
119 mapping exita;
120
121 exita = Query(P_EXITS) || rescueExit();
122
123 if ( !closurep(room) )
124 {
125 object router;
126
127 room = _MakePath(room);
128
129 if ( !clonep(this_object()) && objectp(router = find_object(ROUTER)) )
130 {
131 router->RegisterExit( object_name(this_object()), cmd, room );
132 }
133 }
134
135 if( stringp(cmd) )
136 {
137 exita += ([ cmd : room; message ]);
138 }
139 else
140 {
141 foreach(string c : cmd)
142 {
143 if (stringp(c))
144 exita += ([ c : room; message ]);
145 }
146 }
147
148 Set( P_EXITS, exita );
149}
150
151void AddExit(string|string* cmd, closure|string dest)
152{
153 string msg;
154 if ( stringp(dest) )
155 {
156 int s;
157 if( (s = member(dest, '#')) != -1 )
158 {
159 msg = dest[0..s-1];
160 dest = dest[s+1..];
161 }
162 }
163 _AddExit(cmd, dest, msg);
164}
165
166void RemoveExit(string|string* cmd )
167{
168 mapping exita;
169
170 if ( !cmd ) {
171 SetProp(P_EXITS, ([:2]) );
172 return;
173 }
174
175 if ( stringp(cmd) )
176 cmd = ({ cmd });
177
178 exita = Query(P_EXITS, F_VALUE) || rescueExit();
179 foreach(string c : cmd)
180 m_delete( exita, c );
181
182 Set( P_EXITS, exita, F_VALUE );
183}
184
185
186void AddSpecialExit(string|string* cmd, string|closure functionname )
187{
188
189 if ( stringp(functionname) )
190 functionname = symbol_function( functionname, this_object() );
191
192 if ( !closurep(functionname) )
193 {
194 catch(raise_error(sprintf( "method %O doesn't exist\n",
195 functionname)); publish);
196 return;
197 }
198
199 AddExit( cmd, functionname );
200}
201
202
203void RemoveSpecialExit(string|string* cmd)
204{
205 RemoveExit( cmd );
206}
207
208
209varargs string GetExits( object viewer )
210{
Arathorn9845d112020-01-08 22:02:03 +0100211 string *indices;
212 int|string* hidden;
MG Mud User88f12472016-06-24 23:31:02 +0200213 string exits;
214
215 if ( QueryProp(P_DOOR_INFOS) )
216 call_other( DOOR_MASTER, "init_doors" );
217
218 indices = m_indices( Query(P_EXITS) || rescueExit() );
219
220 if ( pointerp(hidden = QueryProp(P_HIDE_EXITS)) )
221 indices -= hidden;
222
223 int n=sizeof(indices);
224 switch (n) {
225 case 0:
226 return "Es gibt keine sichtbaren Ausgaenge.\n";
227
228 case 1:
229 return "Es gibt einen sichtbaren Ausgang: " + indices[0] + ".\n";
230
231 case 2: case 3: case 4: case 5: case 6: case 7: case 8:
232 exits = "Es gibt " + NUMBERS[n-2] + " sichtbare Ausgaenge: ";
233 break;
234
235 default:
236 exits = "Es gibt viele sichtbare Ausgaenge: ";
237 }
238 exits += CountUp(indices);
239 return break_string( exits+".", 78 );
240}
241
242
243// Richtungsbefehle nur interpretieren, wenn der Spieler *im* Raum steht und
244// nicht davor (Transporter etc.)/o
Zesstra5b71ebb2018-03-07 20:50:35 +0100245public varargs void init(object origin)
MG Mud User88f12472016-06-24 23:31:02 +0200246{
247 if ( environment(this_player()) == this_object() )
248 add_action( "_normalfunction", "", 1 );
249}
250
251
252/* not only normal exits are handled here */
253
254int _normalfunction()
255{
256 int ret;
257 mapping exits = Query(P_EXITS, F_VALUE) || ([:3]);
258 if (!member(exits,query_verb()))
259 return 0;
260
261 string verb = query_verb();
Zesstra298c3de2019-04-25 14:12:35 +0200262 string|closure destroom = exits[query_verb(),0];
MG Mud User88f12472016-06-24 23:31:02 +0200263 string message = exits[query_verb(),1];
264
265 mixed hres = HookFlow(H_HOOK_EXIT_USE, ({verb, destroom, message}));
266 if(hres && pointerp(hres) && sizeof(hres)>H_RETDATA)
267 {
268 if(hres[H_RETCODE]==H_CANCELLED)
269 {
270 return 1;
271 }
272 else if(hres[H_RETCODE]==H_ALTERED
273 && pointerp(hres[H_RETDATA])
274 && sizeof(hres[H_RETDATA]) >= 3)
275 {
276 <string|closure>* hdata = hres[H_RETDATA];
277 if (!stringp(hdata[0])
278 || (!stringp(hdata[1]) && !closurep(hdata[1]))
279 || (hdata[2] && !stringp(hdata[2])) )
280 raise_error(sprintf("Invalide Daten aus H_HOOK_EXIT_USE: %.150O\n",
281 hdata));
282 verb = hdata[0];
283 destroom = hdata[1];
284 message = hdata[2];
285 }
286 }
287
288 if( closurep(destroom) )
289 {
290 ret = funcall( destroom, verb );
291
292 if(ret==MOVE_OK)
293 {
294 GiveEP( EP_EXIT, verb );
295 }
296
297 return ret;
298 }
299
300 if (!stringp(message))
301 {
302 if( member( ({ "sueden", "suedwesten", "westen","nordwesten", "norden",
303 "nordosten", "osten","suedosten" }), verb ) != -1 )
304 {
305 message = "nach " + capitalize(verb);
306 }
307 else if ( member( ({ "oben", "unten" }), verb ) != -1 )
308 {
309 message = "nach " + verb;
310 }
311 else
312 {
313 message = verb;
314 }
315 }
316
317 ret = this_player()->move( destroom, M_GO, message );
318
319 if (ret==MOVE_OK)
320 {
321 GiveEP( EP_EXIT, verb );
322 }
323
324 return ret;
325}
326
327static string _MakePath( string str )
328{
Zesstra6fa58d42018-11-08 22:21:21 +0100329 string *comp = explode( object_name(this_object()), "/" ) - ({""});
MG Mud User88f12472016-06-24 23:31:02 +0200330
Zesstra6fa58d42018-11-08 22:21:21 +0100331 switch( str[0] )
332 {
MG Mud User88f12472016-06-24 23:31:02 +0200333 case '.':
334 str = "/" + implode( comp[0..<2], "/" ) + "/" + str;
335 break;
Zesstra6fa58d42018-11-08 22:21:21 +0100336
337 // Konstruiert einen Pfad relativ zu /players/REAL_UID bzw.
338 // /d/<region>/REAL_UID/.
MG Mud User88f12472016-06-24 23:31:02 +0200339 case '~':
340 str = "/" + comp[0] + "/" + (comp[0] == "d" ? comp[1] + "/" : "")
341 +REAL_UID(this_object()) + str[1..];
342 break;
343 }
Zesstra6fa58d42018-11-08 22:21:21 +0100344 // macht den Pfad absolut und halbwegs "sane", loest .. im Pfad auf.
345 // Beruecksichtigt KEINE weiteren Platzhalter.
346 return master()->make_path_absolute(str);
MG Mud User88f12472016-06-24 23:31:02 +0200347}
348