blob: 1619e394ec6878c735845c33bf7deda87b7c04d9 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// death_room.c -- Der Todesraum
4//
5// $Id: death_room.c 9138 2015-02-03 21:46:56Z Zesstra $
6
7
8#pragma strict_types
9
10#include <defines.h>
11#include <properties.h>
12#include <moving.h>
13#include <language.h>
14#include <wizlevels.h>
15#include <daemon.h>
16#include <new_skills.h>
17
18inherit "/std/room";
19
20mixed *players;
21mapping msgCache;
22
23
24private void flush( int unusedOnly );
25private string expand( string table, int value );
26private string parseText( string msg, object pl );
27private void do_remove();
28private varargs mixed get_sequence( string str );
29void add_player( object pl );
30static int filter_ldfied( string str );
31public int SmartLog( string creat, string myname, string str, string date );
32public mixed hier_geblieben( mixed dest, int methods, string direction,
33 string textout, string textin );
Arathorn306aa032018-08-29 22:18:28 +020034
35public varargs void init(object oldenv)
MG Mud User88f12472016-06-24 23:31:02 +020036{
37 this_player()->move("/room/death/virtual/death_room_"+getuid(this_player()),
38 M_NOCHECK|M_SILENT|M_NO_SHOW);
39 return;
40}
41
42public void create()
43{
44 if (IS_CLONE(this_object())) return;
45 ::create();
46
47 players = ({});
48 flush(0);
49
50 SetProp( P_NAME, "Lars" );
51 SetProp( P_GENDER, MALE );
52 SetProp( P_ARTICLE, 0 );
53 SetProp( P_LIGHT,1 );
54 SetProp( P_NO_TPORT, NO_TPORT_OUT );
55 SetProp( P_LOG_FILE, "TOD/Todesraum" );
56 SetProp( P_INT_SHORT, "Arbeitszimmer des Todes" );
57 SetProp( P_INT_LONG, break_string(
58 "Ein dunkler Raum, erleuchtet von dunklem Licht, das sich der "
59 "Dunkelheit nicht so sehr zu widersetzen scheint, indem es "
60 "leuchtet, als dass es der dunkelste Punkt in einer weniger "
61 "dunklen Umgebung ist. Im seltsamen Licht erkennst Du einen "+
62 "zentral aufgestellten Schreibtisch, der mit Diagrammen und "
63 "Buechern bedeckt ist. Die Waende verschwinden hinter Regalen, "
64 "die gefuellt sind mit in Leder gebundenen, dunklen Waelzern, "
65 "von denen geheimnisvolle Runen leuchten.\nTod.", 78, 0, 1 ) );
66}
67
68
69public void reset()
70{
71 ::reset();
72 flush(1);
73}
74
75private void flush( int unusedOnly )
76{
77 string *mi;
78 int i;
79
80 if ( unusedOnly ){
81 if ( i = sizeof(mi = m_indices(msgCache)) ){
82 for ( ; i--; )
83 if ( msgCache[mi[i], 1] )
84 msgCache[mi[i], 1] = 0;
85 else
86 msgCache = m_copy_delete( msgCache, mi[i] );
87 }
88 }
89 else
90 msgCache = ([]);
91}
92
93
94private string expand( string table, int value )
95{
96 int sz, wert, i;
97 string *texte;
98
99 sz = sizeof( texte = explode( table, "##" ) - ({""}) );
100
101 for ( i = 0; i < sz; i++ )
102 if ( i%2 ){
103 sscanf( texte[i], "%d", wert );
104
105 if ( value < wert )
106 break;
107 }
108 else
109 table = texte[i];
110
111 return table;
112}
113
114
115#define TOS(s) s[<1]
116#define STOS(s) s[<2]
117#define PUSH(x,s) (s+= ({ x }))
118#define POP(s) (s=s[0..<2])
119
120// ziemlich vom htmld abgekupfert ;)
121private string
122parseText( string msg, object pl )
123{
124 string *words, *texte, *todo, *done;
125 int endFlag;
126
127 int sz = sizeof( words = regexplode(msg, "[<][^>]*[>]") );
128 todo = ({ });
129 done = ({""});
130
131 for ( int i=1; i<sz; i+=2 ){
132 string cmd = words[i][1..<2];
133 TOS(done) += words[i-1];
134
135 if ( cmd[0] == '/' ){
136 endFlag = 1;
137 cmd = cmd[1..];
138 }
139 else
140 endFlag = 0;
141
142 switch( cmd[0] ){
143 case 'A': /*** Alignment ersetzen ***/
144 if (!endFlag){
145 PUSH( cmd, todo );
146 PUSH( "", done );
147 }
148 else
149 if ( todo[<1] == "A" ){
150 STOS(done) += expand(TOS(done), (int) pl->QueryProp(P_ALIGN));
151 done = done[0..<2];
152 todo = todo[0..<2];
153 }
154 break;
155
156 case 'D': /*** Tode ersetzen ***/
157 if ( !endFlag ){
158 PUSH( cmd, todo );
159 PUSH( "", done );
160 }
161 else
162 if ( todo[<1] == "D" ){
163 STOS(done) += expand(TOS(done), (int) pl->QueryProp(P_DEADS));
164 POP(done);
165 POP(todo);
166 }
167 break;
168
169 case 'L': /*** Level ersetzen ***/
170 if ( !endFlag ){
171 PUSH( cmd, todo );
172 PUSH( "", done );
173 }
174 else
175 if ( todo[<1] == "L" ){
176 STOS(done) += expand(TOS(done), (int) pl->QueryProp(P_LEVEL));
177 POP(done);
178 POP(todo);
179 }
180 break;
181
182 case 'Z': /*** Zufall ersetzen ***/
183 if ( !endFlag ){
184 PUSH( cmd, todo );
185 PUSH( "", done );
186 }
187 else{
188 if ( todo[<1][0] == 'Z'){
189 int cnt, rnd, wert, sz2;
190
191 if ( !sscanf(todo[<1], "Z=%d", rnd) )
192 STOS(done) += "\n###\n### Syntax Error in <Z>!\n###\n\n";
193 else {
194 rnd = random(rnd);
195 sz2 = sizeof( texte = explode(TOS(done), "##") );
196 wert=0;
197 cnt=0;
198
199 for ( int k = 1; k < sz2; k += 2 ){
200 sscanf( texte[k], "%d", wert );
201 cnt += wert;
202 if ( rnd < cnt ) {
203 STOS(done) += texte[k+1];
204 break;
205 }
206 }
207 }
208 POP(done);
209 POP(todo);
210 }
211 }
212 break;
213
214 case 'G': /*** Gender ersetzen ***/
215 if ( !endFlag ){
216 PUSH( cmd, todo );
217 PUSH( "", done );
218 }
219 else{
220 if( sizeof( texte = regexplode(TOS(done), ":") ) == 3 )
221 STOS(done) += texte[2*((int) pl->QueryProp(P_GENDER)
222 == FEMALE)];
223 POP(done);
224 POP(todo);
225 }
226 break;
227
228 case 'R': /*** Rasse ersetzen ***/
229 if ( !endFlag ){
230 PUSH( cmd, todo );
231 PUSH( "", done );
232 }
233 else{
234 int race;
235
236 texte = regexplode( TOS(done), "\\|" );
237 race = 2 * (member( ({ "Mensch", "Elf", "Zwerg", "Hobbit",
238 "Feline", "Dunkelelf" }),
239 (string) pl->QueryProp(P_RACE) ) + 1);
240
241 if ( race >= sizeof(texte) )
242 race = 0;
243
244 STOS(done) += texte[race];
245 POP(done);
246 POP(todo);
247 }
248 break;
249
250 case 'n': /*** Name, normal geschrieben ***/
251 TOS(done) += (string) (pl->name(RAW));
252 break;
253
254 case 'N': /*** Name, in Grossbuchstaben ***/
255 TOS(done) += upperstring(pl->name(RAW));
256 break;
257 }
258 }
259 PUSH( words[<1], done );
260 return implode( done, "" );
261}
262
263
264public void heart_beat()
265{
266 for ( int j = sizeof(players); j--; )
267 if ( !objectp(players[j][0]) ||
268 environment(players[j][0]) !=
269 find_object("/room/death/virtual/death_room_"+getuid(players[j][0])) )
270 players[j] = 0;
271
272 players -= ({0});
273
274 if ( !sizeof(players) ) {
275 set_heart_beat(0);
276 return;
277 }
278
279 for ( int j = sizeof(players); j--; ) {
280 int nr;
281 string msg;
282
283 nr = ++players[j][1];
284
285 if ( mappingp(players[j][2]) )
286 msg = players[j][2][nr];
287 else
288 msg = 0;
289
290 if ( !msg )
291 msg = players[j][3][1][nr];
292
293 if ( msg )
294 tell_object( players[j][0], parseText( msg, players[j][0] ) );
295 }
296
297 do_remove();
298}
299
300private void
301do_remove()
302{
303 int res;
304 string prayroom;
305 object plobj, pl;
306
307 for ( int j = sizeof(players); j--; ){
308 if ( players[j][1] >= players[j][3][0]){
309 pl = players[j][0];
310 while ( plobj = present("\ndeath_mark", pl) )
311 plobj->remove();
312
313 if ( !(prayroom = (string) pl->QueryPrayRoom()) )
314 prayroom="/room/pray_room";
315
316 pl->Set( P_TMP_MOVE_HOOK, 0 );
317 pl->Set( P_NO_ATTACK, 0, F_QUERY_METHOD );
318 pl->Set( P_LAST_KILLER, 0 );
319 pl->Set( P_KILLER, 0 );
320 pl->Set( P_ENEMY_DEATH_SEQUENCE, 0 );
321 pl->Set( P_NEXT_DEATH_SEQUENCE, 0 );
322 pl->Set( P_POISON, 0, F_QUERY_METHOD );
323
324 if ( catch( res = (int) pl->move(prayroom, M_GO|M_SILENT|M_NOCHECK) )
325 || res < 1 )
326 pl->move( "/room/pray_room", M_GO|M_NOCHECK );
327
328 players[j] = 0;
329 }
330 }
331
332 players -= ({0});
333
334 if ( !sizeof(players) )
335 set_heart_beat(0);
336}
337
338private varargs mixed
339get_sequence( string str )
340{
341 string *sequences;
342 int i, len, cacheable;
343
344 if ( !stringp(str) || catch( len = file_size(str) ) || len <= 0 ){
345 sequences = get_dir( "/room/death/sequences/*" ) - ({ ".", "..", ".svn" });
346 str = "/room/death/sequences/" + sequences[random( sizeof(sequences) )];
347 }
348
349 if ( cacheable = ((sizeof(str) > 21) &&
350 (str[0..21] == "/room/death/sequences/")) ){
351 if ( member(msgCache, str) ){
352 msgCache[str, 1] = 1; // Touch it!
353 return ({ msgCache[str], str });
354 }
355 }
356
357 sequences = explode( read_file(str), "\n" );
358 sscanf( sequences[0], "%d", len );
359 string seq = implode( sequences[1..], "\n" );
360 sequences = regexplode( seq, "[0-9][0-9]*:" );
361 mapping m = ([]);
362
363 for ( i = 1; i < sizeof(sequences)-1; i += 2 )
364 m[(int) sequences[i]] = sequences[i+1];
365
366 if ( cacheable )
367 msgCache += ([ str: ({ len, m }); 1 ]);
368
369 return ({ ({ len, m }), str });
370}
371
372// Description: Adds a player to the list
373void add_player( object pl )
374{
375 int kart, kgen;
376 int escaped;
377 object kill_liv, kill_ob;
378 mixed dseq, act_seq, killer_name, killer_msg;
379
380 set_heart_beat(1);
381 kgen = MALE;
382
383 foreach(object prev : caller_stack(1)) {
384 if ( !objectp(prev) || prev == pl )
385 continue;
386
387 string fn = object_name(prev);
388
389 if ( fn[0..12] == "/secure/login" && !kill_liv ){
390 escaped = 1;
391 break;
392 }
393
394 if ( fn[0..7] == "/secure/" && fn[0..13] != "/secure/merlin" )
395 continue;
396
397 if ( fn[0..21] == "/room/death/death_mark" )
398 continue;
399
400 if ( living(prev) ){
401 kill_liv = prev; // Killer
402 break;
403 }
404
405 kill_ob = prev; // killendes Objekt
406 }
407
408 object pre = (object) pl->QueryProp(P_KILLER);
409 if ( objectp(pre) ) {
410 dseq = (mixed) pre->QueryProp(P_ENEMY_DEATH_SEQUENCE);
411
412 if( !(killer_name = (mixed) pre->QueryProp(P_KILL_NAME)) ){
413 killer_name = (mixed) pre->QueryProp(P_NAME);
414 kart = (int) pre->QueryProp(P_ARTICLE);
415 kgen = (int) pre->QueryProp(P_GENDER);
416 }
417
418 killer_msg = (mixed)pre->QueryProp(P_KILL_MSG);
419 }
420
421 if ( !killer_name && kill_liv && function_exists( "QueryProp", kill_liv ) ){
422 dseq = (mixed) kill_liv->QueryProp(P_ENEMY_DEATH_SEQUENCE);
423
424 if( !(killer_name = (mixed) kill_liv->QueryProp(P_KILL_NAME)) ){
425 killer_name = (mixed) kill_liv->QueryProp(P_NAME);
426 kart = (int) kill_liv->QueryProp(P_ARTICLE);
427 kgen = (int) kill_liv->QueryProp(P_GENDER);
428 }
429
430 killer_msg = (mixed) kill_liv->QueryProp(P_KILL_MSG);
431 pre = kill_liv;
432 }
433
434 if ( !killer_name && kill_ob && function_exists( "QueryProp", kill_ob ) ){
435 dseq = (mixed) kill_ob->QueryProp(P_ENEMY_DEATH_SEQUENCE);
436
437 if( !(killer_name = (mixed) kill_ob->QueryProp(P_KILL_NAME)) ){
438 killer_name = (mixed) kill_ob->QueryProp(P_NAME);
439 kart = (int) kill_ob->QueryProp(P_ARTICLE);
440 kgen = (int) kill_ob->QueryProp(P_GENDER);
441 }
442
443 killer_msg = (mixed) kill_ob->QueryProp(P_KILL_MSG);
444 pre = kill_ob;
445 }
446
447 // falls keine Sequenz gesetzt, eventuelle eigene Todessequenz nehmen
448 if (!dseq)
449 dseq = (mixed)pl->QueryProp(P_NEXT_DEATH_SEQUENCE);
450
451 act_seq = 0;
452
453 if ( mappingp(dseq) )
454 act_seq = get_sequence( "/room/death/sequences/lars" );
455 else if ( pointerp(dseq) ) // ganze Todessequenz...
456 act_seq = ({ dseq, 0 });
457 else if ( stringp(dseq) )
458 act_seq = get_sequence(dseq);
459
460 if(pl->query_hc_play()>1)
461 {
462 act_seq=({({22,([1:"Du faellst und faellst...\n",
463 5:"und faellst...\n",
464 10:"und faellst...\n",
465 12:"direkt in die Arme von TOD.\n",
466 14:"Triumphierend laechelt er Dich an.\n",
467 16:"NUN GEHOERST DU FUER IMMER MIR!\n",
468 18:"HAHHHAHAHAAAAAAAAAAHAAAAAAAAA!\n",
469 20:"TOD schlaegt Dir mit seiner Sense den Kopf ab.\n"])}),0});
470 }
471 if ( !act_seq )
472 act_seq = get_sequence();
473
474 if ( !mappingp(dseq) )
475 dseq = 0;
476
477 int i;
478 for ( i = sizeof(players); i--; )
479 if ( players[i][0] == pl )
480 break;
481
482 if ( i == -1 )
483 players += ({ ({ pl, 0, dseq, act_seq[0], act_seq[1], pre }) });
484 else
485 players[i][5] = pre;
486
487
488 if ( escaped ){
489 killer_name = "";
490 killer_msg = upperstring(getuid(pl)) + " VERSUCHTE, MIR ZU "
491 "ENTKOMMEN - JETZT HABE ICH WIEDER EXTRA-ARBEIT MIT "+
492 ((int) pl->QueryProp(P_GENDER) != 2 ? "IHM" : "IHR") +
493 " ...";
494 }
495 else if ( !killer_name ) {
496 if ( (string) pl->QueryProp(P_KILLER) == "gift" ){
497 killer_name = "Vergiftung";
498 kgen = FEMALE;
499 kart = 1;
500 }
501 else{
502 killer_name = "Etwas Geheimnisvolles und Unbekanntes";
503 kgen = NEUTER;
504 kart = 0;
505 }
506 }
507
508 if ( !pointerp(killer_msg) )
509 killer_msg = ({ killer_msg, 0, 0 });
510 else if ( sizeof(killer_msg) < 3 )
511 killer_msg += ({ 0, 0, 0 });
512
513 if ( stringp(killer_msg[0]) )
514 killer_msg[0] = sprintf( killer_msg[0], capitalize(getuid(pl)) );
515
516 SetProp( P_NAME, killer_name );
517 SetProp( P_ARTICLE, kart );
518 SetProp( P_GENDER, kgen );
519 string killname = Name(WER);
520 SetProp( P_NAME, "Lars" );
521 SetProp( P_ARTICLE, 0 );
522 SetProp( P_GENDER,MALE );
523
524 int magiertestie;
525 string testplayer = (string) pl->QueryProp(P_TESTPLAYER);
526 if (sizeof(testplayer))
527 {
528 if (testplayer[<5..<1]!="Gilde")
529 magiertestie = 1;
530 }
531
532 string kanal;
533 if (magiertestie || IS_LEARNING(pl))
534 kanal = "TdT";
535 else
536 kanal = "Tod";
537
538 CHMASTER->join( kanal, this_object() );
539
540 if ( (!stringp(killer_name) || killer_name != "") &&
541 (sizeof(killer_msg) < 4 || !killer_msg[3]) ){
542 if ( killer_msg[2] == PLURAL )
543 CHMASTER->send( kanal, this_object(),
544 killname + " haben gerade " +
545 capitalize(getuid(pl)) + " umgebracht." );
546 else
547 CHMASTER->send( kanal, this_object(),
548 killname + " hat gerade " +
549 capitalize(getuid(pl)) + " umgebracht." );
550 }
551
552 i = (int) pl->QueryProp(P_DEADS);
553 if ( i && (getuid(pl) == "key" || i%100 == 0 || i%250 == 0) ){
554 SetProp( P_NAME, "Tod" );
555 CHMASTER->send( kanal, this_object(),
556 sprintf( "DAS WAR SCHON DAS %dTE MAL!", i ) );
557 SetProp( P_NAME, "Lars" );
558 }
559
560 if( killer_msg[0] ){
561 if ( stringp(killer_name) && killer_name == "" ){
562 CHMASTER->send( kanal, this_object(),
563 break_string( funcall(killer_msg[0]), 78,
564 "["+kanal+":] " )[0..<2],
565 MSG_EMPTY );
566 return;
567 }
568 else {
569 if ( (killer_msg[1] < MSG_SAY) || (killer_msg[1] > MSG_GEMOTE) )
570 killer_msg[1] = MSG_SAY;
571
572 SetProp( P_NAME, killer_name );
573 SetProp( P_ARTICLE, kart );
574 SetProp( P_GENDER, kgen );
575 CHMASTER->send( kanal, this_object(), funcall(killer_msg[0]),
576 killer_msg[1] );
577 SetProp( P_NAME, "Lars" );
578 SetProp( P_ARTICLE, 0 );
579 SetProp( P_GENDER, MALE );
580 }
581 }
582
583 if ( pointerp(killer_msg = (mixed) pl->QueryProp(P_DEATH_MSG)) &&
584 sizeof(killer_msg) == 2 && stringp(killer_msg[0]) &&
585 intp(killer_msg[1]) ){
586 SetProp( P_NAME, capitalize(getuid(pl)) );
587 SetProp( P_ARTICLE, 0 );
588 SetProp( P_GENDER, pl->QueryProp(P_GENDER) );
589 CHMASTER->send( kanal, this_object(), killer_msg[0],
590 killer_msg[1] );
591 SetProp( P_NAME, "Lars" );
592 SetProp( P_ARTICLE, 0 );
593 SetProp( P_GENDER, MALE );
594 }
595
596 if (pl->query_hc_play()>1){
597 SetProp( P_NAME, "Tod" );
598 CHMASTER->send( kanal, this_object(),"NUN GEHOERST DU FUER EWIG MIR!" );
599 SetProp( P_NAME, "Lars" );
600 }
601}
602
603public int
604SmartLog( string creat, string myname, string str, string date )
605{
606 int i;
607 string fn;
608
609 for ( i = sizeof(players); i--; )
610 if ( players[i][0] == this_player() )
611 break;
612
613 // Spieler (Magier?) ist in keiner Todessequenz -> normales Repfile
614 if ( i == -1 )
615 return 0;
616
617 if ( !(fn = players[i][4]) ){
618 // Spieler hat eine unbekannte Todessequenz (kein Filename, Sequenz
619 // wurde komplett in P_ENEMY_DEATH_SEQUENCE abgelegt)
620 creat = "TOD/unbekannt.rep";
621 fn = "unbekannte Todessequenz";
622 }
623 else
624 // Jede Sequenz mit nem eigenen Repfile
625 creat = "TOD/" + explode( fn, "/" )[<1] + ".rep";
626
627 log_file( creat, myname + " von " + getuid(this_interactive())
628 + " ["+fn+"] (" + date + "):\n" + str + "\n" );
629
630 return 1;
631}
632
633public mixed hier_geblieben( mixed dest, int methods, string direction,
634 string textout, string textin )
635{
636 // Magier duerfen Spieler heraustransen
637 if ( this_interactive() && IS_LEARNER(this_interactive()) &&
638 (this_interactive() != previous_object() ||
639 IS_DEPUTY(this_interactive())) ){
640 previous_object()->Set( P_TMP_MOVE_HOOK, 0 );
641 return ({ dest, methods, direction, textout, textin });
642 }
643
644 // Spieler haengt noch in der Todessequenz
645 for ( int i = sizeof(players); i--; )
646 if ( objectp(players[i][0]) && previous_object() == players[i][0] &&
647 environment(previous_object()) == find_object(
648 "/room/death/virtual/room_death_" + getuid(previous_object()))&&
649 interactive(previous_object()) ) {
650 // Move nur erlaubt, wenn das Ziel wieder der Todesraum ist.
651 // wenn mal fuer nen bestimmten Zwecks Bewegungen raus aus dem
652 // Todesraum erforderlich sind, sollten hier entsprechende
653 // Ausnahmen eingebaut werden.
654 if ( (stringp(dest) &&
655 dest == object_name(environment(previous_object()))) ||
656 (objectp(dest) &&
657 dest == environment(previous_object())) ) {
658 previous_object()->Set( P_TMP_MOVE_HOOK, 0 );
659 return ({ dest, methods, direction, textout, textin });
660 }
661 else
662 return -1;
663 }
664
665 // Spieler ist nicht mehr im Raum oder eingeschlafen
666 if ( previous_object() )
667 previous_object()->Set( P_TMP_MOVE_HOOK, 0 );
668
669 return ({ dest, methods, direction, textout, textin });
670}