Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/living/skills.c b/std/living/skills.c
new file mode 100644
index 0000000..93c9dcc
--- /dev/null
+++ b/std/living/skills.c
@@ -0,0 +1,595 @@
+// MorgenGrauen MUDlib
+//
+// living/skills.c -- Gilden-, Skill- und Spellfunktionen fuer Lebewesen
+//
+// $Id: skills.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/std_skills";
+
+#define NEED_PROTOTYPES
+#include <living/skills.h>
+#include <living/skill_attributes.h>
+#include <player/life.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+
+// speichert die Spell-Fatigues (global, Spruchgruppen, Einzelsprueche)
+private mapping spell_fatigues = ([]);
+
+// Prototypen
+private void expire_spell_fatigues();
+
+protected void create()
+{
+ // mainly necessary for players, but there may be some NPC with savefiles.
+ // Additionally, it simplifies expiration of old keys to have it here.
+ call_out(#'expire_spell_fatigues, 4);
+}
+
+
+// Diese - hier scheinbar sinnlose - Funktion wird von /std/player/skills.c dann
+// ueberladen.
+public int is_deactivated_skill(string sname, string gilde)
+{
+ return 0;
+}
+
+
+
+static string _query_visible_guild()
+{ string res;
+
+ if ( stringp(res=Query(P_VISIBLE_GUILD)) )
+ return res;
+
+ return QueryProp(P_GUILD);
+}
+
+static string _query_visible_subguild_title()
+{ string res;
+
+ if ( stringp(res=Query(P_VISIBLE_SUBGUILD_TITLE)) )
+ return res;
+
+ return QueryProp(P_SUBGUILD_TITLE);
+}
+
+static mixed _query_guild_prepareblock()
+{ mapping res;
+ string gilde;
+
+ if ( !stringp(gilde=QueryProp(P_GUILD)) )
+ return 0;
+ if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK))
+ || !member(res,gilde) )
+ return 0;
+ return res[gilde];
+}
+
+static mixed _set_guild_prepareblock(mixed arg)
+{ mapping res;
+ string gilde;
+
+ if ( !stringp(gilde=QueryProp(P_GUILD)) )
+ return 0;
+ if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK)) )
+ res=([]);
+
+ res[gilde]=arg;
+ Set(P_GUILD_PREPAREBLOCK,res);
+
+ return arg;
+}
+
+
+private nosave int valid_setskills_override;
+// Man sollte eigentlich ja nicht Parameter als globale Variablen
+// uebergeben, aber hier ging es nicht anders
+nomask private int valid_setskills(string gilde)
+{ string fn;
+
+ if ( !query_once_interactive(this_object()) )
+ return 1; // Monster duerfen sich selber Skills setzen :)
+
+ if ( QueryProp(P_TESTPLAYER) || IS_WIZARD(this_object()) )
+ return 1; // Testspieler und Magier sind schutzlose Opfer ;-)
+
+ if ( previous_object() )
+ {
+ if ( previous_object()==this_object()
+ && this_interactive()==this_object() )
+ return 1;
+
+ fn=object_name(previous_object());
+ if ( fn[0..7]=="/gilden/"
+ || fn[0..11]=="/spellbooks/"
+ || fn[0..7]=="/secure/"
+ || fn[0..11]=="/p/zauberer/" )
+ return 1; // Die sollten problemlos aendern duerfen
+
+ if ( file_size("/gilden/access_rights")>0
+ && call_other("/gilden/access_rights",
+ "access_rights",
+ getuid(previous_object()),
+ gilde+".c"))
+ return 1; // Setzendes Objekt kommt vom Gildenprogrammierer
+
+ if ( file_size("/gilden/"+gilde+".c")>0
+ && call_other("/gilden/"+gilde,
+ "valid_setskills",
+ explode(fn,"#")[0]) )
+ return 1; // Die Gilde selber kann Ausnahmen zulassen
+ }
+
+ if (valid_setskills_override)
+ {
+ valid_setskills_override=0;
+ return 1; // Fuers Setzen der Closure
+ }
+
+ if ( this_interactive() )
+ {
+ if ( IS_ARCH(this_interactive()) )
+ return 1; // Erzmagier duerfen immer aendern
+
+ if ( call_other("/gilden/access_rights",
+ "access_rights",
+ getuid(this_interactive()),
+ gilde+".c"))
+ return 1; // Der Gildenprogrammierer selber auch
+ }
+
+ // Fuer die Waffenskills, die sollen sich selbst auch setzen duerfen
+ if (!this_interactive() && this_object()==previous_object())
+ return 1;
+
+
+ log_file("SETSKILLS",sprintf("*****\n%s PO:%O->TO:%O TI:%O\n GUILD:%s VERB_ARGS:'%s'\n",
+ ctime(time())[4..15],
+ previous_object(),
+ this_object(),
+ this_interactive(),
+ gilde,
+ ( this_interactive() ? query_verb() + " " +
+ this_interactive()->_unparsed_args() : "") ));
+
+ return 0;
+}
+
+// Nur interne Verwendung, value wird nicht weiter prueft, muss ok sein.
+// Es wird keine Kopie von value gemacht, wenn es ins Mapping geschrieben
+// wird!
+private mapping internal_set_newskills(mapping value, string gilde) {
+ mapping skills;
+
+ // in der richtigen Gilde setzen.
+ if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+ gilde="ANY";
+
+ // Query(), hier ist eine Kopie nicht sinnvoll.
+ if ( !mappingp(skills=Query(P_NEWSKILLS,F_VALUE)) ) {
+ skills=([]);
+ Set(P_NEWSKILLS, skills, F_VALUE);
+ }
+
+ // Falls dies hier mal ausgewertet werden sollte, nicht vergessen, dass
+ // einige Funktion hier im File die Prop evtl. via
+ // internal_query_newskills() abrufen und direkt aendern...
+ valid_setskills(gilde); // Sicherheitsueberpruefung
+
+ // Skills setzen. Set() unnoetig, weil wir das von Query() gelieferte
+ // Mapping aendern und das ja via Referenz bekommen haben.
+ skills[gilde]=value;
+ //Set(P_NEWSKILLS,skills);
+
+ return(value);
+}
+
+// nur zur internen Verwendung, es wird keine Kopie des Skillmappings gemacht!
+private mapping internal_query_newskills(string gilde) {
+ mapping skills;
+
+ // richtige Gilde abfragen.
+ if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+ gilde="ANY";
+
+ skills=Query(P_NEWSKILLS);
+
+ if (!mappingp(skills) || !mappingp(skills=skills[gilde]) )
+ return ([]);
+
+ return(skills);
+}
+
+// Eigentlich sollte man den _query-Funktionen keine Parameter geben...
+static varargs mapping _query_newskills(string gilde) {
+
+ // sonst Kopie des spellmappings liefern! Kostet zwar, aber verhindert
+ // einige andere Bugs und versehentliche Aenderungen an den Skills!
+ return(deep_copy(internal_query_newskills(gilde)));
+}
+
+// Eigentlich sollte man den _set-Funktionen keine weiteren Parameter geben
+static varargs mapping _set_newskills(mapping value, string gilde) {
+
+ // value auf Mappings normalisieren, ggf. Kopieren
+ if ( !mappingp(value) )
+ value=([]);
+ else
+ //zur Sicherheit, wer weiss, was der setzende noch damit macht...
+ value=deep_copy(value);
+
+ // und setzen...
+ internal_set_newskills(value, gilde);
+
+ // und noch ne Kopie von dem Liefern, was wir gesetzt haben (keine Referenz,
+ // sonst koennte der Aufrufende ja noch im Nachhinein aendern).
+ return(_query_newskills(gilde));
+}
+
+private mapping InternalQuerySkill(string sname, string gilde) {
+ mixed skill, skills;
+ // In is_any wird gespeichert, ob es ein gildenunabhaengier Skill ist,
+ // fuer die is_deactivate_skill-Abfrage.
+ int is_any;
+
+ // Skills komplett abfragen, keine spez. Gilde
+ if (!mappingp(skills=Query(P_NEWSKILLS,F_VALUE)))
+ return 0;
+
+ if (stringp(gilde) && sizeof(gilde)) {
+ //bestimmte Gilde angegeben, gut, dort gucken.
+ if (mappingp(skills[gilde]))
+ skill=skills[gilde][sname];
+ }
+ else {
+ gilde=QueryProp(P_GUILD); //reale Gilde holen
+ if (gilde && mappingp(skills[gilde]) &&
+ (skill=skills[gilde][sname])) {
+ // gibt es den Spell in der Gilde des Spielers?
+ // dann hier nix machen...
+ }
+ else if (mappingp(skills["ANY"])) {
+ // Zum Schluss: Gibt es den Skill vielleicht Gildenunabhaengig?
+ skill=skills["ANY"][sname];
+ // wenn man hier reinkommt, dann spaeter mit is_deactivated_skill()
+ // pruefen!
+ is_any=1;
+ }
+ }
+
+ // wenn kein Skill gefunden, mit 0 direkt raus
+ if (!skill) return 0;
+
+ // Bei gildenunabhaengigen auch im Skillmapping vermerken
+ if ( is_any ) {
+ skill+=([SI_GUILD:"ANY"]);
+ // Ist er vielleicht in der Gilde des Spielers deaktiviert?
+ // Dies kann nur der Fall sein, wenn es kein Gildenskill ist.
+ if (is_deactivated_skill(sname,gilde)) {
+ return 0;
+ }
+ }
+
+ return(skill);
+}
+
+public varargs mapping QuerySkill(string sname, string gilde) {
+
+ if (!stringp(sname) || !sizeof(sname))
+ return 0;
+
+ //Kopie zurueckliefern
+ return(deep_copy(InternalQuerySkill(sname,gilde)));
+}
+
+#define SMUL(x,y) ((x<0 && y<0)?(-1*x*y):(x*y))
+public varargs int QuerySkillAbility(string sname, string gilde)
+{ mapping skill;
+ string skill2;
+
+ if ( !(skill=InternalQuerySkill(sname, gilde)) )
+ return 0;
+
+ int val=skill[SI_SKILLABILITY];
+
+ if (skill2=skill[SI_INHERIT])
+ {
+ int val2;
+ val2=QuerySkillAbility(skill2);
+ val=(val*MAX_ABILITY+SMUL(val,val2))/(2*MAX_ABILITY);
+ }
+
+ return val;
+}
+
+protected varargs mixed LimitAbility(mapping sinfo, int diff)
+{ mixed abil;
+ int max,old,d2;
+
+ abil=sinfo[SI_SKILLABILITY];
+
+ if ( !intp(abil) )
+ return sinfo;
+ old=abil;
+
+ // Beim Spieler eingetragene Schwierigkeit gilt vor angegebener.
+ if ( (d2=sinfo[SI_DIFFICULTY]) )
+ diff=d2;
+
+ // diff <-100 soll nicht hemmen und macht keinen Sinn
+ diff=(diff<(-100))?(-100):diff;
+
+ max=MAX_ABILITY-(diff+100)*(35-QueryProp(P_LEVEL));
+
+// diff|lvl 1:| 3:| 7:| 10:| 13:| 16:| 19:| 22:| 25:| 28:| 31:| 34:|
+// ----+------+-----+-----+----+----+----+----+----+----+----+----+----+
+// -50| 83%| 84%| 86%| 87%| 89%| 90%| 92%| 93%| 95%| 96%| 98%| 99%|
+// -10| 69%| 72%| 74%| 77%| 80%| 82%| 85%| 88%| 91%| 93%| 96%| 99%|
+// 0| 66%| 69%| 72%| 75%| 78%| 81%| 84%| 87%| 90%| 93%| 96%| 99%|
+// 10| 62%| 65%| 69%| 72%| 75%| 79%| 82%| 85%| 89%| 92%| 95%| 98%|
+// 20| 59%| 62%| 66%| 70%| 73%| 77%| 80%| 84%| 88%| 91%| 95%| 98%|
+// 30| 55%| 59%| 63%| 67%| 71%| 75%| 79%| 83%| 87%| 90%| 94%| 98%|
+// 40| 52%| 56%| 60%| 65%| 69%| 73%| 77%| 81%| 86%| 90%| 94%| 98%|
+// 50| 49%| 53%| 58%| 62%| 67%| 71%| 76%| 80%| 85%| 89%| 94%| 98%|
+// 100| 32%| 38%| 44%| 50%| 56%| 62%| 68%| 74%| 80%| 86%| 92%| 98%|
+// 150| 15%| 22%| 30%| 37%| 45%| 52%| 60%| 67%| 75%| 82%| 90%| 97%|
+// 200| -2%| 7%| 16%| 25%| 34%| 43%| 52%| 61%| 70%| 79%| 88%| 97%|
+// 250| -19%| -8%| 2%| 12%| 23%| 33%| 44%| 54%| 65%| 75%| 86%| 96%|
+// 300| -36%| -24%| -12%| 0%| 12%| 24%| 36%| 48%| 60%| 72%| 84%| 96%|
+// 400| -70%| -55%| -40%|-25%|-10%| 5%| 20%| 35%| 50%| 65%| 80%| 95%|
+// 500| -104%| -86%| -68%|-50%|-32%|-14%| 4%| 22%| 40%| 58%| 76%| 94%|
+// 600| -138%|-117%| -96%|-75%|-54%|-33%|-12%| 9%| 30%| 51%| 72%| 93%|
+
+ if ( abil>max )
+ abil=max;
+ if ( abil>MAX_ABILITY
+ ) abil=MAX_ABILITY;
+ else if ( abil<-MAX_ABILITY )
+ abil=-MAX_ABILITY;
+
+ if ( old && !abil )
+ abil=1;
+ // Faehigkeiten sollen nicht durch die Begrenzung verschwinden
+
+ sinfo[SI_SKILLABILITY]=abil;
+
+ return sinfo;
+}
+
+public varargs void ModifySkill(string sname, mixed val, int diff, string gilde)
+{
+ mapping skills;
+ mixed skill;
+
+ if ( !stringp(sname) || !sizeof(sname) )
+ return;
+
+ // internal_query_newskills() macht keine Kopie
+ skills=internal_query_newskills(gilde);
+
+ // Skill ermitteln, wenn nicht existiert, wird er angelegt.
+ if (!skill=skills[sname]) {
+ skill=([]);
+ }
+
+ // Zur Sicherheit mal das Mapping kopieren, wer weiss, was der
+ // Aufrufende dieser Funktion selber spaeter damit noch macht.
+ // ist ok, wenn val kein Mapping ist, dann macht deep_copy nix.
+ val=deep_copy(val);
+
+ // Skill und val vereinigen
+ if ( mappingp(val) )
+ skill+=val;
+ else if (intp(val))
+ skill[SI_SKILLABILITY]=val;
+ else
+ raise_error(sprintf("Bad arg 2 to ModifySkill(): expected 'int', "
+ "got %.10O.\n",val));
+
+ // Lernen entsprechend SI_DIFFICULTY begrenzen.
+ if(diff && !member(skill,SI_DIFFICULTY))
+ skill[SI_DIFFICULTY]=diff;
+ skill=LimitAbility(skill,diff || skill[SI_DIFFICULTY]);
+
+ // schliesslich im Skillmapping vermerken. Im Normalfall ist der Skill jetzt
+ // schon geaendert, nicht erst nach dem internal_set_newskills().
+ skills[sname]=skill;
+
+ // explizites Abspeichern fast ueberfluessig, weil wir oben eine Referenz
+ // auf das Skillmapping gekriegt haben...
+ // Aber es koennte sein, dass dies der erste Skill fuer diese Gilde ist,
+ // dann ist es noetig. Zum anderen wird internal_set_newskills() nochmal
+ // geloggt.
+ internal_set_newskills(skills,gilde);
+}
+
+public varargs void LearnSkill(string sname, int add, int diff)
+{ mapping skill;
+ string skill2,gilde;
+ int val;
+
+ // Spieler sollen nur lernen, wenn sie interactive sind. Das soll
+ // natuerlich nur fuer Spieler gelten.
+ if (query_once_interactive(this_object()) && !interactive())
+ return;
+
+ if ( add>MAX_SKILLEARN )
+ add=MAX_SKILLEARN;
+ else if ( add<1 )
+ add=1;
+
+ // Skillmapping ermitteln (hier kommt keine Kopie zurueck)
+ skill=InternalQuerySkill(sname, 0);
+ // wenn kein Skill, dann Abbruch
+ if (!skill) return;
+
+ val=skill[SI_SKILLABILITY];
+ gilde=skill[SI_GUILD];
+
+ val+=add;
+
+ ModifySkill(sname,val,diff,gilde);
+ if ( skill2=skill[SI_INHERIT] )
+ LearnSkill(skill2,add/3,diff);
+}
+
+public varargs int UseSpell(string str, string spell)
+{ string gilde,sbook;
+ mapping sinfo;
+ closure cl;
+
+ if ( !spell && !(spell=query_verb()) )
+ return 0;
+
+ spell=lower_case(spell);
+
+ // QuerySkill() liefert eine Kopie des Skillmappings.
+ // wenn skill unbekannt oder Ability <= 0, ist der Spell nicht nutzbar.
+ if ( !(sinfo=QuerySkill(spell,0))
+ || sinfo[SI_SKILLABILITY] <= 0 )
+ return 0;
+
+ sinfo[SI_SKILLARG]=str; // Argument eintragen
+
+ if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+ {
+ // Wenn ein Spellbook angegeben ist wird der Spell direkt ausgefuehrt
+ if ( stringp(sbook=sinfo[SI_SPELLBOOK]) )
+ cl=symbol_function("UseSpell",SPELLBOOK_DIR+sbook);
+
+ // Wenn der Spieler in einer Gilde ist, so weiss diese, in welchem
+ // Spellbook der Spell zu finden ist...
+ else if ( (gilde=QueryProp(P_GUILD)) &&
+ ( find_object(GUILD_DIR+gilde) || file_size(GUILD_DIR+gilde+".c")>-1))
+ cl=symbol_function("UseSpell",GUILD_DIR+gilde);
+ else
+ cl=function int () {return 0;};
+
+ sinfo[SI_CLOSURE]=cl;
+ valid_setskills_override=1;
+ ModifySkill(spell,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+ valid_setskills_override=0;
+ }
+ return funcall(cl,this_object(),spell,sinfo);
+}
+
+public varargs mixed UseSkill(string skill, mapping args)
+{ mapping sinfo;
+ string gilde, func,skill2;
+ mixed res;
+ closure cl;
+
+ if ( !skill ||
+ QueryProp(P_GHOST))
+ return 0;
+
+ skill=capitalize(skill);
+ // QuerySkill() liefert eine Kopie des Skillmappings
+ if ( !(sinfo=QuerySkill(skill,0)) )
+ return 0;
+
+ if (args)
+ sinfo+=args;
+
+ if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+ {
+ if ( !(func=sinfo[SI_SKILLFUNC]) // Keine Funktion angegeben?
+ || !(gilde=QueryProp(P_GUILD))) // Keine Gilde angegeben?
+ {
+ // Dann Standard-Funktion nehmen, wenn es die nicht gibt, den
+ // Ability-Wert zurueckliefern.
+ if (!closurep(cl = symbol_function("StdSkill_"+skill,this_object())))
+ cl=function int (object ob, string sname)
+ {return QuerySkillAbility(sname);} ;
+ }
+ else
+ {
+ // Sonst diese Funktion im Gildenobjekt aufrufen
+ cl=symbol_function(func,GUILD_DIR+gilde);
+ }
+
+ sinfo[SI_CLOSURE]=cl;
+ valid_setskills_override=1;
+ ModifySkill(skill,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+ valid_setskills_override=0;
+ }
+
+ res=funcall(cl,this_object(),skill,sinfo);
+ if ( (skill2=sinfo[SI_INHERIT]) && mappingp(res) )
+ res=UseSkill(skill2,res); // Fuer Skills, die von anderen abhaengen
+
+ return res;
+}
+
+// ************** Spellfatigues ***************
+
+/* Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ * <key> darf 0 sein (globale Spruchsperre).
+ * Liefert 0, wenn keine Sperre und die Ablaufzeit, wenn eine Sperre noch
+ * gueltig. ist.
+ */
+public varargs int CheckSpellFatigue(string key) {
+ // key==0 is the (default) global spellfatigue.
+ if (spell_fatigues[key] > time())
+ return spell_fatigues[key]; // Ablaufzeit zurueckgeben.
+
+ return 0; // ok, keine Sperre.
+}
+
+/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
+ * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
+ * Rueckgabewert: Ablaufzeit der gesetzten Sperre
+ -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
+ 0, wenn duration 0 ist.
+ */
+public varargs int SetSpellFatigue(int duration, string key) {
+ // aktuelle Sperre abgelaufen?
+ if (CheckSpellFatigue(key))
+ return -1; // alte Sperre noch aktiv.
+
+ duration += time();
+ // 0 is OK for <key>, it is the key for global spell fatigues
+ spell_fatigues[key] = duration;
+ return duration;
+}
+
+/* Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ * <key> darf fuer diese Funktion 0 (globale Spruchsperre) sein, aber man
+ * darf das Argument nicht weglassen, damit nicht ein verpeilter Magier
+ * versehentlich die globale Spruchsperre nullt.
+ */
+public void DeleteSpellFatigue(string key) {
+ // key==0 is the (default) global spellfatigue.
+ m_delete(spell_fatigues, key);
+}
+
+/** Loescht abgelaufene Keys aus dem spell_fatigue mapping.
+ */
+private void expire_spell_fatigues() {
+ foreach(string key, int endtime: spell_fatigues) {
+ if (endtime <= time())
+ m_delete(spell_fatigues, key);
+ }
+}
+
+/** Setmethode fuer P_NEXT_SPELL_TIME.
+ */
+static int _set_next_spell(int fatigue) {
+ return SetSpellFatigue(fatigue - time());
+}
+/** Querymethode fuer P_NEXT_SPELL_TIME.
+ */
+static int _query_next_spell() {
+ return CheckSpellFatigue();
+}
+