$Id: klass.html,v 1.3 2001/10/08 02:29:15 hpa Exp $

Att skapa en ny klass

Jag blev tillfrågad häromdagen om hur man skriver ett rum där man kan se folk i intilliggade rum. Jag funderade lite på saken och tänkte att det kanske vore värt att skriva ihop som exempel på hur och varför man skapar en ny klass.

Varför en ny klass?

Det är troligt att man vill ha flera sådana "genomskinliga" rum. När man skapar flera stycken av samma sak är det ofta värt att göra det som en klass. I det här fallet vet vi att vi kommer att behöva minst två, eftersom det ju lär finnas minst två rum man kan se emellan...

Skapa en klass

En klass i MOO är helt enkelt ett objekt. MOO stöder tyvärr inte flerfaldigt föräldrarskap (multiple inheritance, a.k.a. MI), så man får tänka sig extra noga för vad man väljer som basklass. I det här fallet verkar det lämpligt att använda $detailed_room som basklass, i och med att man säkert vill kunna sätta detaljer på ett sådant genomskinligt rum:

@create $detailed_room named "Generic Transparent Room"
You now have Generic Transparent Room with object number #336 and parent Generic Detailed Room (#312).

Det känns lite skumt att gå runt och bära på ett rum som inte ens finns på riktigt, så vi teleporterar bort det till ingenstans:

@move #336 to $nothing
You teleport Generic Transparent Room.

Vi har nu en ny klass, som går att använda som sådan. Vår nya klass gör ingenting nytt som inte dess basklass ($detailed_room) redan gör, men det ska vi snart ändra på.

Grundläggande manövrer

Innan vi börjar ändra beskrivningar och sådant, är det värt att fundera på vad vi egentligen kommer att behöva. En sådan sak är att kunna beskriva alla karaktärer som finns i det andra rummet. Vi definierar därför ett nytt verb som gör just det:
@verb #336:characters_here this none this
@edit #336:characters_here

"this none this" betyder bara att detta är en subrutin som inte kan användas direkt av spelaren; jag har också använt som konvention att programmeringsgrejer är på engelska.

Nu definierar vi själva verbkoden, som returnerar en lista av alla $characterer i rummet, d.v.s. alla spelare och SLPer:

"Returns the list of all $characters that are visible in this room.";
c = {};
for t in (this:contents())
  if ($object_utils:isa(t, $character))
    c = setadd(c, t);
  endif
endfor
return c;

Vi använder this:contents() i stället för this.contents utifall det finns dolda objekt av något slag. $object_utils:isa() svarar på frågan "är detta ett objekt av klass $character eller någon av dess underklasser?"

Nu är det värt att skapa ett rum och testa vad vi gjort hittills:

@dig "Testrum 1"
Testrum 1 (#337) created.
@move me to #337
Testrum 1
You see nothing special.
@chparent here to #336
Parent changed.
; here:characters_here()
=> {#101}
[used 69 ticks, 0 seconds.]
; me
=> #101 (Zytor)
[used 1 tick, 0 seconds.]

Hittills verkar saker och ting fungera.

Alice i spegellandet

Nu vill vi kunna skriva meddelanden av följande typ:
På andra sidan spegeln står Alice och den vita drottningen.
... där (får man anta) Alice är en spelare och den vita drottningen är en SLP. I vilket fall som helst så är det väsentligt att utskriften i rummet intill är annorlunda än utskriften i själva rummet, som ju antas säga att Alice och den vita drottningen står här.

Vi skapar en lista på rum man vill kunna se andra personer i, och sätter den ursprungligen till blank:

@prop #336.visible_rooms {} rc
Property added with value {}.

rc betyder att är denna property läsbar av alla och ägs av den som äger rummet.

När vi definierar en property på #336 skapas den automatiskt på alla dess ättlingar (inklusive rummet #337 vi precis skapade) med ett "transparent" (clear) värde -- d.v.s. samma som dess basklass.

Vi skapar också en lista med meddelanden som vi vill ha för varje rum vi kan se in i:

@prop #336.visible_rooms_text {} rc
Property added with value {}.

Nu skapar vi en subrutin som ger oss texten för vad som finns i de andra rummen:

@verb #336:look_other_rooms this none this
Verb added (2).
@edit #336:look_other_rooms
i = 0;
for r in (this.visible_rooms)
  i = i + 1;
  w = r:characters_here();
  if (w)
    player:tell(this.visible_rooms_text[i] + $string_utils:title_list(w) + ".");
  endif
endfor

Här, i håller reda på platsen i .visible_rooms-listan, så vi kan hitta motsvarande meddelande i .visible_rooms_text-listan. Observera att vi anropar :characters_here() på det andra rummet, och inte på oss själva; det är ju det andra rummet vi bryr oss om...

Nu, för att få meddelandena att höra till beskrivningen, tar vi och modifierar verbet :look_self, som är det verb som genererar beskrivningarna av ungefär vad som helst:

@verb #336:look_self this none this
Verb added (3).
@edit #336:look_self
{?brief = 0} = args;
pass(@args);
if (!brief)
  this:look_other_rooms();
endif

Här, brief är ett extra argument som man kan ge för att få en beskrivning i kort form. Vi vill inte lägga till för mycket i kort form, så vi undviker att skriva något extra i kort form.

pass(@args); betyder gör vad min basklass skulle ha gjort, vilket ju betyder skriv ut den vanliga beskrivningen.

Nu är det dags att skapa ett nytt rum, så vi kan prova vad vi gjort hittills:

@dig "genom spegeln,in i spegeln,genom,in" to "Bakom spegeln"
Bakom spegeln (#338) created.
Exit from Testrum 1 (#337) to Bakom spegeln (#338) via {"genom spegeln", "in i spegeln", "genom", "in"} created with id #339.
@chparent #338 to #336
Parent changed.
Nu sätter vi properties på detta rum så man kan se folk bakom spegeln:
; here.visible_rooms = {#338}
=> {#338}
[used 3 ticks, 0 seconds.]
; here.visible_rooms_text = {"På andra sidan spegeln står "}
=> {"På andra sidan spegeln står "}
[used 3 ticks, 0 seconds.]
Nu ber vi en annan spelare, Garnulf, hoppa över till #338, och kollar beskrivningen:
se
Testrum 1
This is an advanced version of an object first designed by Xorian. In a detailed room, you can create details, for example a description of the ceiling, which are not immediately visible to the visitor, but which can be seen with 'look ceiling'. There are instructions for it in Frand's Big Manual, in the Library.
På andra sidan spegeln står Garnulf den allsmäktige.

Beskrivningen är standardbeskrivingen från $detailed_room, med vårt tillägg. Vi fixar standardbeskrivningen härnäst.

Gör klassen allmänt tillgänglig

Nu när vi har en fungerande klass, är det värt att göra den tillgänglig för andra att kunna använda. Det gör vi med kommandot @chmod:
@chmod #336 rf
Object permissions set to "rf".
r betyder att andra spelare kan se vilka verb och properties som har definierats i denna klass; f betyder att andra spelare får använda klassen som basklass.

Slutligen är det värt att sätta en beskrivning på klassen som förklarar hur den används:

@edit #336
Denna klass låter dig skapa rum där man kan se spelare i intilliggande rum, som om de har glasdörrar eller öppen terräng. Följande properties kontrollerar detta:

    .visible_rooms      - lista på rum som man kan se karaktärer i;
    .visible_rooms_text - lista på text som ska föregå listan på karaktärerna.

Exempel:

; here.visible_rooms = {#338}
; here.visible_rooms_text = {"Bakom spegeln står "}