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