Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/obj/team.c b/obj/team.c
new file mode 100644
index 0000000..0319f5e
--- /dev/null
+++ b/obj/team.c
@@ -0,0 +1,1432 @@
+#pragma strong_types,save_types
+#pragma no_shadow
+// TODO: Morgoth erbt das Teamobjekt: klaeren warum und wieso eigentlich.
+//#pragma no_inherit
+#pragma pedantic
+
+#include <living/team.h>
+#include <properties.h>
+#include <language.h>
+#include <new_skills.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#define ME this_object()
+#define PO previous_object()
+#define TP this_player()
+#define TI this_interactive()
+
+#define AUTOINF_HP_MINUS 0x01
+#define AUTOINF_HP_PLUS 0x02
+#define AUTOINF_SP_MINUS 0x04
+#define AUTOINF_SP_PLUS 0x08
+#define AUTOINF_INSTANT 0x10
+
+private nosave mapping is_member; // Teammitglieder
+private nosave object leader; // Teamleiter
+private nosave string tname; // Teamname
+private nosave mapping wanted_row; // Gewuenschte Reihe
+private nosave mapping wimpy_row; // Fluchtreihe
+private nosave mapping act_row; // Aktuelle Reihe
+private nosave mapping autofollow; // Spieler folgt Teamleiter
+private nosave mapping attack_cmd; // Spieler hat Angriffsbefehl
+private nosave mapping assoc_mem; // Zugeordnete Mitglieder (Kampf NPCs)
+private nosave int *formin,*formax; // Formation
+private nosave mixed *rows; // Die Reihen
+private nosave int last_reorder; // Letzte Formationspruefung
+private nosave mapping h; // Sortier-Score: 125*G.Reihe-HP
+private nosave object *att_exec; // Mitglieder, die Attacke ausfuehren.
+private nosave object *mis_attacked;// (Ex-)Mitglieder ohne Begruessungsschlag
+private nosave mapping mis_init_att;// Fehlende Begruessungsschlaege
+private nosave mapping hp_info; // HP, MAX_HP, SP, MAX_SP
+private nosave int autoinf_flags;
+private nosave mapping autoinf_hp;
+private nosave mapping autoinf_sp;
+private nosave int autoinf_time;
+private nosave string *hist;
+
+private nosave object debugger;
+
+void _set_debug() {
+ if (!debugger && IS_LEARNER(TI))
+ debugger=TI;
+ else if (debugger==TI)
+ debugger=0;
+}
+
+private void debug(string str) {
+ if (objectp(debugger) && stringp(str))
+ tell_object(debugger,"#TEAM: "+str);
+}
+
+void create() {
+ autoinf_flags=0;
+ autoinf_sp=([]);
+ autoinf_hp=([]);
+ is_member=([]);
+ leader=0;
+ wanted_row=([]);
+ act_row=([]);
+ wimpy_row=([]);
+ autofollow=([]);
+ attack_cmd=([]);
+ assoc_mem=([]);
+ formin=({1,0,0,0,0});
+ formax=({5,4,3,2,1});
+ rows=EMPTY_TEAMARRAY;
+ h=([]);
+ att_exec=({});
+ mis_init_att=([]);
+ mis_attacked=({});
+ hp_info=([]);
+ hist=({});
+ if (object_name(ME)==TEAM_OBJECT)
+ return;
+ if (!stringp(tname=TEAM_MASTER->RegisterTeam()))
+ tname="?";
+}
+
+object *Members() {
+ return (m_indices(is_member)-({0}));
+}
+
+object Leader() {
+ return leader;
+}
+
+varargs string name(int casus, int demon) {
+ if (!stringp(tname))
+ return "Team ?";
+ return "Team "+capitalize(tname);
+}
+
+varargs string Name(int casus, int demon) {
+ return name(casus,demon);
+}
+
+varargs int remove(int silent) {
+ if (mappingp(is_member) && sizeof(Members())) // Nur leere Teams removen
+ return 0;
+ TEAM_MASTER->UnregisterTeam(); // Teamnamen freigeben.
+ destruct(ME);
+ return 1;
+}
+
+private void TryRemove() {
+ if (clonep(ME)
+ && (!mappingp(is_member) || !sizeof(Members()))
+ && !first_inventory(ME)
+ && find_call_out("remove")<0)
+ call_out("remove",0);
+}
+
+int CmpFirstArrayElement(mixed *a, mixed *b) {
+ return(a[0]<b[0]);
+}
+
+varargs private void gtell(string str, string who, int tohist) {
+ int i;
+ object *tmembers,rochus;
+ string prefix,msg;
+
+ tmembers=Members();
+ prefix=sprintf("[%s:%s] ",name(),stringp(who)?who:"");
+ msg=break_string(str,78,prefix);
+ for (i=sizeof(tmembers)-1;i>=0;i--)
+ tell_object(tmembers[i],msg);
+ if (objectp(rochus=find_player("rochus"))
+ && rochus->QueryProp("debug_team"))
+ tell_object(rochus,msg);
+ if (tohist)
+ hist=(hist+({break_string(str+" <"+ctime()[11..15]+">",78,prefix)}))[-100..];
+}
+
+int IsMember(object ob) {
+ return (objectp(ob) && is_member[ob]);
+}
+
+int IsInteractiveMember(object ob) {
+ return (objectp(ob) && is_member[ob] && query_once_interactive(ob));
+}
+
+varargs private int *GetHpInfo(object ob, closure cl) {
+ int *res;
+
+ if (!closurep(cl)) cl=symbol_function("QueryProp",ob);
+ if (!pointerp(res=hp_info[ob]) || sizeof(res)<4)
+ res=({0,funcall(cl,P_MAX_HP),0,funcall(cl,P_MAX_SP)});
+ res[0]=funcall(cl,P_HP);
+ res[2]=funcall(cl,P_SP);
+ return hp_info[ob]=res;
+}
+
+int CompareHp(object a, object b) {
+ return h[a]>h[b];
+}
+
+// Aktualisiert act_row (->wer steht in welcher Reihe).
+private void UpdateActRow() {
+ int i,j,update_hp;
+ object *new;
+ mixed aso;
+
+ act_row=([]);
+ rows[0]+=Members();
+ update_hp=0;
+ if (!mappingp(h)) {
+ h=([]);
+ update_hp=1;
+ }
+ for (i=MAX_TEAMROWS-1;i>=0;i--) {
+ new=({});
+ foreach(object ob: rows[i]) {
+ if (objectp(ob) && is_member[ob] && !act_row[ob]) {
+ act_row[ob]=i+1;
+ new+=({ob});
+ if (update_hp) {
+ if (!objectp(aso=assoc_mem[ob]) || environment(aso)!=environment(ob))
+ aso=ob;
+ h[ob]=
+ 1000*wanted_row[aso]
+ +40*act_row[aso]
+ -(query_once_interactive(aso)?8:2)*aso->QueryProp(P_HP)
+ -query_once_interactive(ob);
+ // NPCs bekommen fast gleichen Wert wie Caster,
+ // im Zweifelsfalle steht der Caster weiter vorne...
+ }
+ }
+ }
+ rows[i]=sort_array(new,"CompareHp",ME);
+ }
+}
+
+private void CheckFormation() {
+ int i,mincap,maxcap,d,num;
+
+ mincap=maxcap=0;
+ if (formin[0]<1)
+ formin[0]=1;
+
+ // erstmal die Wuensche normalisieren/korrigieren auf sinnvolle Werte.
+ for (i=0;i<MAX_TEAMROWS;i++) {
+ if (formin[i]<0) formin[i]=0;
+ if (formax[i]<formin[i]) formax[i]=formin[i];
+ if (formax[i]>MAX_TEAM_ROWLEN) formax[i]=MAX_TEAM_ROWLEN;
+ if (formin[i]>formax[i]) formin[i]=formax[i];
+ mincap+=formin[i]; // Summe der min. je Reihe gewuenschten.
+ maxcap+=formax[i]; // Summe der max. je Reihe gewuenschten.
+ }
+
+ num=sizeof(Members());
+
+ // max. gewuenschte Reihenlaenge verlaengern, wenn die Summe der maximal je
+ // Reihe gewuenschten kleiner als die Anzahl der Spieler ist. Von vorne
+ // natuerlich.
+ d=num-maxcap;
+ for (i=0;i<MAX_TEAMROWS;i++) {
+ if (d<=0)
+ break;
+ d-=(MAX_TEAM_ROWLEN-formax[i]);
+ formax[i]=MAX_TEAM_ROWLEN;
+ if (d<0)
+ formax[i]+=d; // doch noch was uebrig, wieder anhaengen.
+ }
+ // min. gewuenschte Reihenlaenge auf 0 verkuerzen, wenn die Summe der
+ // minimal je Reihe gewuenschten groesser als die Anzahl der Spieler ist.
+ // Von hinten natuerlich.
+ d=mincap-num; //
+ for (i=MAX_TEAMROWS-1;i>=0;i--) {
+ if (d<=0)
+ break;
+ d-=formin[i];
+ formin[i]=0;
+ if (d<0)
+ formin[i]-=d; // doch noch was uebrig, wieder anhaengen.
+ }
+}
+
+private void MakeFormation() {
+ // Verlegungsstrategie:
+ // Richtung Test Verschieben
+ // 1. -----> a) MAX <- X
+ // b) MAX X ->
+ // c) MIN X <- <- <- <-
+ // 2. <----- a) MIN -> -> -> -> X
+ // b) MAX <- X
+ int i,j,d;
+
+ last_reorder=time();
+ UpdateActRow();
+ CheckFormation();
+ for (i=0;i<MAX_TEAMROWS;i++) {
+ d=sizeof(rows[i]);
+ if (d<formin[i] || d>formax[i])
+ break;
+ }
+ if (i>=MAX_TEAMROWS)
+ return; // Formation ist noch in Ordnung
+
+ for (i=0;i<MAX_TEAMROWS;i++) {
+ if (sizeof(rows[i])>formax[i]) { // Reihe ist zu voll
+ if (i>0) {
+ d=formax[i-1]-sizeof(rows[i-1]);
+ if (d>0) { // Reihe vorher hat d freie Plaetze
+ rows[i-1]+=rows[i][0..(d-1)]; // Also d Mitglieder abgeben
+ rows[i]=rows[i][d..];
+ }
+ }
+ if (i<MAX_TEAMROWS-1 && sizeof(rows[i])>formax[i]) {// Immer noch zu voll
+ rows[i+1]=rows[i][formax[i]..]+rows[i+1]; // Rest nach hinten.
+ rows[i]=rows[i][0..(formax[i]-1)];
+ }
+ continue; // War zu voll, kann nicht zu leer sein
+ }
+ for (j=i+1;j<MAX_TEAMROWS;j++) {
+ d=formin[i]-sizeof(rows[i]);
+ if (d<=0) // Ausreichende Anzahl
+ break; // kein weiteres j noetig
+ rows[i]+=rows[j][0..(d-1)]; // Sonst Nachschub von hinten holen
+ rows[j]=rows[j][d..];
+ }
+ }
+ for (i=MAX_TEAMROWS-1;i>0;i--) {
+ for (j=i-1;j>=0;j--) {
+ d=formin[i]-sizeof(rows[i]);
+ if (d<=0) // Ausreichende Anzahl
+ break; // kein weiteres j noetig
+ rows[i]+=rows[j][0..(d-1)]; // Sonst Nachschub von vorne holen
+ rows[j]=rows[j][d..];
+ }
+ d=sizeof(rows[i])-formax[i];
+ if (d>0) {
+ rows[i-1]+=rows[i][0..(d-1)]; // Ueberschuss nach vorne schieben
+ rows[i]=rows[i][d..];
+ }
+ }
+ UpdateActRow();
+}
+
+private void RemoveFromRow(object ob, int src) {
+ if (src<0 || src>=MAX_TEAMROWS)
+ return;
+ rows[src]-=({ob});
+ if (sizeof(rows[src])>=formin[src])
+ return;
+ // Falls hinten noch Ueberschuss da ist, her damit.
+ if (src<MAX_TEAMROWS-1 && sizeof(rows[src+1])-1>=formin[src+1]) {
+ rows[src]+=rows[src+1][0..0];
+ rows[src+1]=rows[src+1][1..];
+ return;
+ }
+ // Falls vorne noch Ueberschuss da ist, her damit.
+ if (src>0 && sizeof(rows[src-1])-1>=formin[src-1]) {
+ rows[src]=rows[src-1][<1..]+rows[src];
+ rows[src-1]=rows[src-1][0..<2];
+ }
+}
+
+private void AddToRow(object ob, int dest) {
+ if (dest<0 || dest>=MAX_TEAMROWS)
+ return;
+ rows[dest]+=({ob});
+ if (sizeof(rows[dest])<=formax[dest])
+ return;
+ // Falls vorne noch jemand hin kann, dorthin
+ if (dest>0 && sizeof(rows[dest-1])+1<=formax[dest-1]) {
+ rows[dest-1]+=rows[dest][0..];
+ rows[dest]=rows[dest][1..];
+ return;
+ }
+ // Falls hinten noch jemand hin kann, dorthin
+ if (dest<MAX_TEAMROWS-1 && sizeof(rows[dest+1])+1<=formax[dest+1]) {
+ // Dest: ({... <3, <2, ob});
+ rows[dest+1]=rows[dest][<2..<2]+rows[dest+1];
+ rows[dest]=rows[dest][0..<3]+({ob});
+ }
+}
+
+private void CycleRows(object ob, int src, int dest) {
+ int i;
+
+ if (src<0 || src>=MAX_TEAMROWS || dest<0 || dest>=MAX_TEAMROWS)
+ return;
+ rows[src]-=({ob});
+ if (sizeof(rows[src])<formin[src] || sizeof(rows[dest])>=formax[dest]) {
+ if (src<dest) { // (<- -X) <- <- <- <- (+X <-)
+ for (i=src+1;i<=dest;i++) {
+ rows[i-1]+=rows[i][0..0];
+ rows[i]=rows[i][1..];
+ }
+ } else if (src>dest) { // (-> +X) -> -> -> -> (-X ->)
+ for (i=src-1;i>=dest;i--) {
+ rows[i+1]=rows[i][<1..]+rows[i+1];
+ rows[i]=rows[i][0..<2];
+ }
+ }
+ }
+ if (src<=dest)
+ rows[dest]+=({ob});
+ else
+ rows[dest]=({ob})+rows[dest];
+}
+
+// tauscht zufaellig aus den ersten 4 Reihen aus einer Reihe den letzten mit
+// dem ersten aus der Folgereihe, wenn die der Score des vorderen Spieler mehr
+// als 10 groesser als der des hinteren Spielers ist.
+private void RandomChangeRow() {
+ int *nums,i;
+ object p1,p2;
+
+ if (!mappingp(h))
+ UpdateActRow();
+ for (nums=({0,1,2,3});sizeof(nums);nums-=({i})) {
+ i=nums[random(sizeof(nums))];
+ if (!sizeof(rows[i]) || !sizeof(rows[i+1])) continue;
+ if (!objectp(p1=rows[i][<1]) || !objectp(p2=rows[i+1][0])) continue;
+ if (wanted_row[p1]<wanted_row[p2]) continue;
+ if (h[p2]-h[p1]>=-10) continue;
+ rows[i][<1]=p2;
+ rows[i+1][0]=p1;
+ return;
+ }
+}
+
+varargs private string
+CountUpNames(object *obs, string zsing, string zplur) {
+ string res;
+ object ob;
+ int i,sz;
+
+ res="";
+ if (!pointerp(obs)) return res;
+ if (!stringp(zsing)) zsing="";
+ if (!stringp(zplur)) zplur="";
+ if (sz=sizeof(obs)) {
+ for (i=0;i<sz;i++) {
+ if (i)
+ res+=((i<sz-1)?", ":" und ");
+ if (objectp(ob=obs[i]))
+ res+=ob->name(WER);
+ }
+ if (sz>1)
+ res+=zplur;
+ else
+ res+=zsing;
+ }
+ return res;
+}
+
+static void DoChangeRow(object pl, int dest) {
+ mapping old_row,ec1,ec2,ecb;
+ int i;
+ object *obs,*envs,env;
+ string *msg,str;
+
+ dest--;
+ CheckFormation();
+ h=0; // damit HP-Liste geupdated wird.
+ UpdateActRow();
+ old_row=deep_copy(act_row);
+
+ // welche Objekte bewegen?
+ obs=({});
+ if (objectp(pl)) {
+ obs=({pl});
+ if (pointerp(assoc_mem[pl]))
+ obs+=assoc_mem[pl];
+ } else {
+ RandomChangeRow();
+ }
+
+ foreach (object ob:obs) {
+ if (!objectp(ob))
+ continue;
+ // Alle assoziierten NPC kriegen die gleiche gewuenschte Reihe wie der
+ // Spieler.
+ wanted_row[ob]=wanted_row[pl];
+ UpdateActRow();
+ // und dann in die gew. Reihe stopfen.
+ int src=act_row[ob]-1;
+ if (dest<0 || dest>=MAX_TEAMROWS)
+ RemoveFromRow(ob,src);
+ else if (src<0 || src>=MAX_TEAMROWS)
+ AddToRow(ob,dest);
+ else if (src!=dest)
+ CycleRows(ob,src,dest);
+ }
+
+ MakeFormation();
+
+ obs = Members(); // alle Members beruecksichtigen beim Abgleich!
+ object *changed = allocate(0);
+ foreach (object ob:obs)
+ if (objectp(ob) && old_row[ob]!=act_row[ob]) {
+ ob->InformRowChange(old_row[ob],act_row[ob]);
+ changed += ({ob});
+ }
+
+ // Ab jetzt nur noch Ausgabe.
+ if (get_eval_cost()<800000) return; // War schon teuer genug, Lagvermeidung
+ msg=({});ec1=([]);ec2=([]);ecb=([]);
+ foreach (object ob:changed) {
+ tell_object(ob,sprintf("Du bist jetzt in Reihe %d.\n",act_row[ob]));
+ msg+=({sprintf("%s->%d",ob->Name(WER),act_row[ob])});
+ if (query_once_interactive(ob) && !interactive(ob)) continue;
+ if (!objectp(env=environment(ob))) continue;
+ if (old_row[ob]<=1) {
+ if (!pointerp(envs=ec1[env])) envs=({});
+ ec1[env]=envs+({ob});ecb[env]|=1;
+ }
+ if (act_row[ob]<=1) {
+ if (!pointerp(envs=ec2[env])) envs=({});
+ ec2[env]=envs+({ob});ecb[env]|=2;
+ }
+ }
+ if (sizeof(msg))
+ gtell(implode(msg,", ")); // Ausgabe an alle Gruppenmitglieder.
+ m_delete(ecb,find_object("/room/netztot")); // Das gaebe Mega-Lag :-)
+ envs=m_indices(ecb);obs=Members();
+ for (i=sizeof(envs)-1;i>=0;i--) {
+ if (!objectp(env=envs[i])) continue;
+ str="";
+ str+=CountUpNames(ec1[env]," tritt zurueck"," treten zurueck");
+ if (ecb[env]==3) str+=", ";
+ str+=CountUpNames(ec2[env]," tritt vor"," treten vor");
+ str+=".\n";
+ tell_room(env,capitalize(break_string(str,78)),obs);
+ }
+}
+
+void UpdateFormation() {
+ DoChangeRow(0,0);
+}
+
+int SwapRows(object ob1, object ob2) {
+ int i,r1,r2,p1,p2;
+
+ if (!objectp(ob1) || !objectp(ob2) || ob1==ob2)
+ return 0;
+ r1=r2=-1;
+ for (i=0;i<MAX_TEAMROWS;i++) {
+ if (r1==-1 && (p1=member(rows[i],ob1))>=0)
+ r1=i;
+ if (r2==-1 && (p2=member(rows[i],ob2))>=0)
+ r2=i;
+ }
+ if (r1==-1 || r2==-1)
+ return 0;
+ if (r1==r2)
+ return 1;
+ if (r1<r2) { // Nicht Monster vor Spieler stellen
+ if (query_once_interactive(ob1) && !interactive(ob2))
+ return 0;
+ } else {
+ if (query_once_interactive(ob2) && !interactive(ob1))
+ return 0;
+ }
+ rows[r1][p1]=ob2;ob2->InformRowChange(r2,r1);
+ rows[r2][p2]=ob1;ob1->InformRowChange(r1,r2);
+ gtell(ob1->Name(WER)+" und "+ob2->name(WER)+" tauschen die Plaetze.\n");
+ return 1;
+}
+
+private int ChangeRow(string arg) {
+ int num;
+
+ if (!arg || sscanf(arg,"%d",num)!=1)
+ return notify_fail("In welche Reihe willst Du denn wechseln?\n"),0;
+ if (num<1 || num>MAX_TEAMROWS)
+ return notify_fail("Die Reihenangabe ist ungueltig.\n"),0;
+ TP->SetProp(P_TEAM_WANTED_ROW,wanted_row[TP]=num);
+ printf("Du versuchst in Reihe %d zu wechseln.\n",num);
+ DoChangeRow(TP,num);
+ return 1;
+}
+
+private int ChangeWimpyRow(string arg) {
+ int num;
+
+ if (!arg || sscanf(arg,"%d",num)!=1)
+ return notify_fail("In welche Reihe willst Du fliehen?\n"),0;
+ if (num<0 || num>MAX_TEAMROWS)
+ return notify_fail("Die Reihenangabe ist ungueltig.\n"),0;
+ TP->SetProp(P_TEAM_WIMPY_ROW,wimpy_row[TP]=num);
+ if (num>1)
+ printf("Bei der Flucht wirst Du in Reihe %d wechseln.\n",num);
+ else
+ write("Bei der Flucht wirst Du den Raum verlassen.\n");
+ return 1;
+}
+
+mixed *PresentRows(object env) {
+ int i,j,d,arbeit;
+ mixed *res;
+ object *nd,ob;
+
+ if (!objectp(env))
+ env=environment(TP);
+ if (last_reorder!=time() || !mappingp(h))
+ UpdateFormation();
+ res=EMPTY_TEAMARRAY;arbeit=0;nd=({});
+ for (i=0;i<MAX_TEAMROWS;i++) {
+ object *new=({});
+ foreach(ob: rows[i]) {
+ if (objectp(ob) && is_member[ob] && environment(ob)==env) {
+ if (query_once_interactive(ob) && !interactive(ob)) {
+ nd+=({ob});
+ arbeit=1;
+ } else {
+ new+=({ob});
+ }
+ } else {
+ arbeit=1;
+ }
+ }
+ res[i]=new;
+ }
+ if (!arbeit)
+ return res;
+ for (i=j=0;i<MAX_TEAMROWS;i++) {
+ if (j<=i) j=i+1;
+ for (;j<MAX_TEAMROWS;j++) {
+ d=formin[i]-sizeof(res[i]);
+ if (d<=0) // Ausreichende Anzahl
+ break; // kein weiteres j noetig
+ res[i]+=res[j][0..(d-1)]; // Sonst Nachschub von hinten holen
+ res[j]=res[j][d..];
+ }
+ }
+ res[MAX_TEAMROWS-1]+=nd; // Netztote bieten keine Deckung, nach hinten.
+ return res;
+}
+
+mapping PresentPositions(mixed pres_rows) {
+ mapping res=([]);
+ if (objectp(pres_rows))
+ pres_rows=PresentRows(pres_rows);
+ if (!pointerp(pres_rows))
+ return res;
+ for (int i=0;i<MAX_TEAMROWS;i++) {
+ foreach(object ob: pres_rows[i])
+ if (ob)
+ res[ob]=i+1;
+ }
+ return res;
+}
+
+
+varargs int FleeToRow(object ob) {
+ int num;
+
+ if (!objectp(ob))
+ ob=TP;
+ if (!IsMember(ob))
+ return 0;
+ h=0; // Reihen bei naechster Abfrage neu sortieren
+ num=wimpy_row[ob];
+ if (num<2 || num>MAX_TEAMROWS) // Flucht in 1. Reihe nicht sinnvoll.
+ return 0;
+ if (num==wanted_row[ob]) // Ist schonmal nach hinten geflohen.
+ return 0;
+ tell_object(ob,sprintf("Du versuchst in Reihe %d zu fliehen.\n",num));
+ ob->SetProp(P_TEAM_WANTED_ROW,wanted_row[ob]=num);
+ DoChangeRow(ob,num);
+ if (PresentPositions(environment(ob))[ob]<=1) // Flucht gescheitert?
+ return 0;
+ return 1;
+}
+
+static int ChangeFormation(string arg) {
+ string *words;
+ int i,min,max;
+ mapping old_row;
+
+ if (arg=="aus")
+ arg="1-6 0-6 0-6 0-6 0-6";
+ i=sizeof(words=old_explode(arg," "));
+ if (i>MAX_TEAMROWS)
+ i=MAX_TEAMROWS;
+ for (--i;i>=0;i--) {
+ if (sscanf(words[i],"%d-%d",min,max)==2) {
+ formin[i]=min;
+ formax[i]=max;
+ } else if (sscanf(words[i],"%d",min)==1) {
+ formin[i]=formax[i]=min;
+ }
+ }
+
+ UpdateFormation();
+
+ words=({});
+ for (i=0;i<MAX_TEAMROWS;i++)
+ words+=({sprintf("%d-%d",formin[i],formax[i])});
+ gtell("Die Formation ist jetzt "+implode(words," / ")+".\n");
+ return 1;
+}
+
+int Shout(string str) {
+ if (!str || str=="")
+ return notify_fail("Was willst Du den anderen Teammitgliedern sagen?\n"),0;
+ gtell(str,TP->Name(WER),1);
+ return 1;
+}
+
+int Hist(string str) {
+ int i,anz,maximal;
+
+ // non-interactive oder Nicht-Mitglieder sollten die Hist nicht abfragen.
+ if (!IsInteractiveMember(TP)) return -1;
+
+ maximal=sizeof(hist);
+ if (str && sscanf(str,"%d",anz)==1)
+ i=maximal-anz;
+ if (i<0)
+ i=0;
+
+ TP->More(sprintf("%@s",hist[i..maximal])||"");
+ return 1;
+}
+
+private void DoChangeLeader(object ob) {
+ if (objectp(leader) && leader->QueryProp(P_TEAM_LEADER)==ME)
+ leader->SetProp(P_TEAM_LEADER,0);
+ leader=ob;
+ leader->SetProp(P_TEAM_LEADER,ME);
+}
+
+private int ChangeLeader(string arg) {
+ object ob;
+
+ if (stringp(arg) && arg!="") {
+ if (!objectp(ob=find_player(arg))
+ && !objectp(ob=present(arg,environment(TP))))
+ return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+ } else {
+ ob=TP;
+ }
+ if (objectp(leader)
+ && TP!=leader
+ && (!interactive(TP) || (interactive(leader) && query_idle(leader)<180)))
+ return notify_fail("Der Teamleiter ist noch aktiv.\n"),0;
+ if (objectp(leader)
+ && query_once_interactive(leader)
+ && !query_once_interactive(leader))
+ return notify_fail("Nur ein Spieler kann das Team leiten.\n"),0;
+ if (!IsMember(ob))
+ return notify_fail(ob->Name(WER)+" ist kein Teammitglied.\n"),0;
+ DoChangeLeader(ob);
+ gtell(ob->Name(WER)+" leitet jetzt das Team.\n");
+ return 1;
+}
+
+varargs private int CheckSecond(object pl, object *obs) {
+ mixed x,ip;
+ mapping second;
+ object ob;
+ int i;
+
+ if (!objectp(pl))
+ pl=TP;
+ if (!query_once_interactive(pl))
+ return 0;
+ if (!pointerp(obs))
+ obs=Members();
+ obs-=({pl});
+ second=([]);
+ for (i=sizeof(obs)-1;i>=0;i--) {
+ if (!objectp(ob=obs[i]) || !query_once_interactive(ob)) continue;
+ second[getuid(ob)]=1;
+ if (stringp(x=ob->QueryProp(P_SECOND)))
+ second[lower_case(x)]=1;
+ }
+ if (second[getuid(pl)] ||
+ (stringp(x=pl->QueryProp(P_SECOND)) && second[lower_case(x)]))
+ return 1;
+
+ if (!stringp(ip=pl->QueryProp(P_CALLED_FROM_IP)) || ip=="")
+ return 0;
+ for (i=sizeof(obs)-1;i>=0;i--) {
+ if (!objectp(ob=obs[i]) || !query_once_interactive(ob)) continue;
+ if (ob->QueryProp(P_CALLED_FROM_IP)!=ip) continue;
+ log_file("rochus/zweitieverdacht",
+ sprintf("%s %s,%s\n",ctime()[4..15],getuid(pl),getuid(ob)));
+ break;
+ }
+ return 0;
+}
+
+static int Allowed(object pl) {
+ mixed x;
+ string nm;
+
+ if (!objectp(pl))
+ return notify_fail("WER soll ins Team aufgenommen werden?\n"),0;
+ if (pl==TP)
+ nm="Du b";
+ else
+ nm=pl->Name(WER)+" ";
+ if (objectp(x=pl->QueryProp(P_TEAM)) && x!=ME)
+ return notify_fail(nm+"ist schon in einem anderen Team.\n"),0;
+ if (pl->QueryGuest())
+ return notify_fail(nm+"ist ein Gast.\n"),0;
+ if (pl->QueryProp(P_GHOST))
+ return notify_fail(nm+"ist ein Geist.\n"),0;
+ if (CheckSecond(pl))
+ return notify_fail(nm+"ist Zweitspieler eines Teammitglieds.\n"),0;
+ if (sizeof(filter(Members(),"IsInteractiveMember",ME))
+ >=MAX_TEAM_MEMBERS)
+ return notify_fail("Es sind schon zuviele Spieler im Team.\n"),0;
+ return 1;
+}
+
+private void DoAddMember(object ob) {
+ closure cl;
+
+ if (!IsMember(leader))
+ DoChangeLeader(ob);
+ ob->SetProp(P_TEAM,ME);
+ if (IsMember(ob))
+ return;
+ is_member[ob]=1;
+ cl=symbol_function("QueryProp",ob);
+ attack_cmd[ob]=funcall(cl,P_TEAM_ATTACK_CMD);
+ autofollow[ob]=funcall(cl,P_TEAM_AUTOFOLLOW);
+ wimpy_row[ob]=funcall(cl,P_TEAM_WIMPY_ROW);
+ wanted_row[ob]=funcall(cl,P_TEAM_WANTED_ROW);
+ if (!wanted_row[ob]) wanted_row[ob]=1;;
+ ob->SetProp(P_TEAM_NEWMEMBER,0);
+ GetHpInfo(ob,cl);
+ if (query_once_interactive(ob)) ob->AddHpHook(ME);
+ if (!objectp(assoc_mem[ob]))
+ gtell(ob->Name(WER)+" wurde ins Team aufgenommen.\n");
+ DoChangeRow(ob,wanted_row[ob]);
+}
+
+int AddAssocMember(object caster, object npc) {
+ object *obs;
+
+ if (extern_call() && PO!=caster)
+ return 0;
+ if (!IsMember(caster)
+ || !objectp(npc)
+ || query_once_interactive(npc)
+ || IsMember(npc)
+ || objectp(assoc_mem[caster])
+ || assoc_mem[npc]
+ || caster==npc)
+ return 0;
+ assoc_mem[npc]=caster;
+ DoAddMember(npc);
+ if (!pointerp(obs=assoc_mem[caster]))
+ obs=({});
+ obs=(obs-({npc,0}))+({npc});
+ assoc_mem[caster]=obs;
+ return 1;
+}
+
+static void AddMemberAndAssocs(object caster) {
+ object *obs,ob;
+ int i;
+
+ DoAddMember(caster);
+ if (!pointerp(obs=caster->QueryProp(P_TEAM_ASSOC_MEMBERS)))
+ return;
+ for (i=sizeof(obs)-1;i>=0;i--)
+ if (objectp(ob=obs[i]) && !caster->IsEnemy(ob))
+ AddAssocMember(caster,ob);
+}
+
+int AddMember(object ob) {
+
+ if (!Allowed(ob))
+ return 0;
+
+ // Wenn es einen TP gibt, unterliegt er einigen Einschraenkungen.
+ // Dafuer wird er aber via Aufnahme ins Team auch ggf. Teamleiter, wenn
+ // noetig.
+ // TODO: Dieser Code geht davon aus, dass er nur aus einem Kommando
+ // ausgefuehrt wird und TP der Veranlasser der Aufnahme ist. Dies scheint
+ // mir unrealistisch. (Zesstra)
+ if (TP && TP==previous_object()) {
+ if (!Allowed(TP))
+ return 0;
+ if (TP!=ob && CheckSecond(ob,({TP}))) {// Zweitiereglung bei Gruendung.
+ notify_fail(ob->Name(WER)+" ist Zweitspieler von Dir.\n");
+ return 0;
+ }
+ if (!IsMember(leader))
+ AddMemberAndAssocs(TP);
+ if (TP!=leader) {
+ notify_fail("Nur der Teamleiter kann Mitglieder aufnehmen.\n");
+ return 0;
+ }
+ }
+
+ AddMemberAndAssocs(ob);
+ return 1;
+}
+
+private void DoRemoveMember(object ob) {
+ object *tmembers,caster;
+ mixed asmem;
+ int i;
+
+ ob->SetProp(P_TEAM,0);
+ m_delete(is_member,ob);
+ m_delete(wanted_row,ob);
+ m_delete(act_row,ob);
+ m_delete(wimpy_row,ob);
+ m_delete(autofollow,ob);
+ m_delete(attack_cmd,ob);
+ if (objectp(caster=assoc_mem[ob]) && pointerp(asmem=assoc_mem[caster]))
+ assoc_mem[caster]=asmem-({ob,0});
+ if (query_once_interactive(ob)) ob->RemoveHpHook(ME);
+ m_delete(hp_info,ob);
+ m_delete(autoinf_hp,ob);
+ m_delete(autoinf_sp,ob);
+ DoChangeRow(ob,-1);
+
+ if (!objectp(assoc_mem[ob])) {
+ if (ob->QueryProp(P_GHOST)) {
+ gtell(upper_case(ob->name(WER))+" HAT DAS TEAM VERLASSEN.","Tod");
+ } else {
+ tell_object(ob,"Du verlaesst das Team.\n");
+ gtell(ob->Name(WER)+" hat das Team verlassen.\n");
+ }
+ }
+ m_delete(assoc_mem,ob);
+
+ if (IsMember(leader)) // Hat das Team noch einen Leiter?
+ return;
+ tmembers=Members();ob=0;
+ if (i=sizeof(tmembers)) {
+ ob=tmembers[0];
+ for (--i;i>=0;i--) {
+ if (interactive(tmembers[i])) {
+ ob=tmembers[i];
+ break;
+ }
+ if (query_once_interactive(tmembers[i]))
+ ob==tmembers[i];
+ }
+ DoChangeLeader(ob);
+ gtell(leader->Name(WER)+" hat die Teamleitung uebernommen.\n");
+ return;
+ }
+ TryRemove();
+}
+
+int RemoveAssocMember(object caster, object npc) {
+ object *obs;
+
+ if (extern_call() && PO!=caster)
+ return 0;
+ if (!IsMember(caster) || assoc_mem[npc]!=caster)
+ return 0;
+ DoRemoveMember(npc);
+ return 1;
+}
+
+static void RemoveMemberAndAssocs(object caster) {
+ object *obs,ob;
+ int i;
+
+ if (pointerp(obs=caster->QueryProp(P_TEAM_ASSOC_MEMBERS))) {
+ for (i=sizeof(obs)-1;i>=0;i--)
+ if (objectp(ob=obs[i]))
+ RemoveAssocMember(caster,ob);
+ }
+ DoRemoveMember(caster);
+}
+
+private void RemoveSingles() {
+ object *obs;
+ mixed aso;
+
+ if (!IsMember(leader)) return;
+ if (!query_once_interactive(leader)) return; // NPC Team
+ obs=Members()-({leader});
+ if (pointerp(aso=assoc_mem[leader]))
+ obs-=aso;
+ if (sizeof(obs)) return;
+ gtell("Das Team loest sich auf.\n");
+ RemoveMemberAndAssocs(leader);
+ TryRemove();
+}
+
+int RemoveMember(mixed arg) {
+ object *mems,mem,ob;
+ int i;
+
+ if (objectp(arg)) {
+ ob=arg;
+ } else if (stringp(arg) && arg!="") {
+ mems=Members()-({leader});
+ for (i=sizeof(mems)-1;i>=0;i--) {
+ if (objectp(mem=mems[i]) && mem->id(arg)) {
+ ob=mem;
+ if (query_once_interactive(ob))
+ break;
+ }
+ }
+ if (!objectp(ob))
+ return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+ } else {
+ return 0;
+ }
+ if (TP!=leader && TP!=ob && ob!=PO)
+ return notify_fail("Nur der Teamleiter kann Mitglieder entlassen.\n"),0;
+ if (!IsMember(ob) && ob->QueryProp(P_TEAM)!=ME)
+ return notify_fail(ob->Name(WER)+" ist kein Teammitglied.\n"),0;
+ if (PO!=ob && objectp(assoc_mem[ob]))
+ return notify_fail(ob->Name(WER)+" gehoert zu "+
+ assoc_mem[ob]->Name(WEM)+".\n"),0;
+
+ RemoveMemberAndAssocs(ob);
+ RemoveSingles();
+ return 1;
+}
+
+static int ChangeName(string str) {
+ if (leader && TP!=leader)
+ return notify_fail("Nur der Teamleiter kann den Namen aendern.\n"),0;
+ if (!str || str=="")
+ return 0;
+ str=str[0..19];
+ if (!stringp(str=(TEAM_MASTER->RegisterTeam(str))))
+ return notify_fail("Der Name ist schon vergeben.\n"),0;
+ tname=str;
+ if (objectp(TP))
+ gtell(TP->Name(WER)+" aendert den Teamnamen auf \""+tname+"\".\n");
+ return 1;
+}
+
+void TeamInitAttack() {
+ object *obs,*ens,*removed,ob,en;
+ int i,j;
+
+ debug(sprintf("mis_init_att: %O\n",mis_init_att));
+ ens=m_indices(mis_init_att);removed=({});
+ for (i=sizeof(ens)-1;i>=0;i--) {
+ if (!objectp(en=ens[i]))
+ continue;
+ if (!pointerp(obs=mis_init_att[en]))
+ continue;
+ for (j=sizeof(obs)-1;j>=0;j--)
+ if (!IsMember(ob=obs[j]) || environment(ob)!=environment(en))
+ obs[j]=0;
+ obs-=({0});
+ ob=en->SelectNearEnemy(obs,1);
+ debug(sprintf("Begruessungsschlag von %O fuer %O\n",en,ob));
+ if (!objectp(ob))
+ continue;
+ en->Attack2(ob); // Begruessungsschlag
+ removed+=({en}); // Kein Begruessungsschlag mehr von diesem Monster
+ }
+
+ for (i=sizeof(mis_attacked)-1;i>=0;i--)
+ if (objectp(ob=mis_attacked[i]))
+ ob->ExecuteMissingAttacks(removed);
+ // Begruessungsschlaege die ausgefuehrt wurden entfernen
+ // und nicht ausgefuehrte nachholen
+
+ mis_attacked=({});
+ mis_init_att=([]);
+ att_exec=({});
+}
+
+int InitAttack_Callback(object enemy) {
+ object *arr;
+
+ if (!IsMember(PO) || member(att_exec,PO)<0)
+ return 0;
+ if (!pointerp(arr=mis_init_att[enemy]))
+ arr=({});
+ mis_init_att[enemy]=arr+({PO});
+ if (member(mis_attacked,PO)<0)
+ mis_attacked+=({PO});
+ return 1;
+}
+
+void TeamAttackExecuted_Callback(int success) {
+ if (!IsMember(PO))
+ return;
+ att_exec-=({PO,0});
+ if (!sizeof(att_exec))
+ TeamInitAttack();
+}
+
+private int StartAttack() {
+ object *tmembers,ob,env;
+ int i;
+
+ if (TP!=leader || !objectp(env=environment(TP)))
+ return notify_fail("Nur der Teamleiter kann den Angriff starten.\n"),0;
+ tmembers=Members();
+ TeamInitAttack(); // Falls noch Schlaege fehlen....
+ for (i=sizeof(tmembers)-1;i>=0;i--)
+ if (objectp(ob=tmembers[i]) && stringp(attack_cmd[ob]))
+ if (ob->CallTeamAttack(env)) // Angriff wird ausgefuehrt?
+ att_exec+=({ob}); // Liste der Angreifer
+ gtell(TP->Name(WER)+" startet den Angriff.\n");
+ return 1;
+}
+
+void StartFollow(object env) {
+ object *tmembers,ob;
+ int i;
+ string cmd,args;
+
+ // printf("ld:%O PO:%O TP:%O qv:%O env:%O\n",leader,PO,TP,query_verb(),env);
+ if (TP!=leader
+ || !stringp(cmd=query_verb())
+ || !objectp(env)
+ || TP!=PO
+ || TP->QueryProp(P_GHOST) // Der Befehl war wohl ungesund...
+ || TP->IsTeamMove()) // Angriffsbefehl nicht durch verfolge 2 mal...
+ return;
+ cmd="\\"+cmd;
+ if (stringp(args=TP->_unparsed_args()) && args!="")
+ cmd+=(" "+args);
+ tmembers=Members()-({leader});
+ for (i=sizeof(tmembers)-1;i>=0;i--)
+ if (objectp(ob=tmembers[i]) && autofollow[ob])
+ if(!query_once_interactive(ob) || env==environment(ob)) // autofolge nur bei anfuehrer im gleichen raum
+ ob->CallTeamFollow(env,cmd);
+}
+
+void ShowTeamInfo() {
+ int i;
+ object *tmembers,ob;
+ string form;
+
+ if (!TI || TP!=TI || !IS_LEARNER(TI))
+ return;
+
+ UpdateActRow();
+ form="";
+ for (i=0;i<MAX_TEAMROWS;i++)
+ form+=sprintf(" %d-%d",formin[i],formax[i]);
+ printf("%s [%O] L: %s F:%s\n",name(),ME,
+ (objectp(leader)?leader->Name(WER):"?"),form);
+ tmembers=Members();
+ for (i=sizeof(tmembers)-1;i>=0;i--) {
+ ob=tmembers[i];
+ printf(" %d(%d) %s [%O]\n",
+ act_row[ob],wanted_row[ob],ob->Name(WER),ob);
+ }
+}
+
+varargs int ShowTeamHP(string arg) {
+ object *tmembers,ob;
+ closure qp;
+ int i,longinf;
+ mixed *vals,*res,cols,fr,rr,termstr;
+ mapping real_row;
+ string nm,inf;
+
+ if (arg && arg[0..3]=="lang") {
+ if (TP!=leader)
+ return notify_fail("Nur der Teamleiter kann die lange "+
+ "Uebersicht aufrufen.\n"),0;
+ longinf=1;
+ arg=arg[5..];
+ } else
+ longinf=0;
+ real_row=PresentPositions(environment(TP));
+ tmembers=({});
+ for (i=0;i<MAX_TEAMROWS;i++)
+ tmembers+=rows[i];
+ res=({});
+ switch(TP->QueryProp(P_TTY)) {
+ case "ansi":
+ termstr=({ANSI_RED+ANSI_BOLD,ANSI_YELLOW,ANSI_GREEN,ANSI_NORMAL,
+ ANSI_UNDERL});
+ break;
+ case "vt100":
+ termstr=({ANSI_BOLD,"","",ANSI_NORMAL,ANSI_UNDERL});
+ break;
+ default:
+ termstr=({"","","","",""});
+ }
+ vals=({"",0,0,termstr[3],"",0,0,termstr[3]});
+
+ printf(" Name Gilde LV GLV LP (MLP) KP (MKP) Vors. GR AR TR FR A V\n");
+ if (longinf)
+ printf(" (Zeile 2) Angriffsbefehl Fluchtrichtung\n");
+
+ for (i=sizeof(tmembers)-1;i>=0;i--) {
+ if (!objectp(ob=tmembers[i])) continue;
+ qp=symbol_function("QueryProp",ob);
+ fr=GetHpInfo(ob,qp);
+ vals[1]=fr[0];vals[2]=fr[1];vals[5]=fr[2];vals[6]=fr[3];
+ /*
+ vals[1]=funcall(qp,P_HP);vals[2]=funcall(qp,P_MAX_HP);
+ vals[5]=funcall(qp,P_SP);vals[6]=funcall(qp,P_MAX_SP);
+ */
+ if (!pointerp(cols=funcall(qp,P_TEAM_COLORS)) || sizeof(cols)<4)
+ cols=({vals[2]/4,vals[2]/2,vals[6]/4,vals[6]/2});
+ if (vals[1]<cols[0])
+ vals[0]=termstr[0];
+ else if (vals[1]<cols[1])
+ vals[0]=termstr[1];
+ else
+ vals[0]=termstr[2];
+ if (vals[5]<cols[2])
+ vals[4]=termstr[0];
+ else if (vals[5]<cols[3])
+ vals[4]=termstr[1];
+ else
+ vals[4]=termstr[2];
+ if (intp(fr=wimpy_row[ob]) && fr>1) fr=sprintf("%2d",fr); else fr="--";
+ if (intp(rr=real_row[ob]) && rr>0) rr=sprintf("%2d",rr); else rr="--";
+ nm=ob->Name(RAW);
+ inf=sprintf("%s %-11s%s %-14s %3d %3d %s%3d (%3d)%s %s%3d (%3d)%s %5d %2d %2d %2s %2s %1s %1s\n",
+ ((ob==leader)?(termstr[4]+"*"):" "),
+ nm[0..10],termstr[3],
+ capitalize(funcall(qp,P_GUILD)||"")[0..13],
+ funcall(qp,P_LEVEL),
+ funcall(qp,P_GUILD_LEVEL),
+ vals[0],vals[1],vals[2],vals[3],
+ vals[4],vals[5],vals[6],vals[7],
+ funcall(qp,P_WIMPY),
+ wanted_row[ob],act_row[ob],rr,fr,
+ (attack_cmd[ob]?"X":"-"),
+ (autofollow[ob]?"X":"-"));
+ if (longinf) {
+ if (!stringp(fr=funcall(qp,P_WIMPY_DIRECTION))) fr="";
+ if (!stringp(rr=attack_cmd[ob])) rr="";
+ if (fr!="" || rr!="")
+ inf+=sprintf(" %-42s %-21s\n",rr[0..41],fr[0..20]);
+ }
+
+ switch (arg) {
+ case "alphabetisch":
+ res+=({({(query_once_interactive(ob)?"0":"1")+nm,inf})});
+ break;
+ case "sortiert":
+ res+=({({sprintf("%2s %2d %2d %s",rr,act_row[ob],wanted_row[ob],nm),
+ inf})});
+ break;
+ default:
+ res+=({({i,inf})});
+ }
+ }
+ if (arg && arg!="")
+ res=sort_array(res,"CmpFirstArrayElement",ME);
+ for (i=sizeof(res)-1;i>=0;i--)
+ write(res[i][1]);
+ return 1;
+}
+
+varargs int ShowTeamRooms(string arg) {
+ object *tmembers,ob;
+ string s1,s2;
+ mixed *res;
+ int i;
+
+ tmembers=Members();res=({});
+ for (i=sizeof(tmembers)-1;i>=0;i--) {
+ if (!objectp(ob=tmembers[i])) continue;
+ if (!query_once_interactive(ob) && arg!="alle") continue;
+ s1=ob->Name(RAW);
+ if (!objectp(ob=environment(ob))) continue;
+ if (!stringp(s2=ob->QueryProp(P_INT_SHORT))) s2="";
+ res+=({({s1,s2})});
+ }
+ res=sort_array(res,"CmpFirstArrayElement",ME);
+ for (i=sizeof(res)-1;i>=0;i--)
+ printf("%-11s %-66s\n",res[i][0][0..10],res[i][1][0..65]);
+ return 1;
+}
+
+int ChangeColors(string arg) {
+ int *col;
+
+ notify_fail("team farben lp_rot lp_gelb [kp_rot kp_gelb]\n");
+ if (!arg)
+ return 0;
+ col=({0,0,0,0});
+ if (sscanf(arg,"%d %d %d %d",col[0],col[1],col[2],col[3])!=4) {
+ if (sscanf(arg,"%d %d",col[0],col[1])!=2)
+ return 0;
+ col[2]=col[0];col[3]=col[1];
+ }
+ printf("Die Anzeige ist jetzt gelb unter %d LP, rot unter %d LP\n"+
+ " bzw. gelb unter %d KP, rot unter %d KP.\n",
+ col[1],col[0],col[3],col[2]);
+ TP->SetProp(P_TEAM_COLORS,col);
+ TP->Set(P_TEAM_COLORS,SAVE,F_MODE_AS);
+ return 1;
+}
+
+int ChangeAutoInfo(string arg) {
+ string *words,txt;
+ int i,fl;
+
+ if (TP!=leader)
+ return notify_fail("Nur der Teamleiter kann automatisch "+
+ "informiert werden.\n"),0;
+ words=old_explode(arg?arg:""," ");fl=0;
+ for (i=sizeof(words)-1;i>=0;i--) {
+ switch(words[i]) {
+ case "aus":
+ write("Du wirst nicht mehr automatisch informiert.\n");
+ autoinf_flags=0;
+ return 1;
+ case "+kp":
+ fl|=AUTOINF_SP_PLUS;
+ case "kp":
+ fl|=AUTOINF_SP_MINUS;
+ break;
+ case "+lp":
+ fl|=AUTOINF_HP_PLUS;
+ case "lp":
+ case "ein":
+ case "an":
+ fl|=AUTOINF_HP_MINUS;
+ break;
+ case "sofort":
+ fl|=AUTOINF_INSTANT;
+ break;
+ default:
+ ;
+ }
+ }
+ if (!fl)
+ return notify_fail("WIE moechtest Du automatisch informiert werden?\n"),0;
+ if (fl==AUTOINF_INSTANT) fl|=AUTOINF_HP_MINUS;
+ autoinf_flags=fl;
+ txt="Du wirst"+((fl&AUTOINF_INSTANT)?" sofort ":" ")+"informiert, wenn";
+ if (fl&(AUTOINF_HP_PLUS|AUTOINF_HP_MINUS)) {
+ txt+=" die Lebenspunkte eines Teammitglieds";
+ if (fl&(AUTOINF_HP_PLUS)) txt+=" sich aendern"; else txt+=" fallen";
+ if (fl&(AUTOINF_SP_PLUS|AUTOINF_SP_MINUS)) txt+=" oder";
+ }
+ if (fl&(AUTOINF_SP_PLUS|AUTOINF_SP_MINUS)) {
+ txt+=" die Konzentrationspunkte";
+ if (fl&(AUTOINF_SP_PLUS)) txt+=" sich aendern"; else txt+=" fallen";
+ }
+ write(break_string(txt+".\n",78));
+ return 1;
+}
+
+private void DoNotifyHpChange() {
+ object *obs,ob;
+ string str;
+ int i;
+
+ autoinf_time=time();
+ str="";
+ obs=m_indices(autoinf_hp);
+ for (i=sizeof(obs)-1;i>=0;i--) {
+ if (!objectp(ob=obs[i])) continue;
+ if (str!="") str+=", ";
+ str+=sprintf("%s: %d LP",ob->name(WER),autoinf_hp[ob]);
+ if (member(autoinf_sp,ob))
+ str+=sprintf(" / %d KP",autoinf_sp[ob]);
+ m_delete(autoinf_sp,ob);
+ }
+ obs=m_indices(autoinf_sp);
+ for (i=sizeof(obs)-1;i>=0;i--) {
+ if (!objectp(ob=obs[i])) continue;
+ if (str!="") str+=", ";
+ str+=sprintf("%s: %d KP",ob->name(WER),autoinf_sp[ob]);
+ }
+
+ if (str!="" && IsMember(leader))
+ tell_object(leader,break_string(capitalize(str)+"\n",78));
+ autoinf_hp=([]);
+ autoinf_sp=([]);
+}
+
+void NotifyHpChange() {
+ mixed act,old;
+ int change;
+
+ if (!IsMember(PO) || !pointerp(act=hp_info[PO]))
+ return;
+ old=act[0..];
+ act=GetHpInfo(PO);change=0;
+ if (!autoinf_flags) return;
+ if (((autoinf_flags&AUTOINF_HP_MINUS) && act[0]<old[0]) ||
+ ((autoinf_flags&AUTOINF_HP_PLUS) && act[0]>old[0]))
+ autoinf_hp[PO]=act[0],change=1;
+ if (((autoinf_flags&AUTOINF_SP_MINUS) && act[2]<old[2]) ||
+ ((autoinf_flags&AUTOINF_SP_PLUS) && act[2]>old[2]))
+ autoinf_sp[PO]=act[2],change=1;
+
+ if (autoinf_time<time() || (change && (autoinf_flags&AUTOINF_INSTANT)))
+ DoNotifyHpChange();
+}
+
+int TeamCmd(string cmd, string arg) {
+ if (!IsMember(TP)) {
+ notify_fail("Du bist kein Teammitglied.\n");
+ if (TP->QueryProp(P_TEAM)==ME)
+ TP->SetProp(P_TEAM,0);
+ return 0;
+ }
+ switch(cmd) {
+ case "angriff":
+ return StartAttack();
+ case "angriffsbefehl":
+ if (stringp(arg))
+ attack_cmd[TP]=arg;
+ else
+ m_delete(attack_cmd,arg);
+ return 1;
+ case "autofolge":
+ case "autof":
+ if (arg=="ein" || arg=="an")
+ autofollow[TP]=1;
+ else
+ m_delete(autofollow,TP);
+ return 1;
+ case "autoi":
+ case "autoinf":
+ case "autoinfo":
+ case "autoinform":
+ return ChangeAutoInfo(arg);
+ case "entlasse":
+ return RemoveMember(arg);
+ case "farben":
+ return ChangeColors(arg);
+ case "fluchtreihe":
+ case "flucht":
+ return ChangeWimpyRow(arg);
+ case "formation":
+ if (TP!=leader)
+ return notify_fail("Nur der Teamleiter kann die Formation aendern.\n"),0;
+ return ChangeFormation(arg);
+ case "hist":
+ case "history":
+ return Hist(arg);
+ case "":
+ case "info":
+ return ShowTeamHP(arg);
+ case "leiter":
+ case "leiterin":
+ case "leitung":
+ return ChangeLeader(arg);
+ case "name":
+ return ChangeName(arg);
+ case "orte":
+ return ShowTeamRooms(arg);
+ case "kampfreihe":
+ case "reihe":
+ return ChangeRow(arg);
+ case "ruf":
+ case "rufe":
+ return Shout(arg);
+ case "verlasse":
+ TP->SetProp(P_TEAM,0);
+ return RemoveMember(TP);
+ default:;
+ }
+ return 0;
+}
+
+void reset() {
+ RemoveSingles();
+ TryRemove();
+}