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