blob: 6805acc63f00e0a96e7e65132be3f504ae67dce9 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// gilden_ob.c -- Basisfunktionen einer Gilde
4//
5// $Id: gilden_ob.c 8433 2013-02-24 13:47:59Z Zesstra $
6#pragma strict_types
7#pragma save_types
8#pragma no_shadow
9#pragma no_clone
10#pragma range_check
11#pragma pedantic
12
13inherit "/std/restriction_checker";
14#include <properties.h>
15#include <attributes.h>
16#include <new_skills.h>
17#include <defines.h>
18#include <player/fao.h>
19#include <wizlevels.h>
20
21void create() {
22 // P_GUILD_SKILLS sollte nicht direkt von aussen manipuliert werden...
23 SetProp(P_GUILD_SKILLS,([]));
24 Set(P_GUILD_SKILLS,PROTECTED,F_MODE_AS);
25}
26
27mapping _query_guild_skills() {
28 // eine Kopie zurueckliefern, um versehentliche Aenderungen des
29 // Originalmappings in der Gilde zu vermeiden.
30 // Darf nicht ohne weiteres entfernt werden, da es hier im File Code gibt,
31 // der sich auf die Kopie verlaesst.
32 return(deep_copy(Query(P_GUILD_SKILLS)));
33}
34
35string GuildName() {
36 //Gilden muessen Blueprints sein, so. ;-)
37 return object_name(this_object())[8..];
38}
39
40varargs int
41IsGuildMember(object pl) {
42 string plg;
43
44 if (!pl && !(pl=this_player()))
45 return 0;
46 if (!(plg=(string)pl->QueryProp(P_GUILD)))
47 return 0;
48 if (GuildName()!=plg) {
49 _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
50 return 0;
51 }
52 return 1;
53}
54
55int check_cond(mixed cond) {
56 string res;
57
58 if (intp(cond)) {
59 _notify_fail("Dir fehlt noch die noetige Erfahrung. "+
60 "Komm spaeter wieder.\n");
61 return (this_player()->QueryProp(P_XP)>=cond);
62 }
63 if (mappingp(cond)) {
64 res=check_restrictions(this_player(),cond);
65 if (!stringp(res)) return 1;
66 _notify_fail(res);
67 return 0;
68 }
69 return 1;
70}
71
72int can_advance() {
73 int lv;
74 mapping lvs;
75
76 if (!(lv=(int)this_player()->QueryProp(P_GUILD_LEVEL))) return 1;
77 if (!(lvs=QueryProp(P_GUILD_LEVELS))) return 0;
78 return check_cond(lvs[lv+1]); // Bedingung fuer naechsten Level testen.
79}
80
81void adjust_title(object pl) {
82 int lv;
83 mixed ti;
84
85
86 if (!pl ||
87 !(lv=(int)pl->QueryProp(P_GUILD_LEVEL)))
88 return;
89 switch((int)pl->QueryProp(P_GENDER)) {
90 case MALE:
91 ti=QueryProp(P_GUILD_MALE_TITLES);
92 break;
93 case FEMALE:
94 ti=QueryProp(P_GUILD_FEMALE_TITLES);
95 break;
96 default:
97 return;
98 }
99 if (mappingp(ti))
100 ti=ti[lv];
101 if (stringp(ti))
102 pl->SetProp(P_GUILD_TITLE,ti);
103
104 // Spielertitel nullen, damit der Gildentitel angezeigt wird.
105 if (!IS_SEER(pl) && !FAO_HAS_TITLE_GIFT(pl))
106 pl->SetProp(P_TITLE,0);
107}
108
109void do_advance() {
110 int lv;
111
112 lv=this_player()->QueryProp(P_GUILD_LEVEL)+1;
113 if (lv<1) lv=1;
114 this_player()->SetProp(P_GUILD_LEVEL,lv);
115 adjust_title(this_player());
116}
117
118int try_advance() {
119 if (can_advance()) {
120 if (IsGuildMember(this_player()))
121 do_advance();
122 return 1;
123 }
124 return 0;
125}
126
127int beitreten() {
128 string res;
129 int erg;
130
131 if (res=check_restrictions(this_player(),QueryProp(P_GUILD_RESTRICTIONS))) {
132 // Werden die Beitrittsbedingungen erfuellt?
133 printf("Du kannst dieser Gilde nicht beitreten.\nGrund: %s",res);
134 return -3;
135 }
136 if (erg=(int)GUILDMASTER->beitreten()) {
137 if (erg<0)
138 return erg;
139 if (!(this_player()->QueryProp(P_GUILD_LEVEL)))
140 try_advance(); // Level 1 wird sofort vergeben
141 return 1;
142 }
143 return 0;
144}
145
146varargs int austreten(int loss) {
147 return (int)GUILDMASTER->austreten(loss);
148}
149
150int bei_oder_aus_treten(string str) {
151 if (!str) return 0;
152 if (sizeof(regexp(({lower_case(str)}),
153 "\\<aus\\>.*gilde\\>.*\\<aus\\>")))
154 return austreten();
155 if (sizeof(regexp(({lower_case(str)}),
156 "(gilde\\>.*\\<bei\\>|\\<in\\>.*gilde\\>.*\\<ein\\>)")))
157 return beitreten();
158 return 0;
159}
160
161varargs int
162AddSkill(string sname, mapping ski) {
163 mapping skills;
164
165 if (!sname)
166 return 0;
167
168 // per Query() abfragen, hier ist keine Kopie noetig.
169 if (!mappingp(skills=Query(P_GUILD_SKILLS))) {
170 skills=([]);
171 SetProp(P_GUILD_SKILLS,skills);
172 }
173
174 if (!mappingp(ski))
175 ski=([]);
176 else
177 //Zur Sicherheit Kopie erstellen, wer weiss, was der Eintragende noch
178 //mit seinem Mapping macht...
179 ski=deep_copy(ski);
180
181 if (!stringp(ski[SI_SKILLFUNC]))
182 // Wenn keine Funktion angegeben ist, Funktionsname=Skillname
183 ski[SI_SKILLFUNC]=sname;
184
185 // Gilden-Offsets addieren
186 ski=AddSkillMappings(QueryProp(P_GLOBAL_SKILLPROPS),ski);
187
188 // Skill setzen.
189 skills[sname]=ski;
190 //SetProp() unnoetig, da oben per Query() das Originalmapping erhalten
191 //wurde.
192 //SetProp(P_GUILD_SKILLS,skills);
193 return 1;
194}
195
196
197varargs int
198AddSpell(string verb, mapping ski) {
199 mapping skills;
200
201 if (!verb)
202 return 0;
203 if (!mappingp(ski))
204 ski=([]);
205
206 if (!stringp(ski[SI_SPELLBOOK]) &&
207 !stringp(ski[SI_SPELLBOOK]=QueryProp(P_GUILD_DEFAULT_SPELLBOOK)))
208 // Wenn kein Spellbook angegeben ist muss ein
209 // Default-Spellbook angegeben sein, sonst Fehler
210 return 0;
211 if (file_size(SPELLBOOK_DIR+ski[SI_SPELLBOOK]+".c")<0)
212 return 0; // Spellbook sollte auch existieren...
213
214 return AddSkill(lower_case(verb),ski);
215}
216
217mapping QuerySkill(string skill) {
218 mapping ski;
219
220 // Abfrage per Query(), da vielleicht nur ein Skill gewuenscht
221 // wird und daher die komplette Kopie des Spellmappings in der Query-Methode
222 // unnoetig ist.
223 if (!skill
224 || !(ski=Query(P_GUILD_SKILLS, F_VALUE))
225 || !(ski=ski[skill])) // Gildenspezifische Skilleigenschaften
226 return 0;
227 // hier aber dann natuerlich eine Kopie erstellen. ;-)
228 return(deep_copy(ski));
229}
230
231mapping QuerySpell(string spell) {
232 mapping ski,ski2;
233 string spellbook,sfunc;
234
235 // QuerySkill() hier und QuerySpell() im Spellbook liefern Kopien der
236 // Skillmappings zurueck, Kopieren hier daher unnoetig.
237 if (!spell
238 || !(ski=QuerySkill(spell))
239 || !(spellbook=ski[SI_SPELLBOOK]))
240 return 0;
241 if (!(sfunc=ski[SI_SKILLFUNC]))
242 sfunc=spell;
243 spellbook=SPELLBOOK_DIR+spellbook;
244 if (!(ski2=(mapping)(spellbook->QuerySpell(sfunc))))
245 return 0;
246 return AddSkillMappings(ski2,ski); // Reihenfolge wichtig!
247 // Die Gilde kann Spelleigenschaften neu definieren!
248}
249
250varargs int
251UseSpell(object caster, string spell, mapping sinfo) {
252 mapping ski;
253 string spellbook;
254
255 if (!caster
256 || !spell
257 || !(ski=QuerySkill(spell)) // Existiert dieser Spell in dieser Gilde?
258 || !(spellbook=ski[SI_SPELLBOOK])) // Spellbook muss bekannt sein
259 return 0;
260 if (sinfo)
261 ski+=sinfo;
262 spellbook=SPELLBOOK_DIR+spellbook;
263 // printf("%O %O %O %O\n",spellbook,caster,spell,ski);
264 return (int)spellbook->UseSpell(caster,spell,ski);
265}
266
267static int
268InitialSkillAbility(mapping ski, object pl) {
269 if (!ski || !pl) return 0;
270 return (300*GetOffset(SI_SKILLLEARN,ski,pl)+
271 (200*(int)pl->QueryAttribute(A_INT)*
272 GetFactor(SI_SKILLLEARN,ski,pl))/100);
273}
274
275int
276SkillListe(int what) {
277 int res;
278 // Querymethode erstellt Kopie (wichtig!)
279 mapping allskills=QueryProp(P_GUILD_SKILLS);
280 string *skills=({});
281 string *spells=({});
282
283 if (!mappingp(allskills) || !sizeof(allskills))
284 return 0;
285
286 foreach(string s, mapping sdata: &allskills) {
287 // Lernlevel ermitteln und speichern.
288 mapping tmp=QuerySpell(s);
289 // wenn nix gefunden, dann ist es ein Skill, sonst ein Spell.
290 if (tmp) {
291 spells += ({s});
292 // das Spellbook hat im Zweifel das SI_SKILLINFO
293 sdata[SI_SKILLINFO] = tmp[SI_SKILLINFO];
294 }
295 else {
296 // SI_SKILLINFO steht in diesem Fall schon in sdata...
297 tmp = QuerySkill(s);
298 skills += ({s});
299 }
300 // gucken, obs nen Lernlevel gibt und einfach in sdata schreiben
301 if ( (tmp=tmp[SI_SKILLRESTR_LEARN]) )
302 sdata["_restr_level"] = tmp[P_LEVEL];
303 }
304
305 // jetzt sortieren.
306 closure cl = function int (string a, string b)
307 { return allskills[a]["_restr_level"] > allskills[b]["_restr_level"]; };
308 if (what & 0x01)
309 spells = sort_array(spells, cl);
310 if (what & 0x02)
311 skills = sort_array(skills, cl);
312
313 // und ausgeben
314 cl = function void (string *list)
315 {
316 string tmp="";
317 int lvl;
318 foreach(string sp: list) {
319 lvl = allskills[sp]["_restr_level"];
320 if (lvl>0)
321 tmp += sprintf("%-20s %d\n",sp,lvl);
322 else
323 tmp += sprintf("%-20s\n",sp);
324 if (allskills[sp][SI_SKILLINFO])
325 tmp += break_string(allskills[sp][SI_SKILLINFO], 78,
326 " ");
327 }
328 tell_object(PL, tmp);
329 };
330
331 if ((what & 0x01) && sizeof(spells)) {
332 res = 1;
333 tell_object(PL,sprintf(
334 "Du kannst versuchen, folgende Zaubersprueche zu lernen:\n"
335 "%-20s %-3s\n","Zauberspruch","Spielerstufe"));
336 funcall(cl, spells);
337 tell_object(PL,break_string(
338 "Du kannst einen Zauberspruch mit dem Kommando \"lerne\", gefolgt "
339 "vom Zauberspruchnamen lernen.",78));
340 }
341 if ((what & 0x02) && sizeof(skills)) {
342 res = 1;
343 tell_object(PL,sprintf(
344 "Du kannst versuchen, folgende Faehigkeiten zu erwerben:\n"
345 "%-20s %-3s\n","Faehigkeit","Spielerstufe"));
346 funcall(cl, skills);
347 }
348
349 return res;
350}
351
352varargs int
353LearnSpell(string spell,object pl) {
354 mapping ski,restr;
355 string res;
356 mixed learn_initfunc;
357 int abil,diff;
358
359 // Wenn kein pl gesetzt ist, nehmen wir this_player(), das ist der
360 // Normalfall.
361 if (!pl)
362 pl=this_player();
363 if (!IsGuildMember(pl)) {
364 _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
365 return 0;
366 }
367 _notify_fail("Was moechtest Du lernen?\n");
368 if (!spell)
369 return SkillListe(0x01);
370 spell=lower_case(spell);
371 if (!(ski=QuerySpell(spell)))
372 return 0;
373 if (pl->QuerySkill(spell)) {
374 _notify_fail("Du kannst diesen Spruch doch schon!\n");
375 return 0;
376 }
377 if ((restr=ski[SI_SKILLRESTR_LEARN])
378 && (res=check_restrictions(pl,restr))) {
379 printf("Du kannst diesen Spruch noch nicht lernen.\nGrund: %s",res);
380 return 1;
381 }
382 abil=InitialSkillAbility(ski,pl);
383 if (abil<1) abil=1;
384 if (abil>7500) abil=7500;
385 write("Du lernst einen neuen Zauberspruch.\n");
386 if (!(diff=GetFValueO(SI_DIFFICULTY,ski,pl)))
387 diff=GetFValueO(SI_SPELLCOST,ski,pl);
388 pl->ModifySkill(spell,abil,diff);
389 return 1;
390}
391
392int
393LearnSkill(string skill) {
394 mapping ski,restr;
395 object pl;
396 string res;
397 mixed learn_initfunc;
398 int abil,diff;
399
400 if (!IsGuildMember(pl=this_player())) {
401 _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
402 return 0;
403 }
404 _notify_fail("Was moechtest Du lernen?\n");
405 if (!skill)
406 return SkillListe(0x02);
407 skill=capitalize(skill);
408 if (!(ski=QuerySkill(skill)))
409 return 0;
410 if (pl->QuerySkill(skill)) {
411 _notify_fail("Du hast diese Faehigkeit doch schon!\n");
412 return 0;
413 }
414 if ((restr=ski[SI_SKILLRESTR_LEARN])
415 && (res=check_restrictions(pl,restr))) {
416 printf("Du kannst diese Faehigkeit noch nicht erwerben.\nGrund: %s",res);
417 return 1;
418 }
419 abil=InitialSkillAbility(ski,pl);
420 if (!abil) abil=1;
421 if (abil>MAX_ABILITY) abil=MAX_ABILITY;
422 if (abil<-MAX_ABILITY) abil=-MAX_ABILITY;
423 write("Du erwirbst eine neue Faehigkeit.\n");
424 diff=GetFValueO(SI_DIFFICULTY,ski,pl);
425 pl->ModifySkill(skill,abil,diff);
426 return 1;
427}
428
429int GuildRating(object pl)
430{
431 mapping ski;
432 string *sk;
433 int i, cnt, sum;
434 closure qsa;
435
436 if (!IsGuildMember(pl)||!query_once_interactive(pl))
437 return 0;
438
439 if (!(ski = QueryProp(P_GUILD_SKILLS)) ||
440 !(qsa = symbol_function("QuerySkillAbility",pl)))
441 return 0;
442
443 sk = m_indices(ski);
444 cnt = sizeof(ski);
445
446 for (i=cnt-1, sum=0; i>=0; i--)
447 sum += funcall(qsa, sk[i]);
448
449 sum = sum/cnt;
450 if (sum < 0)
451 sum = 0;
452 else if (sum > MAX_ABILITY)
453 sum = MAX_ABILITY;
454
455 return (int)pl->SetProp(P_GUILD_RATING, sum);
456}
457
458// Wird von /std/player/quest.c aufgerufen, wenn Quest geloest.
459// (Jedes mal - nicht nur beim ersten mal.)
460// Ist zum selbst-ueberschreiben gedacht, sollte aber definiert sein.
461void NotifyGiveQuest(object pl, string key){ }
462