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