blob: 503da97d4b401876ad36ebd2f69be4fb9560cd52 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/skills.c -- Spielerskills
4//
5// $Id: skills.c 8809 2014-05-08 19:52:48Z Zesstra $
6
7//
8// 2003-01-20: Nun Zooks Baustelle
9//
10#pragma strong_types
11#pragma save_types
12#pragma range_check
13#pragma no_clone
14#pragma pedantic
15
16inherit "/std/living/skills";
17
18#include <combat.h>
19#include <new_skills.h>
20#include <properties.h>
21#include <break_string.h>
22#include <wizlevels.h>
23
24#define NEED_PROTOTYPES
25#include <player/base.h>
26#include <player/gmcp.h>
27#undef NEED_PROTOTYPES
28
29// Dieses Mapping speichert die deaktivierten Skills der einzelnen Gilden
30// Diese werden in den Gilden ueber P_GUILD_DEACTIVATED_SKILL gesetzt.
31nosave mapping deactivated_skills = ([]);
32
33// Flag fuer den Kompatibilitaetsmodus des Kampfs (Emulation von
34// 2s-Alarmzeit). Wenn != 0 speichert das Flag gleichzeitig die Zeit des
35// letzten Heartbeats. Auf diese Zeit wird der Startpunkt eines Spellfatigues
36// ggf. zurueckdatiert. (max. eine Sekunde)
37int spell_fatigue_compat_mode;
38
39// Ein create() fuer das Mapping
40
41protected void create()
42{
43 mapping act;
44
45 ::create();
46
47 // Wir holen die Gilden aus dem Gildenmaster
48 foreach(string guild:
49 (string *)call_other(GUILDMASTER,"QueryProp",P_VALID_GUILDS))
50 {
51 if(catch(act=call_other("/gilden/"+guild,"QueryProp",
52 P_GUILD_DEACTIVATE_SKILLS); publish ))
53 log_file("WEAPON_SKILLS", sprintf ("%s: Gilde nicht ladbar: "
54 +"TP: %O, TI: %O, PO: %O, Gilde: %s\n", dtime(time()),
55 this_player(), this_interactive(), previous_object(), guild));
56 else if (act) // wenn act, ins Mapping aufnehmen.
57 deactivated_skills+=([guild:act]);
58 }
59 // keine echte Prop mehr, Save-Modus unnoetig.
60 Set(P_NEXT_SPELL_TIME,SAVE,F_MODE_AD);
61
62 Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
63}
64
65// Das Mapping kann man auch abfragen
66
67public mapping GetDeactivatedSkills()
68{
69 return copy(deactivated_skills);
70}
71
72// Funktion, die sagt ob ein ANY-Skill deaktiviert ist.
73public int is_deactivated_skill(string sname,string guild)
74{
75 if (deactivated_skills[guild])
76 return deactivated_skills[guild][sname];
77 return 0;
78}
79
80
81// Funktion fuer die Waffenskills
82// traegt die allg. Waffenskills ein. Wird ggf. von FixSkills() gerufen.
83// (Das Eintragen bedeutet nicht, dass die aktiv sind! Aber bei Gildenwechsel
84// werden sie nicht eingetragen).
85private void set_weapon_skills() {
86
87 if (QuerySkillAbility(FIGHT(WT_SWORD))<=0)
88 ModifySkill(FIGHT(WT_SWORD),([SI_SKILLABILITY:0]),150,"ANY");
89 if (QuerySkillAbility(FIGHT(WT_AXE))<=0)
90 ModifySkill(FIGHT(WT_AXE),([SI_SKILLABILITY:0]),150,"ANY");
91 if (QuerySkillAbility(FIGHT(WT_SPEAR))<=0)
92 ModifySkill(FIGHT(WT_SPEAR),([SI_SKILLABILITY:0]),150,"ANY");
93 if (QuerySkillAbility(FIGHT(WT_WHIP))<=0)
94 ModifySkill(FIGHT(WT_WHIP),([SI_SKILLABILITY:0]),150,"ANY");
95 if (QuerySkillAbility(FIGHT(WT_KNIFE))<=0)
96 ModifySkill(FIGHT(WT_KNIFE),([SI_SKILLABILITY:0]),150,"ANY");
97 if (QuerySkillAbility(FIGHT(WT_CLUB))<=0)
98 ModifySkill(FIGHT(WT_CLUB),([SI_SKILLABILITY:0]),150,"ANY");
99 if (QuerySkillAbility(FIGHT(WT_STAFF))<=0)
100 ModifySkill(FIGHT(WT_STAFF),([SI_SKILLABILITY:0]),150,"ANY");
101}
102
103// initialisiert die Skills fuer Spieler (momentan: allg. Waffenskills setzen
104// und P_SKILLS_VERSION)
105protected void InitSkills() {
106 mapping ski;
107 // schonmal initialisiert?
108 if (mappingp(ski=Query(P_NEWSKILLS, F_VALUE)) && sizeof(ski))
109 return;
110
111 // allg. Waffenskills aktivieren
112 set_weapon_skills();
113
114 // Version setzen
115 SetProp(P_SKILLSVERSION, CURRENT_SKILL_VERSION);
116 Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
117}
118
119// Updated Skills aus Version 0 und 1 heraus.
120private void FixSkillV1(string skillname, mixed sinfo) {
121 // alte Skills auf mappings normieren
122 if (intp(sinfo)) {
123 sinfo = ([SI_SKILLABILITY: sinfo ]);
124 }
125 // Eine Reihe von Daten werden geloescht, da die Daten aus der
126 // Gilde/Spellbook frisch kommen sollten und sie nicht spieler-individuell
127 // sind: SI_CLOSURE (wird onthefly korrekt neu erzeugt), SI_SKILLARG,
128 // SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS, SI_DISTANCE, SI_WIDTH, SI_DEPTH,
129 // SI_TESTFLAG, SI_ENEMY, SI_FRIEND.
130 // Ausserdem sind alle SP_* im toplevel falsch, die muessen a) in SI_SPELL
131 // und sollten b) nicht im Spieler gespeichert werden, sondern von
132 // Gilden/Spellbook jeweils frisch kommen.
133 // all dieses Zeug landete in alten Spielern im Savefile.
134 if (mappingp(sinfo))
135 sinfo -= ([SI_CLOSURE, SI_SKILLARG, SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS,
136 SI_DISTANCE, SI_WIDTH, SI_DEPTH, SI_TESTFLAG, SI_ENEMY,
137 SI_FRIEND, SP_NAME, SP_SHOW_DAMAGE, SP_REDUCE_ARMOUR,
138 SP_PHYSICAL_ATTACK, SP_RECURSIVE, SP_NO_ENEMY,
139 SP_NO_ACTIVE_DEFENSE, SP_GLOBAL_ATTACK ]);
140 else
141 {
142 tell_object(this_object(),sprintf(
143 "\n**** ACHTUNG - FEHLER ***\n"
144 "Deine Skills enthalten einen defekten Skill %O:\n"
145 "Bitte lass dies von einem Erzmagier ueberpruefen.\n",
146 skillname));
147 }
148}
149
150// Updatet und repariert ggf. Skillmappings in Spielern
151protected void FixSkills() {
152
153 // nur bei genug rechenzeit loslegen
154 if (get_eval_cost() < 750000) {
155 call_out(#'FixSkills, 1);
156 return;
157 }
158 // wenn gar keine Skills da (?): InitSkills() rufen.
159 mapping allskills = Query(P_NEWSKILLS, F_VALUE);
160 if (!mappingp(allskills) || !sizeof(allskills)) {
161 InitSkills();
162 return;
163 }
164
165 // Die Fallthroughs in diesem switch sind voll Absicht!
166 switch(QueryProp(P_SKILLSVERSION)) {
167 // bei Version 0 und 1 das gleiche tun
168 case 0: // von 0 auf 1
169 case 1: // von 1 auf 2
170 foreach(string gilde, mapping skills: allskills) {
171 if (!stringp(gilde)) {
172 // sollte nicht vorkommen - tat aber... *seufz*
173 m_delete(skills, gilde);
174 continue;
175 }
176 walk_mapping(skills, #'FixSkillV1);
177 }
178 // allg. Waffenskills aktivieren, einige alte Spieler haben die noch
179 // nicht.
180 set_weapon_skills();
181 // Speicherflag fuer die Versionsprop muss noch gesetzt werden.
182 Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
183 // Version ist jetzt 2.
184 SetProp(P_SKILLSVERSION, 2);
185 // Fall-through
186 case 2:
187 // gibt es noch nicht, nichts machen.
188 //SetProp(P_SKILLSVERSION, 3);
189 // Fall-through, ausser es sind zuwenig Ticks da!
190 if (get_eval_cost() < 750000)
191 break;
192 }
193 // Falls noch nicht auf der aktuellen Version angekommen, neuer callout
194 if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION)
195 call_out(#'FixSkills, 2);
196}
197
198protected void updates_after_restore(int newflag) {
199 //Allgemeine Waffenskills aktivieren, wenn noetig
200 // Wird nun von InitSkills bzw. FixSkills uebernommen, falls noetig.
201 if (newflag) {
202 InitSkills();
203 }
204 else if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION) {
205 // Falls noetig, Skills fixen/updaten. *grummel*
206 FixSkills();
207 }
208 // Prop gibt es nicht mehr. SAVE-Status loeschen.
209 Set(P_GUILD_PREVENTS_RACESKILL,SAVE,F_MODE_AD);
210}
211
212// Standardisierte Nahkampf-Funktion fuer alle Nahkampf-Waffenarten
213protected mapping ShortRangeSkill(object me, string sname, mapping sinfo)
214{
215 int val, w;
216 object enemy;
217
218 if (!mappingp(sinfo) || !objectp(sinfo[P_WEAPON]))
219 return 0;
220
221 w = ([WT_KNIFE : 8,
222 WT_SWORD : 5,
223 WT_AXE : 4,
224 WT_SPEAR : 6,
225 WT_CLUB : 1,
226 WT_WHIP : 9,
227 WT_STAFF : 7])[sinfo[P_WEAPON]->QueryProp(P_WEAPON_TYPE)];
228
229
230 val = sinfo[SI_SKILLABILITY]*(sinfo[P_WEAPON]->QueryProp(P_WC)*
231 (w*QueryAttribute(A_DEX)+
232 (10-w)*QueryAttribute(A_STR))/700)
233 /MAX_ABILITY;
234
235 if (val > 85) {
236 log_file("WEAPON_SKILLS", sprintf("%s: Zu hoher Schaden von: "
237 +"TO: %O, TI: %O, PO: %O, val: %d, A_DEX: %d, A_STR: %d, "
238 +"P_WEAPON: %O, P_WC: %d\n", dtime(time()),
239 this_object(), this_interactive(),
240 previous_object(), val,
241 QueryAttribute(A_DEX),
242 QueryAttribute(A_STR), sinfo[P_WEAPON],
243 sinfo[P_WEAPON]->QueryProp(P_WC)));
244 val = 85;
245 }
246
247 /*
248 Der zusätzliche Schaden der allgemeinen Waffenskills berechnet
249 sich wie folgt:
250
251 sinfo[SI_SKILLABILITY)* (P_WC * ( X ) / 800) / MAX_ABILITY
252
253 Dabei beruecksichtigt X je nach Waffentyp in unterschiedlicher
254 Gewichtung die Werte fuer Geschicklichkeit und Staerke.
255
256 X ==
257
258 Messer : 8*A_DEX + 2*A_STR
259 Schwert : 5*A_DEX + 5*A_STR
260 Axt : 4*A_DEX + 6*A_STR
261 Speer : 6*A_DEX + 4*A_STR
262 Keule : 1*A_DEX + 9*A_STR
263 Peitsche : 9*A_DEX + 1*A_STR
264 */
265
266 sinfo[SI_SKILLDAMAGE]+=val;
267
268
269 /* Lernen: Wird immer schwieriger, nur bei jedem 20. Schlag im Schnitt,
270 * und nur dann, wenn der Gegner auch XP gibt. */
271 if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && !random(10))
272 {
273 enemy=sinfo[SI_ENEMY];
274 if (objectp(enemy) && (enemy->QueryProp(P_XP)>0))
275 {
276 object ausbilder;
277 // log_file("humni/log_wurm","Haut: %s und zwar %s, mit xp %d\n",geteuid(this_object()),to_string(enemy),enemy->QueryProp(P_XP));
278 LearnSkill(sname, random(5), 150);
279 // Gibt es einen Ausbilder?
280 if (QueryProp(P_WEAPON_TEACHER) &&
281 ausbilder=find_player(QueryProp(P_WEAPON_TEACHER)))
282 {
283 // Ist der Ausbilder anwesend?
284 if (present(ausbilder,environment()))
285 {
286 // Ausbilder und Azubi muessen dieselbe Waffe haben.
287 //string wt_aus,wt_azu;
288 object waf_aus,waf_azu;
289
290 waf_azu=QueryProp(P_WEAPON);
291 waf_aus=call_other(ausbilder,"QueryProp",P_WEAPON);
292
293 //wt_azu=call_other(waf_azu,"QueryProp",P_WEAPON_TYPE);
294 //wt_aus=call_other(waf_aus,"QueryProp",P_WEAPON_TYPE);
295 //if (wt_azu==wt_aus)
296 if (objectp(waf_aus) && objectp(waf_azu) &&
297 (string)waf_aus->QueryProp(P_WEAPON_TYPE)
298 == (string)waf_azu->QueryProp(P_WEAPON_TYPE))
299 {
300 // Bonus von bis zu 5 Punkten
301 //log_file("humni/log_azubi",
302 // sprintf("Azubi %O und Ausbilder %O : Waffentypen %s und %s, gelernt\n",this_object(),
303 // ausbilder, wt_azu, wt_aus));
304 LearnSkill(sname,random(6),150);
305 }
306 }
307 }
308 }
309 }
310
311 /*
312 Die Schwierigkeit liegt bei 150, so dass
313 ein Lvl. 1 Spieler maximal 15% Skill
314 usw...
315 lernen kann. (Genaue Tabelle in /std/living/skills bei LimitAbility)
316 */
317
318 return sinfo;
319}
320
321
322// Standardisierte Fernkampf-Funktion fuer alle Fernkampf-Waffenarten
323
324// *** noch deaktiviert ***
325
326protected mapping LongRangeSkill(object me, string sname, mapping sinfo, int dam)
327{ int abil,val;
328
329 if (!mappingp(sinfo) || !dam || !objectp(sinfo[P_WEAPON]) ||
330 (sinfo[P_WEAPON]->QueryProp(P_SHOOTING_WC))<5)
331 return 0;
332
333 abil=sinfo[SI_SKILLABILITY]+sinfo[OFFSET(SI_SKILLABILITY)];
334 val=dam*abil/MAX_ABILITY;
335 val=val/2+random(val/2+1);
336 val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
337 sinfo[SI_SKILLDAMAGE]+=val;
338
339 if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && random(50)==42)
340 LearnSkill(sname, 1, 150);
341
342 return sinfo;
343}
344
345
346
347// Die einzelnen Waffenskills rufen dann nur die Standard-Funktion auf.
348
349protected mapping StdSkill_Fight_axe(object me, string sname, mapping sinfo)
350{
351 return ShortRangeSkill(me, sname, sinfo);
352}
353
354protected mapping StdSkill_Fight_club(object me, string sname, mapping sinfo)
355{
356 return ShortRangeSkill(me, sname, sinfo);
357}
358
359protected mapping StdSkill_Fight_knife(object me, string sname, mapping sinfo)
360{
361 return ShortRangeSkill(me, sname, sinfo);
362}
363
364protected mapping StdSkill_Fight_spear(object me, string sname, mapping sinfo)
365{
366 return ShortRangeSkill(me, sname, sinfo);
367}
368
369protected mapping StdSkill_Fight_sword(object me, string sname, mapping sinfo)
370{
371 return ShortRangeSkill(me, sname, sinfo);
372}
373
374protected mapping StdSkill_Fight_whip(object me, string sname, mapping sinfo)
375{
376 return ShortRangeSkill(me, sname, sinfo);
377}
378
379protected mapping StdSkill_Fight_staff(object me, string sname, mapping sinfo)
380{
381 return ShortRangeSkill(me, sname, sinfo);
382}
383
384
385
386
387// Die Fernwaffenskills sind Munitionsabhaengig
388
389// *** noch deaktiviert ***
390
391protected mapping StdSkill_Shoot_arrow(object me, string sname, mapping sinfo)
392{
393 return LongRangeSkill(me, sname, sinfo, 40);
394}
395
396protected mapping StdSkill_Shoot_bolt(object me, string sname, mapping sinfo)
397{
398 return LongRangeSkill(me, sname, sinfo, 40);
399}
400
401protected mapping StdSkill_Shoot_dart(object me, string sname, mapping sinfo)
402{
403 return LongRangeSkill(me, sname, sinfo, 20);
404}
405
406protected mapping StdSkill_Shoot_stone(object me, string sname, mapping sinfo)
407{
408 return LongRangeSkill(me, sname, sinfo, 40);
409}
410
411protected mixed _query_localcmds() {
412 return ({ ({"spruchermuedung","enable_spell_fatigue_compat",0,0}) });
413}
414
415
416
417// *** Kompatibilitaetsmodus fuer Spellfatigues (Emulation 2s-Alarmzeit) ***
418
419/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
420 * Ist hier nur fuer den Spellfatigue-Compat-mode.
421 * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
422 * Rueckgabewert: Ablaufzeit der gesetzten Sperre
423 -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
424 0, wenn duration 0 ist.
425 */
426public varargs int SetSpellFatigue(int duration, string key) {
427
428 // 0 sollte nie eine Sperre bewirken, auch nicht im compat mode.
429 if (!duration) return 0;
430
431 if (spell_fatigue_compat_mode) {
432 // Spell-Fatigues auf HBs synchronisieren (2s-Alarmzeit-Emulation).
433 // Aufrunden auf ganzzahlige Vielfache von __HEART_BEAT_INTERVAL__
434 if (duration % __HEART_BEAT_INTERVAL__)
435 ++duration;
436
437 // Startpunkt des Delay soll Beginn der aktuellen Kampfrunde (HB-Zyklus)
438 // sein, ggf. um max. eine Sekunde zurueckdatieren.
439 // (spell_fatigue_compat_mode hat die Zeit des letzten HB-Aufrufs)
440 // Falls durch irgendein Problem (z.B. sehr hohe Last), der letzte HB
441 // laenger als 1s zurueckliegt, funktioniert das natuerlich nicht, aber
442 // bei fatigue+=spell_fatigue_compat_mode kann der Spieler zuviel Zeit
443 // einsparen.
444 if (time() > spell_fatigue_compat_mode)
445 --duration; //1s zurueckdatieren
446 }
447
448 return ::SetSpellFatigue(duration, key);
449}
450
451/** Befehlsfunktion fuer Spieler um den Spellfatigue-Kompatibilitaetsmodus
452 * umzuschalten.
453 */
454public int enable_spell_fatigue_compat(string cmd) {
455 if (QueryProp(P_LAST_COMBAT_TIME) + 600 > time()) {
456 write(break_string(
457 "Im Kampf oder kurz nach einem Kampf kannst Du nicht zwischen "
458 "alter und neuer Spruchermuedung umschalten.\n"
459 "Momentan benutzt Du die "
460 + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
461 + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
462 return 1;
463 }
464
465 if (cmd=="alt") {
466 spell_fatigue_compat_mode=time();
467 write(break_string(
468 "Alte Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
469 "zwischen Spruechen werden auf Vielfache von 2s aufgerundet und "
470 "beginnen in der Regel am Anfang Deiner Kampfrunde."));
471 return 1;
472 }
473 else if (cmd=="neu" || cmd=="normal") {
474 spell_fatigue_compat_mode=0;
475 write(break_string(
476 "Normale Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
477 "zwischen Spruechen werden sekundengenau berechnet."));
478 return 1;
479 }
480
481 notify_fail(break_string(
482 "Moechtest Du die alte oder die neue Spruchermuedung?\n"
483 "Momentan benutzt Du die "
484 + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
485 + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
486 return 0;
487}
488
489/** Speichert die Zeit des letztes Heartbeats.
490 */
491protected void heart_beat() {
492 if (spell_fatigue_compat_mode)
493 spell_fatigue_compat_mode = time();
494}
495
496static int _set_guild_level(int num)
497{ string gilde;
498 mapping levels;
499
500 if ( !(gilde=QueryProp(P_GUILD)) )
501 return 0;
502
503 if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
504 levels=([]);
505
506 levels[gilde]=num;
507 Set(P_GUILD_LEVEL,levels);
508 GMCP_Char( ([P_GUILD_LEVEL: num]) );
509
510 return num;
511}
512
513static int _query_guild_level()
514{ string gilde;
515 mapping levels;
516
517 if ( !(gilde=QueryProp(P_GUILD)) )
518 return 0;
519
520 if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
521 return 0;
522
523 return levels[gilde];
524}
525
526static string _set_guild_title(string t)
527{ string gilde;
528 mapping titles;
529
530 if ( !(gilde=QueryProp(P_GUILD)) )
531 return 0;
532
533 if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
534 titles=([]);
535
536 titles[gilde]=t;
537 Set(P_GUILD_TITLE,titles);
538 GMCP_Char( ([P_GUILD_TITLE: t]) );
539 return t;
540}
541
542static string _query_guild_title()
543{ string gilde,t;
544 object g;
545 mapping titles;
546
547 if ( !(gilde=QueryProp(P_GUILD)) )
548 return 0;
549
550 if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
551 titles=([]);
552
553 t=titles[gilde];
554 if ( !t && query_once_interactive(this_object())
555 && objectp(g=find_object("/gilden/"+gilde)) )
556 {
557 g->adjust_title(this_object());
558 SetProp(P_TITLE,0);
559
560 if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
561 return 0;
562
563 t=titles[gilde];
564 }
565
566 return t;
567}
568
569
570static string _set_guild(string gildenname)
571{ object pre;
572
573 if (!objectp(pre=previous_object()))
574 return 0;
575
576 if ( pre!=this_object() // Das Lebewesen selber darf die Gilde setzen,
577 && object_name(pre)!=GUILDMASTER // der Gildenmaster auch
578 && (!this_player()
579 || this_player() != this_interactive()
580 || !IS_ARCH(this_player())
581 )
582 )
583 return 0;
584
585 Set(P_GUILD,gildenname);
586 GMCP_Char( ([P_GUILD: gildenname]) );
587 return gildenname;
588}
589
590static string _query_guild()
591{ string res;
592
593 if ( !(res=Query(P_GUILD)) && query_once_interactive(this_object()) )
594 {
595 // Spieler, die keiner Gilde angehoeren, gehoeren zur Abenteurergilde
596 if ( !(res=QueryProp(P_DEFAULT_GUILD)) )
597 return DEFAULT_GUILD;
598 else
599 Set(P_GUILD,res);
600 return res;
601 }
602
603 return res;
604}
605
606static string _query_title()
607{ string ti;
608
609 if ( stringp(ti=Query(P_TITLE)) )
610 return ti;
611
612 return QueryProp(P_GUILD_TITLE);
613}
614
615static string _set_title(string t)
616{
617 Set(P_TITLE, t, F_VALUE);
618 GMCP_Char( ([P_TITLE: t]) );
619 return t;
620}
621