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