blob: 2b31db9d215bea1b19d4a6687989190c43997aff [file] [log] [blame]
// MorgenGrauen MUDlib
//
// living/team.c
//
// $Id: team.c 9138 2015-02-03 21:46:56Z Zesstra $
#pragma strong_types
#pragma save_types
#pragma range_check
#pragma no_clone
#pragma pedantic
#define NEED_PROTOTYPES
#include <properties.h>
#include <thing/properties.h>
#include <living/combat.h>
#include <combat.h>
#include <living/team.h>
#include <wizlevels.h>
#include <hook.h>
#define ME this_object()
#define TP this_player()
#define PO previous_object()
#define ENV environment()
private nosave string team_attack_cmd;
private nosave mapping team_follow_todo;
private nosave int team_autofollow;
private nosave object teammove;
void create() {
Set(P_TEAM_ATTACK_CMD,-1,F_SET_METHOD);
Set(P_TEAM_ATTACK_CMD,PROTECTED,F_MODE_AS);
Set(P_TEAM_AUTOFOLLOW,-1,F_SET_METHOD);
Set(P_TEAM_AUTOFOLLOW,PROTECTED,F_MODE_AS);
teammove=0;
offerHook(H_HOOK_TEAMROWCHANGE, 1);
}
void add_team_commands() {
add_action("teamcmd","gruppe");
add_action("teamcmd","g");
add_action("teamcmd","team");
}
string _query_team_attack_cmd() {
return Set(P_TEAM_ATTACK_CMD,team_attack_cmd);
}
int _query_team_autofollow() {
return Set(P_TEAM_AUTOFOLLOW,team_autofollow);
}
private int team_help() {
// Syntax-Kompatiblitaet (Avalon) ist ganz nett :-)
write("\
(Befehle des Teamleiters sind mit * gekennzeichnet\n\
\n\
* team angriff\n\
team angriffsbefehl <befehl>\n\
* team aufnahme <name>\n\
team autof[olge] <ein/aus>\n\
* team autoi[nfo] <ein/aus> [+[lp]] [+[kp]] [sofort]\n\
* team entlasse <name>\n\
team farben lp_rot lp_gelb kp_rot kp_gelb\n\
team flucht[reihe] <reihe>\n\
team folge <name>\n\
* team formation <min[-max]> [<min[-max]> ...]\n\
team hilfe|?\n\
team [info] [sortiert|alphabetisch]\n\
team [kampf]reihe <reihe>\n\
* team leiter[in] <name>\n\
team liste\n\
* team name <gruppenname>\n\
team orte [alle]\n\
team ruf[e]\n\
team uebersicht\n\
team verlasse\n");
return 1;
}
object IsTeamLeader() {
object team;
if (!objectp(team=Query(P_TEAM))
|| team!=Query(P_TEAM_LEADER)
|| team->Leader()!=ME)
return 0;
return team;
}
object *TeamMembers() {
object team;
if (!objectp(team=Query(P_TEAM)))
return ({ME});
return team->Members();
}
string TeamPrefix() {
object team;
if (!objectp(team=Query(P_TEAM)))
return "";
return "["+team->Name()+"] ";
}
private int team_aufnahmewunsch(string arg) {
object pl;
if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
|| pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
if (!living(pl))
return notify_fail(pl->Name(WER)+" ist etwas zu inaktiv.\n"),0;
if (pl==ME)
return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
SetProp(P_TEAM_NEWMEMBER,pl);
if (pl->IsTeamLeader()) {
write("Du bittest "+pl->name(WEN)+" um Aufnahme ins Team.\n");
tell_object(pl,TP->Name(WER)+" bittet Dich um Aufnahme ins Team.\n");
} else {
write("Du bittest "+pl->name(WEN)+" um Gruendung eines Teams.\n");
tell_object(pl,TP->Name(WER)+" bittet Dich um Gruendung eines Teams.\n");
}
return 1;
}
private int team_aufnahme(string arg) {
object pl,team;
int res;
if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
|| pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
if (pl->QueryProp(P_TEAM_NEWMEMBER)!=ME)
return notify_fail(pl->Name(WER)+" hat Dich nicht um Aufnahme gebeten.\n"),
0;
if (pl==ME)
return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
if (!objectp(team=QueryProp(P_TEAM)))
team=clone_object(TEAM_OBJECT);
res=team->AddMember(pl);
if (!sizeof(team->Members()))
team->remove();
return res;
}
object IsTeamMove() {
if (!objectp(teammove) || (teammove!=Query(P_TEAM)))
teammove=0;
return teammove;
}
static void DoTeamAttack(object env, object callbackto) {
if (env==ENV && stringp(team_attack_cmd) && !IS_LEARNER(ME)
&& (interactive(ME) || !query_once_interactive(ME))
&& objectp(callbackto) && callbackto==Query(P_TEAM)) {
teammove=callbackto;
command(team_attack_cmd);
}
if (objectp(callbackto))
callbackto->TeamAttackExecuted_Callback(teammove?1:0);
teammove=0;
}
int CallTeamAttack(object env) {
if (stringp(team_attack_cmd)
&& find_call_out("DoTeamAttack")<0
&& PO
&& PO==Query(P_TEAM))
return call_out("DoTeamAttack",0,env,PO),1;
return 0;
}
static int DoTeamFollow() {
string cmd;
if (!team_autofollow
|| (!interactive(ME) && query_once_interactive(ME))
|| IS_LEARNER(ME)
|| !mappingp(team_follow_todo))
return 0;
if (!stringp(cmd=team_follow_todo[ENV]))
return team_follow_todo=0;
do {
m_delete(team_follow_todo,ENV);
tell_object(ME,sprintf("Du folgst Deinem Team mit \"%s\".\n",cmd));
command(cmd);
} while (get_eval_cost()>900000 && random(1000)>20 && objectp(ME)
&& stringp(cmd=team_follow_todo[ENV]));
// Ist Spieler in Umgebung gelandet, fuer die noch ein
// Befehl auszufuehren ist?
if (!objectp(ME) || !stringp(team_follow_todo[ENV]))
return team_follow_todo=0;
while (remove_call_out("DoTeamFollow")!=-1) ;
call_out("DoTeamFollow",0);
return 0;
}
int CallTeamFollow(object env, string cmd) {
if (!team_autofollow
|| PO!=Query(P_TEAM)
|| !PO
|| !objectp(env)
|| !stringp(cmd))
return 0;
if (!mappingp(team_follow_todo))
team_follow_todo=([]);
if (ENV!=env && !team_follow_todo[ENV])
return 0;
team_follow_todo[env]=cmd;
if (find_call_out("DoTeamFollow")<0)
call_out("DoTeamFollow",0);
return 1;
}
int ClearTeamFollow() {
if (PO!=Query(P_TEAM) || !PO)
return 0;
team_follow_todo=([]);
return 1;
}
mixed *PresentTeamRows() {
object team;
mixed *res;
int i;
if (!objectp(team=Query(P_TEAM))) {
res=EMPTY_TEAMARRAY;
res[0]=({ME});
return res;
}
res=team->PresentRows(ENV);
for (i=0;i<MAX_TEAMROWS;i++)
if (member(res[i],ME)>=0)
return res;
res[0]+=({ME});
return res;
}
varargs mixed *PresentEnemyRows(object *here) {
mixed *res,*rows;
mapping added_teams;
int i,j;
object ob,team;
added_teams=([Query(P_TEAM):1]); // Nicht auf eigenes Team hauen
res=EMPTY_TEAMARRAY;
if (!pointerp(here))
here=PresentEnemies();
for (i=sizeof(here)-1;i>=0;i--) {
if (!objectp(ob=here[i]))
continue;
if (!objectp(team=ob->QueryProp(P_TEAM))) {
res[0]+=({ob});
continue;
}
if (added_teams[team])
continue;
added_teams[team]=1;
rows=team->PresentRows(ENV);
for (j=0;j<MAX_TEAMROWS;j++)
res[j]+=rows[j];
}
return res;
}
varargs object SelectNearEnemy(object *here, int forcefrom) {
object ob,en,team;
mixed *rows;
int *prob,prot,i,r,sz,upsz,sum;
if (!pointerp(here))
here=PresentEnemies();
if (!objectp(ob=SelectEnemy(here)))
return 0;
en=ob->QueryProp(P_TEAM); // Feindliches Team
if (objectp(team=Query(P_TEAM))) { // Eigenes Team
if (en==team) // Feind im eigenen Team, kein ANDERES Mitglied waehlen.
return ob; // Aber auch ausserhalb Reihe 1 draufhauen
rows=team->PresentRows(ENV);
if (member(rows[0],ME)<0) // Stehe ich in der ersten Reihe?
return 0; // Falls nein ist auch kein Gegner nahe.
}
if (!objectp(en))
return ob; // Ist nicht in einem Team, also drauf.
rows=en->PresentRows(environment(ob));
prob=({1,0,0,0,0});
prot=sum=0;
for (i=0;i<MAX_TEAMROWS;i++) {
if (prot>0) prot--; // Schutzkegel nimmt ab.
if (!sz=sizeof(rows[i])) continue; // Gegner in dieser Reihe
upsz=sz-prot;if (upsz<0) continue; // Anzahl ungeschuetzter Gegner
prob[i]+=(upsz+sum); // Wahrscheinlichkeit += ungeschuetzt
sum=prob[i]; // Summe bisheriger Wahrscheinlichkeiten
if (sz>prot) prot=sz; // Neuer Schutzkegel
}
r=random(sum);
for (i=0;i<MAX_TEAMROWS;i++)
if (r<prob[i])
break;
if (i>=MAX_TEAMROWS)
i=0;
if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
return en;
if (i && objectp(en=SelectEnemy(forcefrom?(here&rows[0]):rows[0])))
return en;
return ob;
}
varargs object SelectFarEnemy(object *here, int min, int max, int forcefrom) {
mixed *rows;
int *prob,i,r,sum;
object en;
if (max<0 || min>=MAX_TEAMROWS || max<min)
return 0;
if (min<0) min=0;
if (max>=MAX_TEAMROWS) max=MAX_TEAMROWS-1;
if (!pointerp(here))
here=PresentEnemies();
rows=PresentEnemyRows(here);
prob=({0,0,0,0,0});
sum=0;
for (i=min;i<=max;i++)
sum=prob[i]=sum+sizeof(rows[i])+max-i;
r=random(sum);
for (i=min;i<=max;i++)
if (r<prob[i])
break;
if (i>max)
i=min;
if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
return en;
for (i=min;i<=max;i++)
if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
return en;
return 0;
}
mixed _query_friend() {
mixed res;
if (res=Query(P_FRIEND))
return res;
if (objectp(res=Query(P_TEAM_ASSOC_MEMBERS))
&& query_once_interactive(res))
return res;
return 0;
}
int DeAssocMember(object npc) {
mixed obs;
object team;
if (extern_call() && PO!=npc &&
member(({"gilden","spellbooks"}),
explode(object_name(PO),"/")[1])<0)
return 0;
obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
if (!pointerp(obs))
return 0;
obs-=({npc,0});
SetProp(P_TEAM_ASSOC_MEMBERS,obs);
if (objectp(team=QueryProp(P_TEAM)))
team->RemoveAssocMember(ME,npc);
return 1;
}
int AssocMember(object npc) {
mixed obs;
object team;
if (extern_call() && PO!=npc &&
member(({"gilden","spellbooks"}),
explode(object_name(PO),"/")[1])<0)
return 0;
if (!objectp(npc)
|| npc->QueryProp(P_TEAM_ASSOC_MEMBERS)
|| IsEnemy(npc)
|| npc==ME
|| query_once_interactive(npc))
return 0;
obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
if (objectp(obs))
return 0;
if (!pointerp(obs))
obs=({});
obs=(obs-({npc,0}))+({npc});
SetProp(P_TEAM_ASSOC_MEMBERS,obs);
npc->SetProp(P_TEAM_ASSOC_MEMBERS,ME);
if (objectp(team=QueryProp(P_TEAM)))
team->AddAssocMember(ME,npc);
return 1;
}
varargs void InsertEnemyTeam(mixed ens, int rek) {
object *obs,ob,eteam,team;
int i;
team=Query(P_TEAM);
// Alle Teammitglieder des Gegners sind Feind:
if (objectp(ens)) {
if (objectp(eteam=ens->QueryProp(P_TEAM))) {
if (eteam==team) // feindliches Team = eigenes Team?
return; // also nicht alle Teammitglieder gegeneinander hetzen
ens=eteam->Members();
} else {
ens=({ens});
}
}
if (!pointerp(ens))
return;
ens-=({ME});
// Interactives sollen keine Interactives durch Team angreifen:
if (query_once_interactive(ME)) {
for (i=sizeof(ens)-1;i>=0;i--)
if (objectp(ob=ens[i]) && environment(ob)==environment()
&& !query_once_interactive(ob))
InsertSingleEnemy(ob);
} else {
for (i=sizeof(ens)-1;i>=0;i--)
if (objectp(ob=ens[i]) && environment(ob)==environment())
InsertSingleEnemy(ob);
}
// Alle anderen Teammitglieder Informieren:
if (rek || !objectp(team) || !pointerp(obs=team->Members()))
return;
obs-=({ME});
obs-=ens;
for (i=sizeof(obs)-1;i>=0;i--)
if (objectp(ob=obs[i]))
ob->InsertEnemyTeam(ens,1);
}
int TeamFlee() {
object team;
if (Query(P_TEAM_WIMPY_ROW)<2 || !objectp(team=Query(P_TEAM)))
return 0;
if (!team->FleeToRow(ME))
return 0;
if (Query(P_TEAM_LEADER)==team) {
if (team_autofollow)
tell_object(ME,"Du versuchst zu fliehen, "+
"Dein Team folgt Dir nicht mehr.\n");
team_autofollow=0;
}
return 1;
}
varargs mapping PresentTeamPositions(mixed pres_rows) {
mapping res;
int i,j;
object *obs,ob;
res=([]);
if (!pointerp(pres_rows))
pres_rows=PresentTeamRows();
for (i=0;i<MAX_TEAMROWS;i++) {
obs=pres_rows[i];
for (j=sizeof(obs)-1;j>=0;j--)
if (objectp(ob=obs[j]) && !res[ob])
res[ob]=i+1;
}
return res;
}
varargs int PresentPosition(mixed pmap) {
object team;
int i;
if (!objectp(team=Query(P_TEAM)))
return 1;
if (mappingp(pmap))
return pmap[ME];
if (!pointerp(pmap))
pmap=team->PresentRows(ENV);
for (i=1;i<MAX_TEAMROWS;i++)
if (member(pmap[i],ME)>=0)
return i+1;
return 1;
}
#define FILLSTRING " "
varargs private string center_string(string str, int w) {
return (FILLSTRING[0..((w-sizeof(str))/2-1)]+str+FILLSTRING)[0..(w-1)];
}
private int ShowTeamRows() {
int i,j,sz;
mixed *pres_rows;
object *obs,ob;
string str;
pres_rows=PresentEnemyRows();
for (sz=MAX_TEAMROWS-1;sz>=0;sz--)
if (sizeof(pres_rows[sz]))
break;
for (i=sz;i>=0;i--) {
obs=pres_rows[i];str="";
for (j=sizeof(obs)-1;j>=0;j--)
if (objectp(ob=obs[j])) {
if (str!="") str+=" / ";
str+=ob->Name(RAW);
}
printf("%d. %s\n",i+1,center_string(str,75));
}
if (sz>=0)
write(" ---------------------------------------------------------------------------\n");
pres_rows=PresentTeamRows();
for (sz=MAX_TEAMROWS-1;sz>0;sz--)
if (sizeof(pres_rows[sz]))
break;
for (i=0;i<=sz;i++) {
obs=pres_rows[i];str="";
for (j=sizeof(obs)-1;j>=0;j--)
if (objectp(ob=obs[j])) {
if (str!="") str+=" / ";
str+=ob->Name(RAW);
}
printf("%d. %s\n",i+1,center_string(str,75));
}
return 1;
}
varargs int team_list(string arg) {
object *tobs,*obs,tob,ob,ld;
string *nms,*tnms,str;
int i,j;
if (!pointerp(tobs=TEAM_MASTER->ListTeamObjects())) return 0;
if (arg!="alle") arg=0;
tnms=({});
for (i=sizeof(tobs)-1;i>=0;i--) {
if (!objectp(tob=tobs[i])
|| !objectp(ld=tob->Leader())
|| (!query_once_interactive(ld) && !arg)
|| !pointerp(obs=tob->Members()))
continue;
nms=({});
for (j=sizeof(obs)-1;j>=0;j--) {
if (!objectp(ob=obs[j])
|| (!query_once_interactive(ob) &&!arg))
continue;
if (!stringp(str=ob->Name(WER))) str="?";
if (ob==ld) str+="(*)";
nms+=({str});
nms=sort_array(nms,#'>);
}
if (!stringp(str=tob->Name())) str="Team ?";
str+=": ";
tnms+=({break_string(implode(nms,", "),78,str)});
tnms=sort_array(tnms,#'<);
}
if (sizeof(tnms))
tell_object(ME, sprintf("%@s\n", tnms));
else
tell_object(ME, "Keine Teams gefunden.\n");
return 1;
}
varargs int teamcmd(string arg) {
string *words,narg;
object team;
if (!arg)
arg="";
if (!stringp(narg=TP->_unparsed_args()))
narg = arg;
if (!sizeof(words=explode(arg," ")))
return 0;
if (sizeof(words) > 1) {
arg=implode(words[1..]," ");
narg = implode(explode(narg, " ")[1..], " ");
}
else
arg = narg = "";
switch(words[0]) { // Befehle die keine Mitgliedschaft erfordern:
case "aufnahme":
return team_aufnahme(arg);
case "folge":
return team_aufnahmewunsch(arg);
case "?":
case "hilfe":
return team_help();
case "liste":
return team_list(arg);
case "uebersicht":
return ShowTeamRows();
default:;
}
if (!objectp(team=QueryProp(P_TEAM)))
return notify_fail("Du bist in keinem Team.\n"),0;
switch(words[0]) {
case "angriffsbefehl":
if (narg=="") narg=0;
team_attack_cmd=narg;
if (stringp(narg))
write("Du beginnst den Kampf mit \""+narg+"\"\n");
else
write("Du hast den Teamangriffsbefehl deaktiviert.\n");
break; // NICHT return!
case "autofolge":
case "autof":
if (arg=="ein" || arg=="an") {
team_autofollow=1;
if (IsTeamLeader())
write("Dein Team folgt Dir.\n");
else
write("Du folgst jetzt dem Teamleiter.\n");
} else {
team_autofollow=0;
if (IsTeamLeader())
write("Dein Team folgt Dir nicht mehr.\n");
else
write("Du folgst jetzt nicht mehr dem Teamleiter.\n");
}
break; // NICHT return!
default: ;
}
return team->TeamCmd(words[0],narg); // Befehle die Mitgliedschaft erfordern:
}
varargs void InformRowChange(int from, int to, object caster) {
if (caster) return; // Fuer den Fall, dass Gildenobjekt==ME ist
if (PO!=Query(P_TEAM)) return;
#if __BOOT_TIME__ < 1281904437
mixed gilde = QueryProp(P_GUILD);
if (!stringp(gilde)) return;
if (!objectp(gilde=find_object("/gilden/"+gilde))) return;
gilde->InformRowChange(from,to,ME);
#endif
HookFlow(H_HOOK_TEAMROWCHANGE, ({from,to}) );
}