blob: 607e07a155d8fc5df30325bb9b13f350c09cf166 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001#pragma strong_types,save_types
2#pragma no_shadow
Arathorn3bc8ea62021-03-28 13:02:49 +02003// Teamobjekt muss leider noch geerbt werden koennen.
4// Arathorn, 2021-03-28
5//#pragma no_inherit
MG Mud User88f12472016-06-24 23:31:02 +02006#pragma pedantic
7
8#include <living/team.h>
9#include <properties.h>
10#include <language.h>
11#include <new_skills.h>
12#include <ansi.h>
13#include <wizlevels.h>
Zesstrafb2614a2022-01-19 22:42:57 +010014#include <living/comm.h>
15
MG Mud User88f12472016-06-24 23:31:02 +020016#define ME this_object()
17#define PO previous_object()
18#define TP this_player()
19#define TI this_interactive()
20
21#define AUTOINF_HP_MINUS 0x01
22#define AUTOINF_HP_PLUS 0x02
23#define AUTOINF_SP_MINUS 0x04
24#define AUTOINF_SP_PLUS 0x08
25#define AUTOINF_INSTANT 0x10
26
27private nosave mapping is_member; // Teammitglieder
28private nosave object leader; // Teamleiter
29private nosave string tname; // Teamname
30private nosave mapping wanted_row; // Gewuenschte Reihe
31private nosave mapping wimpy_row; // Fluchtreihe
32private nosave mapping act_row; // Aktuelle Reihe
33private nosave mapping autofollow; // Spieler folgt Teamleiter
34private nosave mapping attack_cmd; // Spieler hat Angriffsbefehl
35private nosave mapping assoc_mem; // Zugeordnete Mitglieder (Kampf NPCs)
36private nosave int *formin,*formax; // Formation
37private nosave mixed *rows; // Die Reihen
38private nosave int last_reorder; // Letzte Formationspruefung
39private nosave mapping h; // Sortier-Score: 125*G.Reihe-HP
40private nosave object *att_exec; // Mitglieder, die Attacke ausfuehren.
41private nosave object *mis_attacked;// (Ex-)Mitglieder ohne Begruessungsschlag
42private nosave mapping mis_init_att;// Fehlende Begruessungsschlaege
43private nosave mapping hp_info; // HP, MAX_HP, SP, MAX_SP
44private nosave int autoinf_flags;
45private nosave mapping autoinf_hp;
46private nosave mapping autoinf_sp;
47private nosave int autoinf_time;
Zesstrafb2614a2022-01-19 22:42:57 +010048private nosave object *autoinf_subscribers = ({});
MG Mud User88f12472016-06-24 23:31:02 +020049private nosave string *hist;
50
51private nosave object debugger;
52
53void _set_debug() {
54 if (!debugger && IS_LEARNER(TI))
55 debugger=TI;
56 else if (debugger==TI)
57 debugger=0;
58}
59
60private void debug(string str) {
61 if (objectp(debugger) && stringp(str))
62 tell_object(debugger,"#TEAM: "+str);
63}
64
65void create() {
66 autoinf_flags=0;
67 autoinf_sp=([]);
68 autoinf_hp=([]);
69 is_member=([]);
70 leader=0;
71 wanted_row=([]);
72 act_row=([]);
73 wimpy_row=([]);
74 autofollow=([]);
75 attack_cmd=([]);
76 assoc_mem=([]);
77 formin=({1,0,0,0,0});
78 formax=({5,4,3,2,1});
79 rows=EMPTY_TEAMARRAY;
80 h=([]);
81 att_exec=({});
82 mis_init_att=([]);
83 mis_attacked=({});
84 hp_info=([]);
85 hist=({});
86 if (object_name(ME)==TEAM_OBJECT)
87 return;
88 if (!stringp(tname=TEAM_MASTER->RegisterTeam()))
89 tname="?";
90}
91
92object *Members() {
93 return (m_indices(is_member)-({0}));
94}
95
96object Leader() {
97 return leader;
98}
99
100varargs string name(int casus, int demon) {
101 if (!stringp(tname))
102 return "Team ?";
103 return "Team "+capitalize(tname);
104}
105
106varargs string Name(int casus, int demon) {
107 return name(casus,demon);
108}
109
110varargs int remove(int silent) {
111 if (mappingp(is_member) && sizeof(Members())) // Nur leere Teams removen
112 return 0;
113 TEAM_MASTER->UnregisterTeam(); // Teamnamen freigeben.
114 destruct(ME);
115 return 1;
116}
117
118private void TryRemove() {
119 if (clonep(ME)
120 && (!mappingp(is_member) || !sizeof(Members()))
121 && !first_inventory(ME)
122 && find_call_out("remove")<0)
123 call_out("remove",0);
124}
125
126int CmpFirstArrayElement(mixed *a, mixed *b) {
127 return(a[0]<b[0]);
128}
129
130varargs private void gtell(string str, string who, int tohist) {
131 int i;
132 object *tmembers,rochus;
133 string prefix,msg;
134
135 tmembers=Members();
136 prefix=sprintf("[%s:%s] ",name(),stringp(who)?who:"");
137 msg=break_string(str,78,prefix);
138 for (i=sizeof(tmembers)-1;i>=0;i--)
139 tell_object(tmembers[i],msg);
140 if (objectp(rochus=find_player("rochus"))
141 && rochus->QueryProp("debug_team"))
142 tell_object(rochus,msg);
143 if (tohist)
144 hist=(hist+({break_string(str+" <"+ctime()[11..15]+">",78,prefix)}))[-100..];
145}
146
147int IsMember(object ob) {
148 return (objectp(ob) && is_member[ob]);
149}
150
151int IsInteractiveMember(object ob) {
152 return (objectp(ob) && is_member[ob] && query_once_interactive(ob));
153}
154
155varargs private int *GetHpInfo(object ob, closure cl) {
156 int *res;
157
158 if (!closurep(cl)) cl=symbol_function("QueryProp",ob);
159 if (!pointerp(res=hp_info[ob]) || sizeof(res)<4)
160 res=({0,funcall(cl,P_MAX_HP),0,funcall(cl,P_MAX_SP)});
161 res[0]=funcall(cl,P_HP);
162 res[2]=funcall(cl,P_SP);
163 return hp_info[ob]=res;
164}
165
166int CompareHp(object a, object b) {
167 return h[a]>h[b];
168}
169
170// Aktualisiert act_row (->wer steht in welcher Reihe).
171private void UpdateActRow() {
Arathorn99f3bc72021-03-28 13:04:13 +0200172 int i,update_hp;
MG Mud User88f12472016-06-24 23:31:02 +0200173 object *new;
174 mixed aso;
175
176 act_row=([]);
177 rows[0]+=Members();
178 update_hp=0;
179 if (!mappingp(h)) {
180 h=([]);
181 update_hp=1;
182 }
183 for (i=MAX_TEAMROWS-1;i>=0;i--) {
184 new=({});
185 foreach(object ob: rows[i]) {
186 if (objectp(ob) && is_member[ob] && !act_row[ob]) {
187 act_row[ob]=i+1;
188 new+=({ob});
189 if (update_hp) {
190 if (!objectp(aso=assoc_mem[ob]) || environment(aso)!=environment(ob))
191 aso=ob;
192 h[ob]=
193 1000*wanted_row[aso]
194 +40*act_row[aso]
195 -(query_once_interactive(aso)?8:2)*aso->QueryProp(P_HP)
196 -query_once_interactive(ob);
197 // NPCs bekommen fast gleichen Wert wie Caster,
198 // im Zweifelsfalle steht der Caster weiter vorne...
199 }
200 }
201 }
202 rows[i]=sort_array(new,"CompareHp",ME);
203 }
204}
205
206private void CheckFormation() {
207 int i,mincap,maxcap,d,num;
208
209 mincap=maxcap=0;
210 if (formin[0]<1)
211 formin[0]=1;
212
213 // erstmal die Wuensche normalisieren/korrigieren auf sinnvolle Werte.
214 for (i=0;i<MAX_TEAMROWS;i++) {
215 if (formin[i]<0) formin[i]=0;
216 if (formax[i]<formin[i]) formax[i]=formin[i];
217 if (formax[i]>MAX_TEAM_ROWLEN) formax[i]=MAX_TEAM_ROWLEN;
218 if (formin[i]>formax[i]) formin[i]=formax[i];
219 mincap+=formin[i]; // Summe der min. je Reihe gewuenschten.
220 maxcap+=formax[i]; // Summe der max. je Reihe gewuenschten.
221 }
222
223 num=sizeof(Members());
224
225 // max. gewuenschte Reihenlaenge verlaengern, wenn die Summe der maximal je
226 // Reihe gewuenschten kleiner als die Anzahl der Spieler ist. Von vorne
227 // natuerlich.
228 d=num-maxcap;
229 for (i=0;i<MAX_TEAMROWS;i++) {
230 if (d<=0)
231 break;
232 d-=(MAX_TEAM_ROWLEN-formax[i]);
233 formax[i]=MAX_TEAM_ROWLEN;
234 if (d<0)
235 formax[i]+=d; // doch noch was uebrig, wieder anhaengen.
236 }
237 // min. gewuenschte Reihenlaenge auf 0 verkuerzen, wenn die Summe der
238 // minimal je Reihe gewuenschten groesser als die Anzahl der Spieler ist.
239 // Von hinten natuerlich.
240 d=mincap-num; //
241 for (i=MAX_TEAMROWS-1;i>=0;i--) {
242 if (d<=0)
243 break;
244 d-=formin[i];
245 formin[i]=0;
246 if (d<0)
247 formin[i]-=d; // doch noch was uebrig, wieder anhaengen.
248 }
249}
250
251private void MakeFormation() {
252 // Verlegungsstrategie:
253 // Richtung Test Verschieben
254 // 1. -----> a) MAX <- X
255 // b) MAX X ->
256 // c) MIN X <- <- <- <-
257 // 2. <----- a) MIN -> -> -> -> X
258 // b) MAX <- X
259 int i,j,d;
260
261 last_reorder=time();
262 UpdateActRow();
263 CheckFormation();
264 for (i=0;i<MAX_TEAMROWS;i++) {
265 d=sizeof(rows[i]);
266 if (d<formin[i] || d>formax[i])
267 break;
268 }
269 if (i>=MAX_TEAMROWS)
270 return; // Formation ist noch in Ordnung
271
272 for (i=0;i<MAX_TEAMROWS;i++) {
273 if (sizeof(rows[i])>formax[i]) { // Reihe ist zu voll
274 if (i>0) {
275 d=formax[i-1]-sizeof(rows[i-1]);
276 if (d>0) { // Reihe vorher hat d freie Plaetze
277 rows[i-1]+=rows[i][0..(d-1)]; // Also d Mitglieder abgeben
278 rows[i]=rows[i][d..];
279 }
280 }
281 if (i<MAX_TEAMROWS-1 && sizeof(rows[i])>formax[i]) {// Immer noch zu voll
282 rows[i+1]=rows[i][formax[i]..]+rows[i+1]; // Rest nach hinten.
283 rows[i]=rows[i][0..(formax[i]-1)];
284 }
285 continue; // War zu voll, kann nicht zu leer sein
286 }
287 for (j=i+1;j<MAX_TEAMROWS;j++) {
288 d=formin[i]-sizeof(rows[i]);
289 if (d<=0) // Ausreichende Anzahl
290 break; // kein weiteres j noetig
291 rows[i]+=rows[j][0..(d-1)]; // Sonst Nachschub von hinten holen
292 rows[j]=rows[j][d..];
293 }
294 }
295 for (i=MAX_TEAMROWS-1;i>0;i--) {
296 for (j=i-1;j>=0;j--) {
297 d=formin[i]-sizeof(rows[i]);
298 if (d<=0) // Ausreichende Anzahl
299 break; // kein weiteres j noetig
300 rows[i]+=rows[j][0..(d-1)]; // Sonst Nachschub von vorne holen
301 rows[j]=rows[j][d..];
302 }
303 d=sizeof(rows[i])-formax[i];
304 if (d>0) {
305 rows[i-1]+=rows[i][0..(d-1)]; // Ueberschuss nach vorne schieben
306 rows[i]=rows[i][d..];
307 }
308 }
309 UpdateActRow();
310}
311
312private void RemoveFromRow(object ob, int src) {
313 if (src<0 || src>=MAX_TEAMROWS)
314 return;
315 rows[src]-=({ob});
316 if (sizeof(rows[src])>=formin[src])
317 return;
318 // Falls hinten noch Ueberschuss da ist, her damit.
319 if (src<MAX_TEAMROWS-1 && sizeof(rows[src+1])-1>=formin[src+1]) {
320 rows[src]+=rows[src+1][0..0];
321 rows[src+1]=rows[src+1][1..];
322 return;
323 }
324 // Falls vorne noch Ueberschuss da ist, her damit.
325 if (src>0 && sizeof(rows[src-1])-1>=formin[src-1]) {
326 rows[src]=rows[src-1][<1..]+rows[src];
327 rows[src-1]=rows[src-1][0..<2];
328 }
329}
330
331private void AddToRow(object ob, int dest) {
332 if (dest<0 || dest>=MAX_TEAMROWS)
333 return;
334 rows[dest]+=({ob});
335 if (sizeof(rows[dest])<=formax[dest])
336 return;
337 // Falls vorne noch jemand hin kann, dorthin
338 if (dest>0 && sizeof(rows[dest-1])+1<=formax[dest-1]) {
339 rows[dest-1]+=rows[dest][0..];
340 rows[dest]=rows[dest][1..];
341 return;
342 }
343 // Falls hinten noch jemand hin kann, dorthin
344 if (dest<MAX_TEAMROWS-1 && sizeof(rows[dest+1])+1<=formax[dest+1]) {
345 // Dest: ({... <3, <2, ob});
346 rows[dest+1]=rows[dest][<2..<2]+rows[dest+1];
347 rows[dest]=rows[dest][0..<3]+({ob});
348 }
349}
350
351private void CycleRows(object ob, int src, int dest) {
352 int i;
353
354 if (src<0 || src>=MAX_TEAMROWS || dest<0 || dest>=MAX_TEAMROWS)
355 return;
356 rows[src]-=({ob});
357 if (sizeof(rows[src])<formin[src] || sizeof(rows[dest])>=formax[dest]) {
358 if (src<dest) { // (<- -X) <- <- <- <- (+X <-)
359 for (i=src+1;i<=dest;i++) {
360 rows[i-1]+=rows[i][0..0];
361 rows[i]=rows[i][1..];
362 }
363 } else if (src>dest) { // (-> +X) -> -> -> -> (-X ->)
364 for (i=src-1;i>=dest;i--) {
365 rows[i+1]=rows[i][<1..]+rows[i+1];
366 rows[i]=rows[i][0..<2];
367 }
368 }
369 }
370 if (src<=dest)
371 rows[dest]+=({ob});
372 else
373 rows[dest]=({ob})+rows[dest];
374}
375
376// tauscht zufaellig aus den ersten 4 Reihen aus einer Reihe den letzten mit
377// dem ersten aus der Folgereihe, wenn die der Score des vorderen Spieler mehr
378// als 10 groesser als der des hinteren Spielers ist.
379private void RandomChangeRow() {
380 int *nums,i;
381 object p1,p2;
382
383 if (!mappingp(h))
384 UpdateActRow();
385 for (nums=({0,1,2,3});sizeof(nums);nums-=({i})) {
386 i=nums[random(sizeof(nums))];
387 if (!sizeof(rows[i]) || !sizeof(rows[i+1])) continue;
388 if (!objectp(p1=rows[i][<1]) || !objectp(p2=rows[i+1][0])) continue;
389 if (wanted_row[p1]<wanted_row[p2]) continue;
390 if (h[p2]-h[p1]>=-10) continue;
391 rows[i][<1]=p2;
392 rows[i+1][0]=p1;
393 return;
394 }
395}
396
397varargs private string
398CountUpNames(object *obs, string zsing, string zplur) {
399 string res;
400 object ob;
401 int i,sz;
402
403 res="";
404 if (!pointerp(obs)) return res;
405 if (!stringp(zsing)) zsing="";
406 if (!stringp(zplur)) zplur="";
407 if (sz=sizeof(obs)) {
408 for (i=0;i<sz;i++) {
409 if (i)
410 res+=((i<sz-1)?", ":" und ");
411 if (objectp(ob=obs[i]))
412 res+=ob->name(WER);
413 }
414 if (sz>1)
415 res+=zplur;
416 else
417 res+=zsing;
418 }
419 return res;
420}
421
422static void DoChangeRow(object pl, int dest) {
423 mapping old_row,ec1,ec2,ecb;
424 int i;
425 object *obs,*envs,env;
426 string *msg,str;
427
428 dest--;
429 CheckFormation();
430 h=0; // damit HP-Liste geupdated wird.
431 UpdateActRow();
432 old_row=deep_copy(act_row);
433
434 // welche Objekte bewegen?
435 obs=({});
436 if (objectp(pl)) {
437 obs=({pl});
438 if (pointerp(assoc_mem[pl]))
439 obs+=assoc_mem[pl];
440 } else {
441 RandomChangeRow();
442 }
443
444 foreach (object ob:obs) {
445 if (!objectp(ob))
446 continue;
447 // Alle assoziierten NPC kriegen die gleiche gewuenschte Reihe wie der
448 // Spieler.
449 wanted_row[ob]=wanted_row[pl];
450 UpdateActRow();
451 // und dann in die gew. Reihe stopfen.
452 int src=act_row[ob]-1;
453 if (dest<0 || dest>=MAX_TEAMROWS)
454 RemoveFromRow(ob,src);
455 else if (src<0 || src>=MAX_TEAMROWS)
456 AddToRow(ob,dest);
457 else if (src!=dest)
458 CycleRows(ob,src,dest);
459 }
460
461 MakeFormation();
462
463 obs = Members(); // alle Members beruecksichtigen beim Abgleich!
464 object *changed = allocate(0);
465 foreach (object ob:obs)
466 if (objectp(ob) && old_row[ob]!=act_row[ob]) {
467 ob->InformRowChange(old_row[ob],act_row[ob]);
468 changed += ({ob});
469 }
470
471 // Ab jetzt nur noch Ausgabe.
472 if (get_eval_cost()<800000) return; // War schon teuer genug, Lagvermeidung
473 msg=({});ec1=([]);ec2=([]);ecb=([]);
474 foreach (object ob:changed) {
475 tell_object(ob,sprintf("Du bist jetzt in Reihe %d.\n",act_row[ob]));
476 msg+=({sprintf("%s->%d",ob->Name(WER),act_row[ob])});
477 if (query_once_interactive(ob) && !interactive(ob)) continue;
478 if (!objectp(env=environment(ob))) continue;
479 if (old_row[ob]<=1) {
480 if (!pointerp(envs=ec1[env])) envs=({});
481 ec1[env]=envs+({ob});ecb[env]|=1;
482 }
483 if (act_row[ob]<=1) {
484 if (!pointerp(envs=ec2[env])) envs=({});
485 ec2[env]=envs+({ob});ecb[env]|=2;
486 }
487 }
488 if (sizeof(msg))
489 gtell(implode(msg,", ")); // Ausgabe an alle Gruppenmitglieder.
490 m_delete(ecb,find_object("/room/netztot")); // Das gaebe Mega-Lag :-)
491 envs=m_indices(ecb);obs=Members();
492 for (i=sizeof(envs)-1;i>=0;i--) {
493 if (!objectp(env=envs[i])) continue;
494 str="";
495 str+=CountUpNames(ec1[env]," tritt zurueck"," treten zurueck");
496 if (ecb[env]==3) str+=", ";
497 str+=CountUpNames(ec2[env]," tritt vor"," treten vor");
498 str+=".\n";
499 tell_room(env,capitalize(break_string(str,78)),obs);
500 }
501}
502
503void UpdateFormation() {
504 DoChangeRow(0,0);
505}
506
507int SwapRows(object ob1, object ob2) {
508 int i,r1,r2,p1,p2;
509
510 if (!objectp(ob1) || !objectp(ob2) || ob1==ob2)
511 return 0;
512 r1=r2=-1;
513 for (i=0;i<MAX_TEAMROWS;i++) {
514 if (r1==-1 && (p1=member(rows[i],ob1))>=0)
515 r1=i;
516 if (r2==-1 && (p2=member(rows[i],ob2))>=0)
517 r2=i;
518 }
519 if (r1==-1 || r2==-1)
520 return 0;
521 if (r1==r2)
522 return 1;
523 if (r1<r2) { // Nicht Monster vor Spieler stellen
524 if (query_once_interactive(ob1) && !interactive(ob2))
525 return 0;
526 } else {
527 if (query_once_interactive(ob2) && !interactive(ob1))
528 return 0;
529 }
530 rows[r1][p1]=ob2;ob2->InformRowChange(r2,r1);
531 rows[r2][p2]=ob1;ob1->InformRowChange(r1,r2);
532 gtell(ob1->Name(WER)+" und "+ob2->name(WER)+" tauschen die Plaetze.\n");
533 return 1;
534}
535
536private int ChangeRow(string arg) {
537 int num;
538
539 if (!arg || sscanf(arg,"%d",num)!=1)
540 return notify_fail("In welche Reihe willst Du denn wechseln?\n"),0;
541 if (num<1 || num>MAX_TEAMROWS)
542 return notify_fail("Die Reihenangabe ist ungueltig.\n"),0;
543 TP->SetProp(P_TEAM_WANTED_ROW,wanted_row[TP]=num);
544 printf("Du versuchst in Reihe %d zu wechseln.\n",num);
545 DoChangeRow(TP,num);
546 return 1;
547}
548
549private int ChangeWimpyRow(string arg) {
550 int num;
551
552 if (!arg || sscanf(arg,"%d",num)!=1)
553 return notify_fail("In welche Reihe willst Du fliehen?\n"),0;
554 if (num<0 || num>MAX_TEAMROWS)
555 return notify_fail("Die Reihenangabe ist ungueltig.\n"),0;
556 TP->SetProp(P_TEAM_WIMPY_ROW,wimpy_row[TP]=num);
557 if (num>1)
558 printf("Bei der Flucht wirst Du in Reihe %d wechseln.\n",num);
559 else
560 write("Bei der Flucht wirst Du den Raum verlassen.\n");
561 return 1;
562}
563
564mixed *PresentRows(object env) {
565 int i,j,d,arbeit;
566 mixed *res;
567 object *nd,ob;
568
569 if (!objectp(env))
570 env=environment(TP);
571 if (last_reorder!=time() || !mappingp(h))
572 UpdateFormation();
573 res=EMPTY_TEAMARRAY;arbeit=0;nd=({});
574 for (i=0;i<MAX_TEAMROWS;i++) {
575 object *new=({});
576 foreach(ob: rows[i]) {
577 if (objectp(ob) && is_member[ob] && environment(ob)==env) {
578 if (query_once_interactive(ob) && !interactive(ob)) {
579 nd+=({ob});
580 arbeit=1;
581 } else {
582 new+=({ob});
583 }
584 } else {
585 arbeit=1;
586 }
587 }
588 res[i]=new;
589 }
590 if (!arbeit)
591 return res;
592 for (i=j=0;i<MAX_TEAMROWS;i++) {
593 if (j<=i) j=i+1;
594 for (;j<MAX_TEAMROWS;j++) {
595 d=formin[i]-sizeof(res[i]);
596 if (d<=0) // Ausreichende Anzahl
597 break; // kein weiteres j noetig
598 res[i]+=res[j][0..(d-1)]; // Sonst Nachschub von hinten holen
599 res[j]=res[j][d..];
600 }
601 }
602 res[MAX_TEAMROWS-1]+=nd; // Netztote bieten keine Deckung, nach hinten.
603 return res;
604}
605
606mapping PresentPositions(mixed pres_rows) {
607 mapping res=([]);
608 if (objectp(pres_rows))
609 pres_rows=PresentRows(pres_rows);
610 if (!pointerp(pres_rows))
611 return res;
612 for (int i=0;i<MAX_TEAMROWS;i++) {
613 foreach(object ob: pres_rows[i])
614 if (ob)
615 res[ob]=i+1;
616 }
617 return res;
618}
619
620
621varargs int FleeToRow(object ob) {
622 int num;
623
624 if (!objectp(ob))
625 ob=TP;
626 if (!IsMember(ob))
627 return 0;
628 h=0; // Reihen bei naechster Abfrage neu sortieren
629 num=wimpy_row[ob];
630 if (num<2 || num>MAX_TEAMROWS) // Flucht in 1. Reihe nicht sinnvoll.
631 return 0;
632 if (num==wanted_row[ob]) // Ist schonmal nach hinten geflohen.
633 return 0;
634 tell_object(ob,sprintf("Du versuchst in Reihe %d zu fliehen.\n",num));
635 ob->SetProp(P_TEAM_WANTED_ROW,wanted_row[ob]=num);
636 DoChangeRow(ob,num);
637 if (PresentPositions(environment(ob))[ob]<=1) // Flucht gescheitert?
638 return 0;
639 return 1;
640}
641
642static int ChangeFormation(string arg) {
643 string *words;
644 int i,min,max;
MG Mud User88f12472016-06-24 23:31:02 +0200645
646 if (arg=="aus")
647 arg="1-6 0-6 0-6 0-6 0-6";
648 i=sizeof(words=old_explode(arg," "));
649 if (i>MAX_TEAMROWS)
650 i=MAX_TEAMROWS;
651 for (--i;i>=0;i--) {
652 if (sscanf(words[i],"%d-%d",min,max)==2) {
653 formin[i]=min;
654 formax[i]=max;
655 } else if (sscanf(words[i],"%d",min)==1) {
656 formin[i]=formax[i]=min;
657 }
658 }
659
660 UpdateFormation();
661
662 words=({});
663 for (i=0;i<MAX_TEAMROWS;i++)
664 words+=({sprintf("%d-%d",formin[i],formax[i])});
665 gtell("Die Formation ist jetzt "+implode(words," / ")+".\n");
666 return 1;
667}
668
669int Shout(string str) {
670 if (!str || str=="")
671 return notify_fail("Was willst Du den anderen Teammitgliedern sagen?\n"),0;
672 gtell(str,TP->Name(WER),1);
673 return 1;
674}
675
676int Hist(string str) {
677 int i,anz,maximal;
678
679 // non-interactive oder Nicht-Mitglieder sollten die Hist nicht abfragen.
680 if (!IsInteractiveMember(TP)) return -1;
681
682 maximal=sizeof(hist);
683 if (str && sscanf(str,"%d",anz)==1)
684 i=maximal-anz;
685 if (i<0)
686 i=0;
687
688 TP->More(sprintf("%@s",hist[i..maximal])||"");
689 return 1;
690}
691
692private void DoChangeLeader(object ob) {
693 if (objectp(leader) && leader->QueryProp(P_TEAM_LEADER)==ME)
694 leader->SetProp(P_TEAM_LEADER,0);
695 leader=ob;
696 leader->SetProp(P_TEAM_LEADER,ME);
697}
698
699private int ChangeLeader(string arg) {
700 object ob;
701
702 if (stringp(arg) && arg!="") {
703 if (!objectp(ob=find_player(arg))
704 && !objectp(ob=present(arg,environment(TP))))
705 return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
706 } else {
707 ob=TP;
708 }
709 if (objectp(leader)
710 && TP!=leader
711 && (!interactive(TP) || (interactive(leader) && query_idle(leader)<180)))
712 return notify_fail("Der Teamleiter ist noch aktiv.\n"),0;
713 if (objectp(leader)
714 && query_once_interactive(leader)
715 && !query_once_interactive(leader))
716 return notify_fail("Nur ein Spieler kann das Team leiten.\n"),0;
717 if (!IsMember(ob))
718 return notify_fail(ob->Name(WER)+" ist kein Teammitglied.\n"),0;
719 DoChangeLeader(ob);
720 gtell(ob->Name(WER)+" leitet jetzt das Team.\n");
721 return 1;
722}
723
724varargs private int CheckSecond(object pl, object *obs) {
725 mixed x,ip;
726 mapping second;
727 object ob;
728 int i;
729
730 if (!objectp(pl))
731 pl=TP;
732 if (!query_once_interactive(pl))
733 return 0;
734 if (!pointerp(obs))
735 obs=Members();
736 obs-=({pl});
737 second=([]);
738 for (i=sizeof(obs)-1;i>=0;i--) {
739 if (!objectp(ob=obs[i]) || !query_once_interactive(ob)) continue;
740 second[getuid(ob)]=1;
741 if (stringp(x=ob->QueryProp(P_SECOND)))
742 second[lower_case(x)]=1;
743 }
744 if (second[getuid(pl)] ||
745 (stringp(x=pl->QueryProp(P_SECOND)) && second[lower_case(x)]))
746 return 1;
747
748 if (!stringp(ip=pl->QueryProp(P_CALLED_FROM_IP)) || ip=="")
749 return 0;
750 for (i=sizeof(obs)-1;i>=0;i--) {
751 if (!objectp(ob=obs[i]) || !query_once_interactive(ob)) continue;
752 if (ob->QueryProp(P_CALLED_FROM_IP)!=ip) continue;
753 log_file("rochus/zweitieverdacht",
754 sprintf("%s %s,%s\n",ctime()[4..15],getuid(pl),getuid(ob)));
755 break;
756 }
757 return 0;
758}
759
760static int Allowed(object pl) {
761 mixed x;
762 string nm;
763
764 if (!objectp(pl))
765 return notify_fail("WER soll ins Team aufgenommen werden?\n"),0;
766 if (pl==TP)
767 nm="Du b";
768 else
769 nm=pl->Name(WER)+" ";
770 if (objectp(x=pl->QueryProp(P_TEAM)) && x!=ME)
771 return notify_fail(nm+"ist schon in einem anderen Team.\n"),0;
772 if (pl->QueryGuest())
773 return notify_fail(nm+"ist ein Gast.\n"),0;
774 if (pl->QueryProp(P_GHOST))
775 return notify_fail(nm+"ist ein Geist.\n"),0;
776 if (CheckSecond(pl))
777 return notify_fail(nm+"ist Zweitspieler eines Teammitglieds.\n"),0;
778 if (sizeof(filter(Members(),"IsInteractiveMember",ME))
779 >=MAX_TEAM_MEMBERS)
780 return notify_fail("Es sind schon zuviele Spieler im Team.\n"),0;
781 return 1;
782}
783
784private void DoAddMember(object ob) {
785 closure cl;
786
787 if (!IsMember(leader))
788 DoChangeLeader(ob);
789 ob->SetProp(P_TEAM,ME);
790 if (IsMember(ob))
791 return;
792 is_member[ob]=1;
793 cl=symbol_function("QueryProp",ob);
794 attack_cmd[ob]=funcall(cl,P_TEAM_ATTACK_CMD);
795 autofollow[ob]=funcall(cl,P_TEAM_AUTOFOLLOW);
796 wimpy_row[ob]=funcall(cl,P_TEAM_WIMPY_ROW);
797 wanted_row[ob]=funcall(cl,P_TEAM_WANTED_ROW);
798 if (!wanted_row[ob]) wanted_row[ob]=1;;
799 ob->SetProp(P_TEAM_NEWMEMBER,0);
800 GetHpInfo(ob,cl);
801 if (query_once_interactive(ob)) ob->AddHpHook(ME);
802 if (!objectp(assoc_mem[ob]))
803 gtell(ob->Name(WER)+" wurde ins Team aufgenommen.\n");
804 DoChangeRow(ob,wanted_row[ob]);
805}
806
807int AddAssocMember(object caster, object npc) {
808 object *obs;
809
810 if (extern_call() && PO!=caster)
811 return 0;
812 if (!IsMember(caster)
813 || !objectp(npc)
814 || query_once_interactive(npc)
815 || IsMember(npc)
816 || objectp(assoc_mem[caster])
817 || assoc_mem[npc]
818 || caster==npc)
819 return 0;
820 assoc_mem[npc]=caster;
821 DoAddMember(npc);
822 if (!pointerp(obs=assoc_mem[caster]))
823 obs=({});
824 obs=(obs-({npc,0}))+({npc});
825 assoc_mem[caster]=obs;
826 return 1;
827}
828
829static void AddMemberAndAssocs(object caster) {
830 object *obs,ob;
831 int i;
832
833 DoAddMember(caster);
834 if (!pointerp(obs=caster->QueryProp(P_TEAM_ASSOC_MEMBERS)))
835 return;
836 for (i=sizeof(obs)-1;i>=0;i--)
837 if (objectp(ob=obs[i]) && !caster->IsEnemy(ob))
838 AddAssocMember(caster,ob);
839}
840
841int AddMember(object ob) {
842
843 if (!Allowed(ob))
844 return 0;
845
846 // Wenn es einen TP gibt, unterliegt er einigen Einschraenkungen.
847 // Dafuer wird er aber via Aufnahme ins Team auch ggf. Teamleiter, wenn
848 // noetig.
849 // TODO: Dieser Code geht davon aus, dass er nur aus einem Kommando
850 // ausgefuehrt wird und TP der Veranlasser der Aufnahme ist. Dies scheint
851 // mir unrealistisch. (Zesstra)
852 if (TP && TP==previous_object()) {
853 if (!Allowed(TP))
854 return 0;
855 if (TP!=ob && CheckSecond(ob,({TP}))) {// Zweitiereglung bei Gruendung.
856 notify_fail(ob->Name(WER)+" ist Zweitspieler von Dir.\n");
857 return 0;
858 }
859 if (!IsMember(leader))
860 AddMemberAndAssocs(TP);
861 if (TP!=leader) {
862 notify_fail("Nur der Teamleiter kann Mitglieder aufnehmen.\n");
863 return 0;
864 }
865 }
866
867 AddMemberAndAssocs(ob);
868 return 1;
869}
870
871private void DoRemoveMember(object ob) {
872 object *tmembers,caster;
873 mixed asmem;
874 int i;
875
876 ob->SetProp(P_TEAM,0);
877 m_delete(is_member,ob);
878 m_delete(wanted_row,ob);
879 m_delete(act_row,ob);
880 m_delete(wimpy_row,ob);
881 m_delete(autofollow,ob);
882 m_delete(attack_cmd,ob);
883 if (objectp(caster=assoc_mem[ob]) && pointerp(asmem=assoc_mem[caster]))
884 assoc_mem[caster]=asmem-({ob,0});
885 if (query_once_interactive(ob)) ob->RemoveHpHook(ME);
886 m_delete(hp_info,ob);
887 m_delete(autoinf_hp,ob);
888 m_delete(autoinf_sp,ob);
889 DoChangeRow(ob,-1);
890
891 if (!objectp(assoc_mem[ob])) {
892 if (ob->QueryProp(P_GHOST)) {
893 gtell(upper_case(ob->name(WER))+" HAT DAS TEAM VERLASSEN.","Tod");
894 } else {
895 tell_object(ob,"Du verlaesst das Team.\n");
896 gtell(ob->Name(WER)+" hat das Team verlassen.\n");
897 }
898 }
899 m_delete(assoc_mem,ob);
900
901 if (IsMember(leader)) // Hat das Team noch einen Leiter?
902 return;
903 tmembers=Members();ob=0;
904 if (i=sizeof(tmembers)) {
905 ob=tmembers[0];
906 for (--i;i>=0;i--) {
907 if (interactive(tmembers[i])) {
908 ob=tmembers[i];
909 break;
910 }
911 if (query_once_interactive(tmembers[i]))
912 ob==tmembers[i];
913 }
914 DoChangeLeader(ob);
915 gtell(leader->Name(WER)+" hat die Teamleitung uebernommen.\n");
916 return;
917 }
918 TryRemove();
919}
920
921int RemoveAssocMember(object caster, object npc) {
MG Mud User88f12472016-06-24 23:31:02 +0200922 if (extern_call() && PO!=caster)
923 return 0;
924 if (!IsMember(caster) || assoc_mem[npc]!=caster)
925 return 0;
926 DoRemoveMember(npc);
927 return 1;
928}
929
930static void RemoveMemberAndAssocs(object caster) {
931 object *obs,ob;
932 int i;
933
934 if (pointerp(obs=caster->QueryProp(P_TEAM_ASSOC_MEMBERS))) {
935 for (i=sizeof(obs)-1;i>=0;i--)
936 if (objectp(ob=obs[i]))
937 RemoveAssocMember(caster,ob);
938 }
939 DoRemoveMember(caster);
940}
941
942private void RemoveSingles() {
Dominik Schäfer11488312017-07-29 16:40:00 +0200943 object *obs;
944 mixed aso;
945
946 if (!IsMember(leader)) return;
947 if (!query_once_interactive(leader)) return; // NPC Team
948 obs=Members()-({leader});
949 if (pointerp(aso=assoc_mem[leader]))
950 obs-=aso;
951 if (sizeof(obs)) return;
952 gtell("Das Team loest sich auf.\n");
953 RemoveMemberAndAssocs(leader);
954 TryRemove();
MG Mud User88f12472016-06-24 23:31:02 +0200955}
956
957int RemoveMember(mixed arg) {
958 object *mems,mem,ob;
959 int i;
960
961 if (objectp(arg)) {
962 ob=arg;
963 } else if (stringp(arg) && arg!="") {
964 mems=Members()-({leader});
965 for (i=sizeof(mems)-1;i>=0;i--) {
966 if (objectp(mem=mems[i]) && mem->id(arg)) {
967 ob=mem;
968 if (query_once_interactive(ob))
969 break;
970 }
971 }
972 if (!objectp(ob))
973 return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
974 } else {
975 return 0;
976 }
977 if (TP!=leader && TP!=ob && ob!=PO)
978 return notify_fail("Nur der Teamleiter kann Mitglieder entlassen.\n"),0;
979 if (!IsMember(ob) && ob->QueryProp(P_TEAM)!=ME)
980 return notify_fail(ob->Name(WER)+" ist kein Teammitglied.\n"),0;
981 if (PO!=ob && objectp(assoc_mem[ob]))
982 return notify_fail(ob->Name(WER)+" gehoert zu "+
983 assoc_mem[ob]->Name(WEM)+".\n"),0;
984
985 RemoveMemberAndAssocs(ob);
986 RemoveSingles();
987 return 1;
988}
989
990static int ChangeName(string str) {
991 if (leader && TP!=leader)
992 return notify_fail("Nur der Teamleiter kann den Namen aendern.\n"),0;
993 if (!str || str=="")
994 return 0;
995 str=str[0..19];
996 if (!stringp(str=(TEAM_MASTER->RegisterTeam(str))))
997 return notify_fail("Der Name ist schon vergeben.\n"),0;
998 tname=str;
999 if (objectp(TP))
1000 gtell(TP->Name(WER)+" aendert den Teamnamen auf \""+tname+"\".\n");
1001 return 1;
1002}
1003
1004void TeamInitAttack() {
1005 object *obs,*ens,*removed,ob,en;
1006 int i,j;
1007
1008 debug(sprintf("mis_init_att: %O\n",mis_init_att));
1009 ens=m_indices(mis_init_att);removed=({});
1010 for (i=sizeof(ens)-1;i>=0;i--) {
1011 if (!objectp(en=ens[i]))
1012 continue;
1013 if (!pointerp(obs=mis_init_att[en]))
1014 continue;
1015 for (j=sizeof(obs)-1;j>=0;j--)
1016 if (!IsMember(ob=obs[j]) || environment(ob)!=environment(en))
1017 obs[j]=0;
1018 obs-=({0});
1019 ob=en->SelectNearEnemy(obs,1);
1020 debug(sprintf("Begruessungsschlag von %O fuer %O\n",en,ob));
1021 if (!objectp(ob))
1022 continue;
1023 en->Attack2(ob); // Begruessungsschlag
1024 removed+=({en}); // Kein Begruessungsschlag mehr von diesem Monster
1025 }
1026
1027 for (i=sizeof(mis_attacked)-1;i>=0;i--)
1028 if (objectp(ob=mis_attacked[i]))
1029 ob->ExecuteMissingAttacks(removed);
1030 // Begruessungsschlaege die ausgefuehrt wurden entfernen
1031 // und nicht ausgefuehrte nachholen
1032
1033 mis_attacked=({});
1034 mis_init_att=([]);
1035 att_exec=({});
1036}
1037
1038int InitAttack_Callback(object enemy) {
1039 object *arr;
1040
1041 if (!IsMember(PO) || member(att_exec,PO)<0)
1042 return 0;
1043 if (!pointerp(arr=mis_init_att[enemy]))
1044 arr=({});
1045 mis_init_att[enemy]=arr+({PO});
1046 if (member(mis_attacked,PO)<0)
1047 mis_attacked+=({PO});
1048 return 1;
1049}
1050
1051void TeamAttackExecuted_Callback(int success) {
1052 if (!IsMember(PO))
1053 return;
1054 att_exec-=({PO,0});
1055 if (!sizeof(att_exec))
1056 TeamInitAttack();
1057}
1058
1059private int StartAttack() {
1060 object *tmembers,ob,env;
1061 int i;
1062
1063 if (TP!=leader || !objectp(env=environment(TP)))
1064 return notify_fail("Nur der Teamleiter kann den Angriff starten.\n"),0;
1065 tmembers=Members();
1066 TeamInitAttack(); // Falls noch Schlaege fehlen....
1067 for (i=sizeof(tmembers)-1;i>=0;i--)
1068 if (objectp(ob=tmembers[i]) && stringp(attack_cmd[ob]))
1069 if (ob->CallTeamAttack(env)) // Angriff wird ausgefuehrt?
1070 att_exec+=({ob}); // Liste der Angreifer
1071 gtell(TP->Name(WER)+" startet den Angriff.\n");
1072 return 1;
1073}
1074
1075void StartFollow(object env) {
1076 object *tmembers,ob;
1077 int i;
1078 string cmd,args;
1079
1080 // printf("ld:%O PO:%O TP:%O qv:%O env:%O\n",leader,PO,TP,query_verb(),env);
1081 if (TP!=leader
1082 || !stringp(cmd=query_verb())
1083 || !objectp(env)
1084 || TP!=PO
1085 || TP->QueryProp(P_GHOST) // Der Befehl war wohl ungesund...
1086 || TP->IsTeamMove()) // Angriffsbefehl nicht durch verfolge 2 mal...
1087 return;
1088 cmd="\\"+cmd;
1089 if (stringp(args=TP->_unparsed_args()) && args!="")
1090 cmd+=(" "+args);
1091 tmembers=Members()-({leader});
1092 for (i=sizeof(tmembers)-1;i>=0;i--)
1093 if (objectp(ob=tmembers[i]) && autofollow[ob])
1094 if(!query_once_interactive(ob) || env==environment(ob)) // autofolge nur bei anfuehrer im gleichen raum
1095 ob->CallTeamFollow(env,cmd);
1096}
1097
1098void ShowTeamInfo() {
1099 int i;
1100 object *tmembers,ob;
1101 string form;
1102
1103 if (!TI || TP!=TI || !IS_LEARNER(TI))
1104 return;
1105
1106 UpdateActRow();
1107 form="";
1108 for (i=0;i<MAX_TEAMROWS;i++)
1109 form+=sprintf(" %d-%d",formin[i],formax[i]);
1110 printf("%s [%O] L: %s F:%s\n",name(),ME,
1111 (objectp(leader)?leader->Name(WER):"?"),form);
1112 tmembers=Members();
1113 for (i=sizeof(tmembers)-1;i>=0;i--) {
1114 ob=tmembers[i];
1115 printf(" %d(%d) %s [%O]\n",
1116 act_row[ob],wanted_row[ob],ob->Name(WER),ob);
1117 }
1118}
1119
1120varargs int ShowTeamHP(string arg) {
1121 object *tmembers,ob;
1122 closure qp;
1123 int i,longinf;
1124 mixed *vals,*res,cols,fr,rr,termstr;
1125 mapping real_row;
1126 string nm,inf;
1127
1128 if (arg && arg[0..3]=="lang") {
1129 if (TP!=leader)
1130 return notify_fail("Nur der Teamleiter kann die lange "+
1131 "Uebersicht aufrufen.\n"),0;
1132 longinf=1;
1133 arg=arg[5..];
1134 } else
1135 longinf=0;
1136 real_row=PresentPositions(environment(TP));
1137 tmembers=({});
1138 for (i=0;i<MAX_TEAMROWS;i++)
1139 tmembers+=rows[i];
1140 res=({});
1141 switch(TP->QueryProp(P_TTY)) {
1142 case "ansi":
1143 termstr=({ANSI_RED+ANSI_BOLD,ANSI_YELLOW,ANSI_GREEN,ANSI_NORMAL,
1144 ANSI_UNDERL});
1145 break;
1146 case "vt100":
1147 termstr=({ANSI_BOLD,"","",ANSI_NORMAL,ANSI_UNDERL});
1148 break;
1149 default:
1150 termstr=({"","","","",""});
1151 }
1152 vals=({"",0,0,termstr[3],"",0,0,termstr[3]});
1153
1154 printf(" Name Gilde LV GLV LP (MLP) KP (MKP) Vors. GR AR TR FR A V\n");
1155 if (longinf)
1156 printf(" (Zeile 2) Angriffsbefehl Fluchtrichtung\n");
1157
1158 for (i=sizeof(tmembers)-1;i>=0;i--) {
1159 if (!objectp(ob=tmembers[i])) continue;
1160 qp=symbol_function("QueryProp",ob);
1161 fr=GetHpInfo(ob,qp);
1162 vals[1]=fr[0];vals[2]=fr[1];vals[5]=fr[2];vals[6]=fr[3];
1163 /*
1164 vals[1]=funcall(qp,P_HP);vals[2]=funcall(qp,P_MAX_HP);
1165 vals[5]=funcall(qp,P_SP);vals[6]=funcall(qp,P_MAX_SP);
1166 */
1167 if (!pointerp(cols=funcall(qp,P_TEAM_COLORS)) || sizeof(cols)<4)
1168 cols=({vals[2]/4,vals[2]/2,vals[6]/4,vals[6]/2});
1169 if (vals[1]<cols[0])
1170 vals[0]=termstr[0];
1171 else if (vals[1]<cols[1])
1172 vals[0]=termstr[1];
1173 else
1174 vals[0]=termstr[2];
1175 if (vals[5]<cols[2])
1176 vals[4]=termstr[0];
1177 else if (vals[5]<cols[3])
1178 vals[4]=termstr[1];
1179 else
1180 vals[4]=termstr[2];
1181 if (intp(fr=wimpy_row[ob]) && fr>1) fr=sprintf("%2d",fr); else fr="--";
1182 if (intp(rr=real_row[ob]) && rr>0) rr=sprintf("%2d",rr); else rr="--";
1183 nm=ob->Name(RAW);
1184 inf=sprintf("%s %-11s%s %-14s %3d %3d %s%3d (%3d)%s %s%3d (%3d)%s %5d %2d %2d %2s %2s %1s %1s\n",
1185 ((ob==leader)?(termstr[4]+"*"):" "),
1186 nm[0..10],termstr[3],
1187 capitalize(funcall(qp,P_GUILD)||"")[0..13],
1188 funcall(qp,P_LEVEL),
1189 funcall(qp,P_GUILD_LEVEL),
1190 vals[0],vals[1],vals[2],vals[3],
1191 vals[4],vals[5],vals[6],vals[7],
1192 funcall(qp,P_WIMPY),
1193 wanted_row[ob],act_row[ob],rr,fr,
1194 (attack_cmd[ob]?"X":"-"),
1195 (autofollow[ob]?"X":"-"));
1196 if (longinf) {
1197 if (!stringp(fr=funcall(qp,P_WIMPY_DIRECTION))) fr="";
1198 if (!stringp(rr=attack_cmd[ob])) rr="";
1199 if (fr!="" || rr!="")
1200 inf+=sprintf(" %-42s %-21s\n",rr[0..41],fr[0..20]);
1201 }
1202
1203 switch (arg) {
1204 case "alphabetisch":
1205 res+=({({(query_once_interactive(ob)?"0":"1")+nm,inf})});
1206 break;
1207 case "sortiert":
1208 res+=({({sprintf("%2s %2d %2d %s",rr,act_row[ob],wanted_row[ob],nm),
1209 inf})});
1210 break;
1211 default:
1212 res+=({({i,inf})});
1213 }
1214 }
1215 if (arg && arg!="")
1216 res=sort_array(res,"CmpFirstArrayElement",ME);
1217 for (i=sizeof(res)-1;i>=0;i--)
1218 write(res[i][1]);
1219 return 1;
1220}
1221
1222varargs int ShowTeamRooms(string arg) {
1223 object *tmembers,ob;
1224 string s1,s2;
1225 mixed *res;
1226 int i;
1227
1228 tmembers=Members();res=({});
1229 for (i=sizeof(tmembers)-1;i>=0;i--) {
1230 if (!objectp(ob=tmembers[i])) continue;
1231 if (!query_once_interactive(ob) && arg!="alle") continue;
1232 s1=ob->Name(RAW);
1233 if (!objectp(ob=environment(ob))) continue;
1234 if (!stringp(s2=ob->QueryProp(P_INT_SHORT))) s2="";
1235 res+=({({s1,s2})});
1236 }
1237 res=sort_array(res,"CmpFirstArrayElement",ME);
1238 for (i=sizeof(res)-1;i>=0;i--)
1239 printf("%-11s %-66s\n",res[i][0][0..10],res[i][1][0..65]);
1240 return 1;
1241}
1242
1243int ChangeColors(string arg) {
1244 int *col;
1245
1246 notify_fail("team farben lp_rot lp_gelb [kp_rot kp_gelb]\n");
1247 if (!arg)
1248 return 0;
1249 col=({0,0,0,0});
1250 if (sscanf(arg,"%d %d %d %d",col[0],col[1],col[2],col[3])!=4) {
1251 if (sscanf(arg,"%d %d",col[0],col[1])!=2)
1252 return 0;
1253 col[2]=col[0];col[3]=col[1];
1254 }
1255 printf("Die Anzeige ist jetzt gelb unter %d LP, rot unter %d LP\n"+
1256 " bzw. gelb unter %d KP, rot unter %d KP.\n",
1257 col[1],col[0],col[3],col[2]);
1258 TP->SetProp(P_TEAM_COLORS,col);
1259 TP->Set(P_TEAM_COLORS,SAVE,F_MODE_AS);
1260 return 1;
1261}
1262
1263int ChangeAutoInfo(string arg) {
1264 string *words,txt;
1265 int i,fl;
1266
Zesstrafb2614a2022-01-19 22:42:57 +01001267 // Eigentlich braeuchte man die nur fuer die SP-Benachrichtigungen, aber
1268 // dann wird alle viel komplizierter, weil man dann bei Aenderungen und
1269 // Teamleiterwechseln pruefen muss etc.
1270 if (!(TP->QueryProp(P_CAN_FLAGS) & CAN_REPORT_SP)) {
1271 TP->ReceiveMsg("Du musst zunaechst die Quest \"Hilf den "
1272 "Gnarfen\" bestehen, um diese Funktion nutzen zu koennen.",
1273 MT_NOTIFICATION|MSG_DONT_IGNORE);
1274 return 1;
1275 }
MG Mud User88f12472016-06-24 23:31:02 +02001276 if (TP!=leader)
Zesstrafb2614a2022-01-19 22:42:57 +01001277 {
1278 switch(arg)
1279 {
1280 case "an":
1281 case "ein":
1282 if (!(TP in autoinf_subscribers))
1283 autoinf_subscribers += ({TP});
1284 TP->ReceiveMsg("Du erhaelst eine Kopie des Autoinform-Berichtes "
1285 "an den Teamleiter, falls bzw. sobald dieser einen solchen "
1286 "konfiguriert hat.",MT_NOTIFICATION|MSG_DONT_IGNORE);
1287 break;
1288
1289 case "aus":
1290 autoinf_subscribers -= ({TP});
1291 TP->ReceiveMsg("Du erhaelst keine Kopie des Autoinform-Berichtes "
1292 "an den Teamleiter.",MT_NOTIFICATION|MSG_DONT_IGNORE);
1293 break;
1294
1295 default:
1296 notify_fail("Nur der Teamleiter kann den Inhalt des "
1297 "Autoinform-Berichts konfigurieren.\n");
1298 return 0;
1299 }
1300 return 1;
1301 }
1302 //else TP == leader
MG Mud User88f12472016-06-24 23:31:02 +02001303 words=old_explode(arg?arg:""," ");fl=0;
Zesstrafb2614a2022-01-19 22:42:57 +01001304 for (i=sizeof(words)-1;i>=0;i--)
1305 {
MG Mud User88f12472016-06-24 23:31:02 +02001306 switch(words[i]) {
1307 case "aus":
Zesstrafb2614a2022-01-19 22:42:57 +01001308 TP->ReceiveMsg(
1309 "Du und das Team werden nicht mehr automatisch informiert.",
1310 MT_NOTIFICATION|MSG_DONT_IGNORE);
MG Mud User88f12472016-06-24 23:31:02 +02001311 autoinf_flags=0;
Zesstrafb2614a2022-01-19 22:42:57 +01001312 autoinf_subscribers -= ({TP});
MG Mud User88f12472016-06-24 23:31:02 +02001313 return 1;
1314 case "+kp":
1315 fl|=AUTOINF_SP_PLUS;
1316 case "kp":
1317 fl|=AUTOINF_SP_MINUS;
1318 break;
1319 case "+lp":
1320 fl|=AUTOINF_HP_PLUS;
1321 case "lp":
1322 case "ein":
1323 case "an":
1324 fl|=AUTOINF_HP_MINUS;
1325 break;
1326 case "sofort":
1327 fl|=AUTOINF_INSTANT;
1328 break;
MG Mud User88f12472016-06-24 23:31:02 +02001329 }
1330 }
Zesstrafb2614a2022-01-19 22:42:57 +01001331 if (!fl) {
1332 notify_fail("WIE moechtest Du automatisch informiert werden?\n");
1333 return 0;
1334 }
MG Mud User88f12472016-06-24 23:31:02 +02001335 if (fl==AUTOINF_INSTANT) fl|=AUTOINF_HP_MINUS;
1336 autoinf_flags=fl;
Zesstrafb2614a2022-01-19 22:42:57 +01001337 if (!(TP in autoinf_subscribers))
1338 autoinf_subscribers += ({TP});
MG Mud User88f12472016-06-24 23:31:02 +02001339 txt="Du wirst"+((fl&AUTOINF_INSTANT)?" sofort ":" ")+"informiert, wenn";
1340 if (fl&(AUTOINF_HP_PLUS|AUTOINF_HP_MINUS)) {
1341 txt+=" die Lebenspunkte eines Teammitglieds";
1342 if (fl&(AUTOINF_HP_PLUS)) txt+=" sich aendern"; else txt+=" fallen";
1343 if (fl&(AUTOINF_SP_PLUS|AUTOINF_SP_MINUS)) txt+=" oder";
1344 }
1345 if (fl&(AUTOINF_SP_PLUS|AUTOINF_SP_MINUS)) {
1346 txt+=" die Konzentrationspunkte";
1347 if (fl&(AUTOINF_SP_PLUS)) txt+=" sich aendern"; else txt+=" fallen";
1348 }
Zesstrafb2614a2022-01-19 22:42:57 +01001349 TP->ReceiveMsg(txt+".", MT_NOTIFICATION|MSG_DONT_IGNORE);
MG Mud User88f12472016-06-24 23:31:02 +02001350 return 1;
1351}
1352
1353private void DoNotifyHpChange() {
1354 object *obs,ob;
1355 string str;
1356 int i;
1357
1358 autoinf_time=time();
1359 str="";
1360 obs=m_indices(autoinf_hp);
1361 for (i=sizeof(obs)-1;i>=0;i--) {
1362 if (!objectp(ob=obs[i])) continue;
1363 if (str!="") str+=", ";
1364 str+=sprintf("%s: %d LP",ob->name(WER),autoinf_hp[ob]);
1365 if (member(autoinf_sp,ob))
1366 str+=sprintf(" / %d KP",autoinf_sp[ob]);
1367 m_delete(autoinf_sp,ob);
1368 }
1369 obs=m_indices(autoinf_sp);
1370 for (i=sizeof(obs)-1;i>=0;i--) {
1371 if (!objectp(ob=obs[i])) continue;
1372 if (str!="") str+=", ";
1373 str+=sprintf("%s: %d KP",ob->name(WER),autoinf_sp[ob]);
1374 }
MG Mud User88f12472016-06-24 23:31:02 +02001375 autoinf_hp=([]);
1376 autoinf_sp=([]);
Zesstrafb2614a2022-01-19 22:42:57 +01001377 str = capitalize(str);
1378 // Und an alle Subscriber zustellen.
1379 foreach(object pl: autoinf_subscribers)
1380 {
1381 if (pl)
1382 pl->ReceiveMsg(str, MT_NOTIFICATION|MSG_DONT_IGNORE);
1383 else
1384 autoinf_subscribers -= ({0}); // bereinigen
1385 }
MG Mud User88f12472016-06-24 23:31:02 +02001386}
1387
1388void NotifyHpChange() {
1389 mixed act,old;
1390 int change;
1391
1392 if (!IsMember(PO) || !pointerp(act=hp_info[PO]))
1393 return;
1394 old=act[0..];
1395 act=GetHpInfo(PO);change=0;
1396 if (!autoinf_flags) return;
1397 if (((autoinf_flags&AUTOINF_HP_MINUS) && act[0]<old[0]) ||
1398 ((autoinf_flags&AUTOINF_HP_PLUS) && act[0]>old[0]))
1399 autoinf_hp[PO]=act[0],change=1;
1400 if (((autoinf_flags&AUTOINF_SP_MINUS) && act[2]<old[2]) ||
1401 ((autoinf_flags&AUTOINF_SP_PLUS) && act[2]>old[2]))
1402 autoinf_sp[PO]=act[2],change=1;
1403
1404 if (autoinf_time<time() || (change && (autoinf_flags&AUTOINF_INSTANT)))
1405 DoNotifyHpChange();
1406}
1407
1408int TeamCmd(string cmd, string arg) {
1409 if (!IsMember(TP)) {
1410 notify_fail("Du bist kein Teammitglied.\n");
1411 if (TP->QueryProp(P_TEAM)==ME)
1412 TP->SetProp(P_TEAM,0);
1413 return 0;
1414 }
1415 switch(cmd) {
1416 case "angriff":
1417 return StartAttack();
1418 case "angriffsbefehl":
1419 if (stringp(arg))
1420 attack_cmd[TP]=arg;
1421 else
1422 m_delete(attack_cmd,arg);
1423 return 1;
1424 case "autofolge":
1425 case "autof":
1426 if (arg=="ein" || arg=="an")
1427 autofollow[TP]=1;
1428 else
1429 m_delete(autofollow,TP);
1430 return 1;
1431 case "autoi":
1432 case "autoinf":
1433 case "autoinfo":
1434 case "autoinform":
1435 return ChangeAutoInfo(arg);
1436 case "entlasse":
1437 return RemoveMember(arg);
1438 case "farben":
1439 return ChangeColors(arg);
1440 case "fluchtreihe":
1441 case "flucht":
1442 return ChangeWimpyRow(arg);
1443 case "formation":
1444 if (TP!=leader)
1445 return notify_fail("Nur der Teamleiter kann die Formation aendern.\n"),0;
1446 return ChangeFormation(arg);
1447 case "hist":
1448 case "history":
1449 return Hist(arg);
1450 case "":
1451 case "info":
1452 return ShowTeamHP(arg);
1453 case "leiter":
1454 case "leiterin":
1455 case "leitung":
1456 return ChangeLeader(arg);
1457 case "name":
1458 return ChangeName(arg);
1459 case "orte":
1460 return ShowTeamRooms(arg);
1461 case "kampfreihe":
1462 case "reihe":
1463 return ChangeRow(arg);
1464 case "ruf":
1465 case "rufe":
1466 return Shout(arg);
1467 case "verlasse":
1468 TP->SetProp(P_TEAM,0);
1469 return RemoveMember(TP);
1470 default:;
1471 }
1472 return 0;
1473}
1474
1475void reset() {
1476 RemoveSingles();
1477 TryRemove();
1478}