Struktur eines EL – Projekts

Bild: Computer

Die Struktur eines Efferdland Projektes unterscheidet sich von diversen anderen MUDs. Deshalb ist es für wechselwillige Magier aus anderen MUDs manchmal etwas schwierig, sich anfangs im Efferdland zurecht zu finden.

Wenn man kein eigenes Home-Mud zur Verfügung hat, kann man auch direkt im Efferdland Programmieren. Hierzu gibt es die Region /d/work/. Ein Regionsmagier legt Dir dort Dein Projekt Verzeichnis an und gibt Dir die nötigen Schreibrechte. Deine Files kannst Du dann mit einem FTP-Programm übertragen. Der FTP Port des Efferdlands lautet ‘6666’.

In Deinem neuen Arbeitsverzeichnis /d/work/<mein projekt>/ findest Du die Unterverzeichnisse für Objekte ( obj/ ), Monster ( npc/ ) und Räume ( room/ ).

Waffen und Rüstungen liegen aber nicht dort, sondern befinden sich im Efferdland in /p/armour/ bzw. /p/weapon/. Auf unser Beispiel Projekt bezogen wären das

  1. /p/armour/work/<mein_projekt>/ für Deine Rüstungen
  2. /p/weapon/work/<mein_projekt>/ für Deine Waffen.

Im Efferdland benutzen alle Magier die selben Header-Files, um für alle identische Defines für die Pfade zu erhalten. Das macht die spätere Fehlersuche einfacher und man kann ganze Projekte ohne größere Anpassungen in andere Regionen verschieben.

Dafür legt man sich in seinem Wurzelverzeichnis in /d/work/<mein_projekt>/ eine Datei def.h an (wenn das der zuständige Regionsmagier nicht schon alles für Dich erledigt hat) und bindet die ../region.h ein. Zusätzlich muss man in def.h noch ein paar Anpassungen machen. Z.B. musst Du Deinen Magiernamen eintragen und den Verzeichnisnamen Deines Projekts. In unserem Beispiel wäre das ‘mein_projekt’.

Beispiel für die def.h:

#ifndef __MEIN_PROJEKT_H__
#define __MEIN_PROJEKT_H__

#define MAGIERNAME "manwe"
#define GEBIET     "mein_project"

#include "../region.h"

#endif

Nun hast Du folgende Defines zur Verfügung:

  1. WEAPON : Waffen in /p/weapon/work/<mein_projekt>/
  2. ARMOUR : Rüstungen in /p/armour/work/<mein_projekct>/
  3. NPC : Monster in /d/work/<mein_projekt>/npc/
  4. OBJ: Objekte in /d/work/<mein_projekt>/obj/
  5. ROOM : In /d/work/<mein_projekt>/room/

Willst du jetzt z.b. ein NPC in Deinen Raum einbinden, geht das wie folgt:

#include "../def.h"

inherit "/std/room";

protected void create() {
  ::create();
  SetProp(P_INT_SHORT, "Ein Raum");
  SetProp(P_INT_LONG,  "Ein Raum.");

  // mein_monster.c aus /d/work/mein_projekt/npc/
  AddItem(NPC "mein_monster", REFRESH_DESTRUCT);
}

Viele Magier binden auch einfach diverse System-Defines in ihr def.h ein, damit sie das nicht jedes mal in ihr Programm schreiben müssen. Da kann man sich drüber streiten, ob das gut oder schlecht ist, das sollte jeder aber für sich entscheiden. Das sähe dann so aus:

#ifndef __MEIN_PROJEKT_H__
#define __MEIN_PROJEKT_H__

#define MAGIERNAME "manwe"
#define GEBIET     "mein_projekt"

#include "../region.h"

#include <v_compiler.h>
#include <properties.h>
#include <moving.h>
#include <jewel.h>
#include <kneipe.h>
#include <bank.h>
#include <quest.h>
#include <mudmaster.h>
#include <sequence.h>
#include <living/bskills.h>

#endif

Zum Schluss erwähne ich hier noch einem die Standard Files, die viele Magier anlegen. Das kann beispielsweise ein Standard Monster oder ein Standard Raum sein. In solchen Standard Files werden oftmals Dinge eingetragen, die sich ständig wiederholen, um sie nicht doppelt und dreifach schreiben zu müssen.

Im Efferdland ist es wichtig, dass solche Standard Files in einem Unterverzeichnis std/ liegen. Für STD-Räume ist das wichtig, damit sie sich nicht in die Efferdland-Karte eintragen, für STD-Monster, damit ihnen kein Erstkill zugewiesen wird.

Beispiel für einen Raum in /d/work/mein_projekt/room/

Man legt zuerst eine neues Verzeichnis an. Für einen Raum wäre das /p/work/<mein_projekt>/room/std/

In diesem Verzeichnis erstellt man dann seinen STD-Raum, in unserem Fall /p/work/<mein_projekt>/room/std/raum_std.c

#include ../../def.h"

inherit "/std/room"; // Dies ist ein STD-Raum, den die
                     // Mudlib zur Verfügung stellt

protected void create() {
  ::create();
  SetProp(P_INT_SHORT,"Ein STD-Raum");
  SetProp(P_INT_LONG, "Ein STD-Raum");

  AddDetail(({"boden","fussboden"}),
  "Der Boden ist unter deinen Fuessen.");
}

Dann schreibt man seinen Raum in /d/work/mein_project/room/raum.c

#include "../def.h"

inherit ROOM "std/raum_std";  // Hier binden wir den STD-
                              // Raum ein.

protected void create() {
  ::create();
  SetProp(P_INT_SHORT,"Ein Raum");
  SetProp(P_INT_LONG, "Ein Raum.");
}

Unser raum.c in /d/work/mein_project/room/ besitzt jetzt auch das Detail ‘boden’ und ‘fussboden’, so dass dieses nicht ständig neu mit dem selben Wortlaut in raum.c geschrieben werden muss. Sowas sollte man aber nicht zu oft machen, da dann Deine Gegend schnell langweilig wird.

NPC Firstkill Nummern

Bild: Cyberworld

Seit kurzem besitzt jeder (metzelbare) NPC eine eindeutige ID. Diese ID

  • wird vom Killmaster in jedem metzelbaren NPC gesetzt
  • kann im NPC über die Property P_NPC_KILL_NUM abgerufen werden. Die Property ist in /sys/npc/combat.h definiert.

Beispielweise ist die P_NPC_KILL_NUM von einem Drachen die 284.

Will man nun wissen, ob der Spieler diesen Drachen schon getötet hat, dann kann man das wie folgt realisieren:

public int test_kill(object pl) {
  if(objectp(pl) && interactive(pl)) {
   
    // Alle Kills des Spielers befinden sich in diesem
    // Bitfeld (String) direkt in Spielerobjekt
    string|int killbits = pl->GetKillBits();  

    // Noch keine Kills im Spieler gesetzt
    if(!stringp(killbits)) {
      return 0; 
    }
    // Testet auf die ID (284) unseres Drachen
    if(long_test_bit(killbits,284)) {
      
      // Drache bereits einmal getötet
      return 1;  
    } 
  }
  return 0;  // Drache noch nie getötet.
}

Man kann natürlich auch eine ganze Liste von NPCs anlegen und diese dann abfragen. Hier ein Beispiel mit einem Mapping:

protected mapping killmap = 
([
  284: "Drache 1",
  285: "Drache 2",
  400: "Drache 3",
]);

public int|int* test_kill(object pl) {
  if(objectp(pl) && interactive(pl)) {
    string|int killbits = pl->GetKillBits();

    if(!stringp(killbits)) {
      return 0; // Noch keine Kills im Spieler gesetzt
    }

    // Hier schreiben wir später unser Ergebnis rein
    int *ret = ({});

    // Durchlaeuft alle Keys des Mappings killmap
    foreach(int num:killmap) {

      // auf Key (num) testen
      if(long_test_bit(killbits,num)) {

        // falls bereits im Spieler gesetzt, dann in
        // einem Array eintragen, das wir später zurück
        // geben können. 
        ret += ({num});                    
      }
    }
    // Array mit allen NPCs aus killmap, die der Spieler 
    // bereits getötet hat, zurück geben.
    return num; 
  }
  return 0;
}

Ich hoffe, diese kleinen Beispiele regen etwas Eure Fantasie an. Damit kann man einige interessante Aufgaben schreiben. Ich selbst arbeite gerade an einen ‘Orden der Drachentöter’.

LG, It@Efferdland

Kompass

Bild: Kompass

Hallo liebe Magier,

für die Quest ‘Bildschön’ in Ollenhayn an der südlichen Grenze zum Mittland erhält man als Belohnung einen Kompass. Dieser führt Spieler durch den Sumpf am Großen See in der Ebene.

Der Kompass befindet sich in /p/pool/special/kompass.c

Inzwischen kann man den Kompass auch für andere Gebiete benutzen und so z.B. Hinweise zu versteckten Wegen geben. Dazu müsst ihr in Euren Räumen den Header:

/p/pool/special/kompass/sys/kompass.h

includen und danach im Raum die Property P_KOMPASS setzen. P_KOMPASS enthält dann die Richtung, in der sich die Kompassnadel dreht.

Beispiel: SetProp(P_KOMPASS,”norden”);

LG, It@Efferdland

P_TERRAIN & P_ENVIRONMENT

Bild: Berge

Jeder Raum im Efferdland muss bestimmte Elemente enthalten, damit er in das Konzept passt. Wichtige Elemente im Efferdland sind z.B. P_ENVIRONMENT und P_TERRAIN, die (fast) immer gesetzt werden müssen.

P_TERRAIN:

P_TERRAIN beschreibt den Landschaftstypen und ist definiert in /sys/room/terrain.h. Das Setzen dieser Property ist natürlich nur ausserhalb von Gebäuden sinnvoll.

Folgende Terrain Typen gibt es im Efferdland

T_AIRin der Luftz.B. in einem Ballon
T_UNDER_WATERunter Wasserbeim Tauchen
T_AT_WATERam Wasserz.B. Flussufer
T_IN_WATERim Wasserschwimmend
T_HILLauf einem Hügel
T_MOUNTAINim Gebirge
T_PLAINin einer Ebene
T_SWAMPim Sumpf
T_DESERTin einer Wüste
T_FORESTWaldgebiet
T_URBANStadtgebiet
T_FLOODPLAINim Auenegebieteines Flusses…
T_GARDENim Gartenauch Parks u.ä.
T_MOORim Moor
T_UNDERGROUNDim UntergrundHöhlen, Gänge, Tunnel…
T_JUNGLEim Dschungelauch Urwälder
T_COPSEim Unterholz
T_ICEEislandschaft
T_MEADOWWeisen / Wiesen
T_VELDin einer Steppe
T_VILLAGEin einem Dorf
T_POISENDgiftige Umgebungz.B. im Vulkankrater
T_PATHauf einem Pfad
T_TRACKauf einem Weg
T_ROADauf einer Strasse

Es können auch mehrere Terrains gleichzeitig gesetzt werden. Dies ist nötig, wenn man sich z.B. auf einen Weg in einem Dorf befindet. Die Syntax hierfür ist: SetProp(P_TERRAIN, T_TRACK | T_VILLAGE);

Wie gesagt ist P_TERRAIN zwingend zu setzen! Diese Property sollte ernst genommen werden und es ist untersagt, hier irgendwelchen Mumpitz einzutragen!

P_ENVIRONMENT:

Ähnlich wichtig ist die Property P_ENVIRONMENT. Sie beschreibt die Eigenschaften einer Umgebung, z.B. Temperatur. Höhe über NN und Windgeschwindigkeiten. Auch diese Property muss in jedem Raum gesetzt werden. P_ENVIRONMENT ist definiert in /sys/environment.h

P_ENVIRONMENT ist ein Mapping und kann folgende Werte enthalten:

ENV_TEMPERATURETemperaturin Grad Celsius
ENV_ENVSPEEDUmgebungs Geschwindigkeit1 Beaufort (Wertebereich: 1-12), unter Wasser: Strömung
ENV_ALTITUDEHöheüber NN in Meter
ENV_WATERWassertiefein Meter
ENV_HUMIDITYLuftfeuchtigkeitIn %

Aus ENV_TEMPERATURE und ENV_SPEED wird die Property P_WINDCHILL
berechnet, die nur abfragbar ist und die gefühlte Temperatur enthält. Hier ein Beispiel, wie man P_ENVIRONMENT richtig setzt.

SetProp(P_ENVIRONMENT,
([
  ENV_ENVSPEED: 3,     // Windstärke 3
  ENV_TEMPERAURE: -5   // -5 Grad Celsius
  ENV_ALTITUDE: 300    // Meter Höhe 
]));

Schreibt man P_ENVIRONMENT in ein STD-File und möchte man z.B. nur die Temperatur ändern, weil man z.B. immer weiter in den kalten Norden geht, muss man nicht die komplette Property neu setzen, sondern nur den Wert, den man ändern möchte:

// So ändert man nur die Temperatur und lässt alle anderen Werte unberührt:
SetProp(P_ENVIRONMENT,
([
  ENV_TEMPERATURE: -10,
]));

P_ENVIRONMENT enthält von Haus aus STD-Werte:

ENV_ENVSPEEDWindgeschwindigkeitIm Raum: 0
Draußen: 1
ENV_HUMIDITYLuftfeuchtigkeitIm Raum: 50%
Draußen: 60%
ENV_TEMPERATURETemperatur.Im, Raum: 20
Draußen: 15
ENV_ALTITUDEHöhe3m über NN

LG, It@Efferdland

P_VALUE Obsolete

Bild: Münzen

Hallo,

P_VALUE war der Geldwert eines Objektes in Kreuzer. Dieser wurde in den Läden benutzt, um den Verkaufspreis zu berechen.

Statt P_VALUE gibt es jetzt P_BUY_VALUE. Diese neue Propertiy entspricht zu 100% dem alten P_VALUE.

Nur umbenennen macht natürlich keinen Sinn… deshalb gibt es jetzt eine zweite Property: P_SELL_VALUE. Sie kann den Preis in Kreuzer enthalten, die ein Laden als Verkaufspreis ansetzen soll.

Dabei muss P_SELL_VALUE ÜBER dem normalen Verkaufspreis im Laden liegen, ansonsten wird es ignoriert. Daraus folgt: mit P_SELL_VALUE kann man Objekte nur teurer, aber niemals billiger machen.

LG, It@Efferdland