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