Inhalt:
Das Tutorial ist so aufgebaut, dass am Ende eine kleine Mission steht, die den Spieler ein Schwert für einen Nsc holen lässt, im Verlauf wird, an den entsprechenden Stellen erklärt, wie eine Teilaufgabe umzusetzen ist. Z.B. wird für eine hol mir ein Schwert Mission natürlich ein tolles Schwert benötigt, also wird zu diesem Zeitpunkt die Item-Klasse erklärt und dann mit Inhalt gefüllt, bis ein entsprechendes Schwert vorliegt, das dann ins Spiel eingefügt und dort benutzt werden kann.
2. Der Auftraggeber
Um einen neuen Nsc im Spiel zur Verfügung zu haben muß eine Instanz der Klasse C_Npc erstellt werden. Im folgenden wird erst kurz ein allgemeiner Abriss der Klasse gegeben und diese dann mit Inhalt gefüllt.
Die Klasse
CLASS C_NPC
{
VAR INT id; // absolute ID des NPCs
Die Id ist ein eindeutiger Indentifier, mit dem der Nsc im Programm referenziert werden kann und wird zum Beispiel benutzt um Tagesabläufe mit dem Nsc zu verknüpfen.
VAR STRING name [5]; // Namen des NPC
Der Name ist ein String und taucht z.B. auf wenn man im Spiel einen Nsc im Focus hat.
VAR STRING slot;
Unbenutzt, aber vorhanden, lässt sich also für spezielle Zwecke als Membervariable, die einen String neben dem Namen speichern kann benutzen.
VAR INT npcType;
Integer, mit dem in Gothic der Charakter bestimmten Gruppen zugeordnet wird. Npc_TypeMain ist z.B. einer der Hauptcharaktere (z.B. Diego).
VAR INT flags;
Hier lässt sich der Nsc auf Immortal setzen flags = NPC_FLAG_IMMORTAL;
VAR INT attribute [ATR_INDEX_MAX] ;
Die Attributwerte des Nsc werden in ein Array gespeichert, die Benutzung wird an Hand des Beispielcharakters, der weiter unten erstellt wird klarer.
VAR INT protection [PROT_INDEX_MAX];
Die Protection wird genauso gehandelt wie die Attribute, hier kann man dem Nsc einen natürlichen Rüstschutz gegen bestimmte Schadenstypen verpassen, Z.B. gegen Feuer
VAR INT damage [DAM_INDEX_MAX] ;
Gibt die Möglichkeit mehrere Arten von Nahkampfschäden an, den einen Nsc zuzuweisen, die er mit den bloßen Fäusten verursacht, ist aber nicht unbedingt sinnvoll, weil Nsc die meiste Zeit eine Waffe in der Hand haben sollten, zum rumexperimentieren Damagetypes, die das Array akzeptiert sind :
- DAM_INDEX_BLUNT
- DAM_INDEX_EDGE
- DAM_INDEX_POINT
- DAM_INDEX_FIRE
- DAM_INDEX_FLY
- DAM_INDEX_MAGIC
VAR INT damagetype;
Gibt den Nahkampfschaden an, den ein Nsc mit den bloßen Fäusten macht, ist aber nicht unbedingt sinnvoll, weil Nsc die meiste Zeit eine Waffe in der Hand haben sollten.
VAR INT guild,level;
Mit Gilde gibt man dem Nsc eine entsprechende Gilde, wie z.B. GIL_NONE für gildenlos.Mit Level wird dem Nsc ein entsprechender Level verpasst.
VAR FUNC mission [MAX_MISSIONS];
Obsolet
var INT fight_tactic;
Hier wird dem Nsc seine Taktik, die er in Kämpfen anwendet zugeordnet. à Wird aber später noch genauer erklärt, wie die FAI´s funktionieren
VAR INT weapon;
Obsolet
VAR INT voice;
Stimmnr. des Charakters à wichtig für Standardausgaben in Zuständen
VAR INT voicePitch;
Gibt die Möglichkeit die Stimme des Nsc zu pitchen.
VAR INT bodymass;
Obsolet
VAR FUNC daily_routine; // Tagesablauf
Hier wird der Tagesablauf den ein Nsc hat angemeldet, exklusiv zu start_aistate.
VAR FUNC start_aistate; // Zustandsgesteuert
Hier kann ein Zustand angegeben werden, in dem sich ein Nsc beim einsetzen befindet. Darf nur exklusiv zu daily_routine benutzt werden.
// **********************
// Spawn
// **********************
VAR STRING spawnPoint; // Beim Tod, wo respawnen ?
Erfordert einen gültigen Waypoint aus dem zen, wenn leer ist ein toter Nsc für immer tot.
VAR INT spawnDelay; // Mit Delay in (Echtzeit)-Sekunden
Verzögerung, mit der der Spawnvorgang begonnen wird Spawnzeitpunkt = Todeszeitpunkt +spawnDelay.
// **********************
// SENSES
// **********************
VAR INT senses; // Sinne
Den Nscs stehen drei Sinne zur Verfügung :
- SENSE_SEE Sichtkegel gibt Wahrnehmungsbegrenzung an, ist echte Sicht, d.h. Hindernisse "stören" beim sehen.
- SENSE_HEAR Wände etc blockieren, Sichthindernisse wie z.B. ein Baum nicht oder hinter einem Nsc kann damit auch etwas wahrgenommen werden, obwohl es außerhalb des Sichtkegels liegt.
- SENSE_SMELL ist die mächtigste Wahrnehmung und einzige Begrenzung ist die Range, d.h. alles was innerhalb der senses_Range liegt wird wahrgenommen.
VAR INT senses_range;
Reichweite der Sinne in cm.
// **********************
// Feel free to use
// **********************
VAR INT aivar[50];
Array von 50 (in G1) bzw. 100 (in G2) Variablen, die ein Nsc speichern kann. Achtung werden von der AI-Logik verwendet (siehe AI_constants).
Einige Plätze sind allerdings unbenutzt und können für eigene Zwecke verwendet werden.
VAR STRING wp;
C++-Code (Gothic-exe) speichert hier den aktuellen W(ay)P(oint) des Nsc, damit er für Abfragen zur Verfügung steht, d.h. ein T(ages)A(blauf) übergibt einen WP und dieser ist dann der aktuelle self.wp.
// **********************
// Experience dependant
// **********************
VAR INT exp; // EXerience Points
VAR INT exp_next; // EXerience Points needed to advance to next level
VAR INT lp; // Learn Points
};
Diese drei Variablen speichern ExperiencePoints, die Anzahl der benötigten Experience Points für den nächsten level und die Anzahl von Lp des Charakters.Wird aber nur für den Spieler benötigt.
So jetzt soll es auch schon losgehen der ganzen Sache mal ein wenig Fleisch zu geben und einen NSc zu kreieren, der auch wirklich in der Welt rumläuft.
Der Nsc soll Gunther heißen, keiner Gilde angehören und ein Ambient Nsc sein. Er wird einen Level von 17 haben, Stärke ist 100, Ausdauer auch, er hat kein Mana und 200 H(it)P(oints). Zu Übungzwecken ist der Nsc leider taub und hat eine WN Reichweite von 20m, dafür ist er ein guter Kämpfer, mit dem Talent 1_Hand der Stufe zwei.Dafür benötigt er natürlich eine Waffe, die seinem Talent entspricht und sich in seinem Inventory befindet.
// Klasse von der der Nsc abgeleitet ist kann auch
// direkt von C_Npc abgeleitet werden
instance None_999_Gunther (Npc_Default)
{
//-------- primary data --------
name = "Gunther";
guild = GIL_NONE;
npctype = NPCTYPE_AMBIENT;
level = 17;
voice = 8;
id = 999;
// ----- attributes --------
attribute[ATR_STRENGTH] = 100;
attribute[ATR_DEXTERITY] = 100;
attribute[ATR_MANA_MAX] = 0;
attribute[ATR_MANA] = 0;
attribute[ATR_HITPOINTS_MAX]= 200;
attribute[ATR_HITPOINTS] = 200;
//-------- visuals --------
// basic animation file - Bei Menschen immer Humans.mds,
// so lange keine selbsterstellten Daten vorliegen, sondern nur
// auf Animantionsdaten von Gothic gearbeitet wird.
Mdl_SetVisual (self, "humans.mds");
// overlay animation file ß Bewegungtyp: Militär
Mdl_ApplyOverlayMds (self, "humans_militia.mds");
// Nur in G1
// Aussehen und Rüstung festlegen
Mdl_SetVisualBody (self, "hum_body_naked0", // body mesh
0, // body texture variant
1, // skin color
"hum_head_fighter", // head mesh
51, // head texture variant
2, // teeth texture variant
GRD_ARMOR_M); // armor instance
// body width according to strength of character
B_Scale (self);
// Nur in G2
// Aussehen und Rüstung festlegen
B_SetNpcVisual (self, MALE, // Geschlecht
"Hum_Head_Fighter", // body mesh
Face_L_ToughBald01, // Gesicht-Textur (siehe AI_Constants.d)
BodyTex_L, // Körper-Textur (siehe AI_Constants.d)
ITAR_MIL_M); // Rüstung
// Wieder in G1+G2
// limb fatness
Mdl_SetModelFatness (self, 0);
//-------- talents -------- (in G1)
Npc_SetTalentSkill (self, NPC_TALENT_1H,2);
//-------- talents -------- (in G2)
// Allgemeine Talente setzen
B_GiveNpcTalents (self);
// Alle vier Kampf-Talente auf einmal setzen
B_SetFightSkills (self, 60);
//-------- inventory -------- (in G1)
EquipItem (self, ItMw_1H_Mace_War_03);
// Zehn Heiltränke falls der Bursche mal verletzt wird:
CreateInvItems (self, ITFO_Potion_Health_01, 10);
//-------- inventory -------- (in G2)
EquipItem (self, ItMw_1h_Mil_Sword);
// Zehn Heiltränke falls der Bursche mal verletzt wird:
CreateInvItems (self, ItPo_Health_01, 10);
//--------senses-------------
senses = SENSE_SEE | SENSE_SMELL;
senses_range = 2000;
//------------- ai -------------
fight_tactic = FAI_HUMAN_STRONG;
// Anmeldung des Tagesablaufs, wird aber später noch genauer erklärt:
daily_routine = Rtn_start_999;
};
Auch wenn es erst später erkärt wird muß hier diese Funktion schon angelegt sein, damit der Nsc auch "klappt".
func void Rtn_Start_999 ()
{
};
Kopiert man diesen Nsc jetzt per Texteditor in ein vorhandenes File und hat das Parameter -zreparse in den GothicStarter eingetragen, kann man im Spiel über die Konsole per insert [instance], hier None_999_Gunther den Nsc schon mal einfügen und betrachten, auch wenn er jetzt noch nicht so viel tut. Im GothicStarter MUSS der Parameter -devmode angegeben sein, um die Konsole öffnen zu können.
Um aber die Übersicht bei vielen Nscs nicht zu verlieren ist es ganz nützlich die schon in Gothic verwendeten Strukturen zu benutzen. Nscs sind im Ordner /_work/data/skripts/content/story/Npc abgelegt. Wenn ein File mit der Dateierweiterung .d erstellt wird und in diesem Ordner abgelegt wird, wird es automatisch mit geparst und steht dann im Spiel zur Verfügung.
3.Der Bursche kriegt eine Aufgabe
Damit er was zu tun hat, während er auf Euch wartet, muß der Charakter entweder einen TA oder einen StartState haben, hier wird jetzt der etwas komplexere Teil eine TA zu erstellen erklärt, weil darin auch das erstellen eines Z(u)S(tands) enthalten ist, der für StartState nötig ist.
In diesem Zustand wird nicht viel passieren, außer das der Nsc sich auf den zugewiesenen Wegpunkt begibt um hier auf den Spieler zu warten und sich von Zeit zu Zeit mal von diesem WP wegzubegeben um zu pinkeln.
Damit das W(ahr)N(ehmungs)-System etwas deutlicher wird, bekommt der Nsc erst mal nur zwei WN, d.h. aber auch, dass er erst mal nur auf die Annäherung und Ansprechen des Spielers reagiert und ihm alles andere egal ist, wie Schaden nehmen angesprochen werden etc.
func void ZS_GuntherWait ()
{
// Debugausgaben, die man sich im Spy anzeigen lassen kann
PrintDebugNpc (PD_TA_FRAME, "ZS_GuntherWait");
// führt unter anderem dazu, dass der Nsc bei Annäherung des Spielers
// kontrolliert ob er eine Info hat, die ihm zum Ansprechen des Spielers
// veranlasst (Info wird später erstellt)
// In G1
Npc_PercEnable(self, PERC_ASSESSPLAYER, B_AssessSC);
// In G2
Npc_PercEnable(self, PERC_ASSESSPLAYER, B_AssessPlayer);
// macht den Nsc ansprechbar, damit der Spieler auch den Auftrag beenden kann
Npc_PercEnable(self, PERC_ASSESSTALK, B_AssessTalk);
AI_StandUp (self);
AI_SetWalkmode (self, NPC_WALK); // Walkmode für den Zustand, in diesem Fall gehen
AI_GotoWP (self, self.wp); // Gehe zum Tagesablaufstart
AI_AlignToWP (self); // Ausrichten am WP (Pfeilrichtung im Spacer)
};
func void ZS_GuntherWait_Loop ()
{
PrintDebugNpc (PD_TA_LOOP, "ZS_GuntherWait_Loop"); // s.o.
AI_GotoWP (self, self.wp); // Gehe zum Tagesablaufstart
AI_Wait (self, 100); // 100 Sekunden warten, bis die Wp gewechselt wird
// Anderen WP in der Nähe suchen, damit er nicht dahin pinkelt wo er steht
AI_GotoWP (self, Npc_GetNearestWP (Self));
AI_PlayAni(self, "T_PEE");
AI_Wait (self, 100); // 100 Sekunden warten, bis die Loop wieder von vorne losgeht
};
func void ZS_GuntherWait_End ()
{
PrintDebugNpc (PD_TA_FRAME,"ZS_GuntherWait_End"); // s.o.
};
So jetzt ist es schon fast fertig, damit aber dieser ZS auch als TA verwendet werden kann muss er angemeldet werden. Dafür muss in der /_work/data/skripts/content/story/ZS/TA.d folgende Zeile auftauchen:
func void TA_GuntherWait ( var int start_h,
var int start_m,
var int stop_h,
var int stop_m,
VAR string waypoint)
{
TA_Min (self, start_h,start_m, stop_h, stop_m, ZS_GuntherWait, waypoint);
};
Anmerkung:In der TA.d findet man auch eine Übersicht über die möglichen bereits vorhandenen Tagesabläufe für NSCs.
und die
func void Rtn_Start_999 ()
{
TA_GuntherWait (0,00,13,00, "OC1");
TA_GuntherWait (13,00,0,00, "OC1");
};
führt in dieser Form dazu, dass sich der Nsc nach dem Einfügen 24h am Eingang des Oldcamps rumtreibt. Ein häufiger Anfängerfehler ist übrigens, die Wegpunkte nicht
komplett gross zu schreiben! Dadurch wird dieser nicht von Gothic gefunden und der NSC erscheint im Spiel nicht.
HINWEIS: TAs müssen mindesten zweimal in den Routinen auftauchen, auch wenn die Nscs 24h lang das gleiche tun, das hat was damit zu tun, dass der C++-Code die TAs von ZS nur so unterscheiden kann .
Zustände sind /_work/data/skripts/content/story/ZS zu finden und es empfiehlt sich auch hier wieder, wie schon beim Npc, ein eigenes File mit einem Texteditor zu erstellen und mit der Dateierweiterung .d ( dieses ist zwingend) zu speichern. Auch hier wird wieder mit einer Wildcard geparst, so dass es reicht das File im entsprechenden Pfad abzulegen.
Ein letzter Schritt und Gunther ist dauerhaft im Spiel :
Anmeldung in der /_work/data/skripts/content/story/Startup.d
Wld_InsertNpc(None_999_Gunther,"OC1"); in den Oldcamp-Block, schließlich steht Gunther ja da rum.
Bei NSC mit Tagesablauf kann man als Insert-Wegpunkt im Prinzip einen beliebigen Wegpunkt angeben, solange dieser existiert und komplett gross geschrieben wurde.
Wo sich der NSC rumtreibt, wird schließlich durch den Tagesablauf bestimmt.
4. Der Auftrag
Aufträge werden in Gothic über ein Dialogsystem implementiert, deshalb kommt jetzt erst mal ein leerer Dialog an Hand dessen die Grundfunktionalität erklärt wird und der später mit Inhalt gefüllt wird, bis es zu einem Auftrag mit hol mir das Schwert und werde dafür belohnt führt.
instance Instanz_Name (C_INFO)
{
// Diesem Nsc "gehört" der Dialog
npc = NPC_Name;
// Hier gehört der Funktionsname, der Funktion rein,
// die die Bedingungen, das der Dialog gültig ist überprüft.
// Solange diese Funktion FALSE zurückgibt, ist der Dialog nicht in der Auswahlliste
// aufgeführt bzw. der NSC spricht den Spieler nicht wegen dem Dialog von sich an.
condition = Instanz_Name_Condition;
// Diese Funktion steuert den eigentlichen Dialog.
information = Instanz_Name_Info;
important = 1;
Ist ein Dialog Important spricht der Nsc den Sc von sich aus an, sobald er ihn entdeckt (deshalb WN AssessPlayer/AssessSC in ZS_GuntherWait aktiviert s.o.)
permanent = 0;
};
Ist ein Dialog Permanent wird er, so lange seine Condition True ist immer angeboten
FUNC int Instanz_Name_Condition()
{
return 1;
};
Hier sollten per Abfrage Bedingungen erfragt werden, damit der Dialog gestartet werden kann. Gibt die Funktion einfach TRUE zurück wird der Dialog immer angeboten.
func void Instanz_Name_Info()
{
// Wie hier der Inhalt reinkommt wird beim Auftrag "basteln" erklärt.
};
Da es sich beim Exit auch um einen Dialog handelt wird hier nicht mehr Schritt für Schritt erklärt wofür dieser ist, nur soviel, die Exit-Funktion sorgt dafür, das es immer eine Auswahl im Dialogscreen gibt, mit der der Spieler den Dialog verlassen kann.
// ************************ EXIT **************************
instance Instanz_Name_Exit (C_INFO)
{
npc = NPC_Name;
condition = Instanz_Name_Exit_Condition;
information = Instanz_Name_Exit_Info;
important = 0;
permanent = 1;
description = "ENDE";
};
FUNC int Instanz_Name_Exit_Condition()
{
return 1;
};
FUNC VOID Instanz_Name_Exit_Info()
{
AI_StopProcessInfos (self);
};
Durch geschicktes Verschachteln von Dialogen und Dialogbedingungen wird dann ein Auftrag gebaut.
Lets Go:
Notwendige Globale variablen, ist so nicht sauber und sollte in einem eigenen File gepflegt werden, aber für die Tutorial Zwecke reicht es so erst mal à In Gothic sind sie aber in _work/data/skripts/content/story/Storyglobals.d abgelegt, es ist also ganz sinnvoll hier einen neuen Teil einzufügen, der neu erstellte GlobaleVariablen aufnimmt.
const string GunthersSword = " Bringe Gunthers Schwert zurück";
var int int_GotSword;
instance None_999_Gunther_AskForSword (C_INFO)
{
npc = None_999_Gunther;
condition = None_999_Gunther_AskForSword_Condition;
information = None_999_Gunther_AskForSword_Info;
important = TRUE; // Gunther soll den Spieler selbsttätig ansprechen
permanent = FALSE; // Es reicht aber wenn er einmal den Auftrag vergibt
description = ""; // Bleibt hier leer, weil es eine Importantinfo ist, die
// direkt abgefeuert wird und somit gar nicht im Dialogscreen auftaucht
};
FUNC int None_999_Gunther_AskForSword_Condition()
{
if (hero.level >= 0) // Nur Helden, die "schon" auf Level null sind,
// haben die Ehre Gunther sein Schwert bringen zu dürfen
{
return TRUE;
};
return FALSE;
};
func void None_999_Gunther_AskForSword_Info()
{
AI_Output ( self, other, "None_999_Gunther_AskForSword_Info_8_01"); // Hey Du, Heute schon was vor?
// Achtung: In dem String, der die Outputunit verschlüsselt,
// ist die erste Zahl die Stimmnummer und die zweite eine laufende Nummer,
// die nur einmal pro vorangestelltem String verwendet werden darf
Info_ClearChoices(None_999_Gunther_AskForSword);
Info_AddChoice(None_999_Gunther_AskForSword,
"Auf jedenfall genug", None_999_Gunther_AskForSword_Yes);
Info_AddChoice(None_999_Gunther_AskForSword,
"Nichts spezielles", None_999_Gunther_AskForSword_No);
};
// Der Spieler will nicht
func void None_999_Gunther_AskForSword_Yes ()
{
AI_Output(other,self,
"None_999_Gunther_AskForSword_Info_8_02"); // Es geht Dich zwar nichts an, [...]
AI_Output(self,other,
"None_999_Gunther_AskForSword_Info_8_03"); // Na gut dann scheidest Du wohl aus:
AI_StopProcessInfos (self);
};
// Der Spieler bekundet Interesse
func void None_999_Gunther_AskForSword_No ()
{
AI_Output(other,self,"None_999_Gunther_AskForSword_Info_8_04"); // Bisher noch nichts spezielles.
AI_Output(self,other,"None_999_Gunther_AskForSword_Info_8_05"); // Das ist gut, denn Du mußt [...]
Log_CreateTopic (GunthersSword,LOG_MISSION);
Log_SetTopicStatus (GunthersSword,LOG_RUNNING);
B_LogEntry(GunthersSword, "Gunther hat mich gebeten sein Milhouseschwert für Ihn aufzutreiben");
AI_StopProcessInfos (self);
};
So das ist erst mal der Dialog, mit dem Gunther den Auftrag vergibt, wenn man sagt man hat noch nix vor wird der Auftrag direkt vergeben, damit der Dialog erst mal etwas kleiner und übersichtlicher bleibt.
Die Add_Choice Befehle bauen im Dialogscreen den angegeben String ein und wenn man ihm im Dialog aufruft wird auf die Funktion verwiesen, die hinter dem entsprechenden String angegeben ist. Hier z.B.: None_999_Gunther_AskForSword_No.
Dialog, gehören in den Ordner /_work/data/skripts/content/story/Missions und müssen mit dem Kürzel Dia_ beginnen, damit sie automatisch gelesen werden.
!!! Damit die neu angelegten O(utput)U(nits) auch als Text im Spiel auftauchen muss nun der Spacer gestartet werden. Im Spacer ist auf der Leiste mit den Tool-Buttons ein Symbol, dass aussieht wie ein beschriebenes Blatt, dieses anklicken und der OU-Dialog erscheint.
Mit Update werden alle Skripte geparst und nach neuen O-Us durchsucht, mit einem darauf folgen Safe werden alle neu angelegten Ous gespeichert und sind dann im Spiel verfügbar.
Wenn man jetzt das Spiel startet und zu Gunther geht spricht er den Hero an und man kann sich für oder gegen das Holen des Schwertes entscheiden.
Anmerkung: Bitte daran denken in der Mod.ini das Parameter enableSubtitles zu setzen, sonst gibt es keine Textausgaben, weil keine Soundfiles zu den Ausgaben existieren.
5. Den Gegenstand der Begierde erstellen
Gunther will ein Schwert haben, das es so in Gothic nicht gibt, also muss es erstellt werden.
Die Klasse
CLASS C_Item
{
// Für alle Items
VAR INT id;
Id wie bei der C_Npc Klasse (s.o.)
VAR STRING name,nameID;
Synonym zu C_Npc
VAR INT hp,hp_max;
Synonym zu C_Npc
VAR INT mainflag,flags; // Hauptflag und weitere Flags
VAR INT weight,value;
Gewicht und Wert lassen sich als integer angeben.
// Für Waffen
VAR INT damageType; // Welche Schadensarten
Synonym zu C_Npc.
VAR INT damageTotal;
Synonym zu C_Npc
VAR INT damage [DAM_INDEX_MAX];
Synonym zu C_Npc
// Für Rüstungen
VAR INT wear;
Hier ist eigentlich nur WEAR_TORSO interessant, weil keine Helme im Spiel sind.
VAR INT protection [PROT_INDEX_MAX];
Dies ist ein Array in dem für jede Schadensklasse ein eigener Rüstschutz angegeben werden kann.
// Für Nahrung
VAR INT nutrition ; // HP-Steigerung bei Nahrung
Obsolet
// Benötigte Attribute zum Benutzen des Items
VAR INT cond_atr [3] ;
VAR INT cond_value [3] ;
Ein Array von zweimal drei Integer, in dem angegeben werde kann welche Stärke (z.B. 10) etc. nötig ist um diese Item anzulegen oder zu Benutzen.
// Attribute, die bei anlegen des Items verändert werden
VAR INT change_atr [3] ;
VAR INT change_value [3] ;
Obsolet
// Parserfunktionen
VAR FUNC magic; // Parserfunktion zum "Magie Header"
VAR FUNC on_equip; // Parserfunktion, wenn Item equipped wird.
VAR FUNC on_unequip; // Parserfunktion, wenn Item unequipped wird.
VAR FUNC on_state [4];
// Besitzer
VAR FUNC owner;
// Besitzer : Instanz-Name
VAR INT ownerGuild;
// Besitzer : Gilde
VAR INT disguiseGuild;
// Zur Schau getragene Gilde durch Verkleidung à Obsolet
// Die 3DS-Datei
VAR STRING visual;
Gibt's an welche Datei als Darstellung für das Item geladen wird.
// Veränderung des NSC-Meshes beim Anlegen dieses Gegenstandes
VAR STRING visual_change ; // ASC - File
Wird zum Anlegen einer Rüstung benutzt, weil dann ein anderes ASC.File als visual geladen wird.
VAR INT visual_skin;
Texturvariation für das betreffende Rüstungsmesh.
VAR STRING scemeName;
Interner Name für das Benutzen von Items.
VAR INT material;
Mit dem Material sind z.B. die "Klangeigenschaften" eines Items festgelegt.
// VAR STRING pfx; // Magic Weapon PFX
Obsolet
VAR INT munition; // Instance of Munition
Bei Bögen wird hier angegeben mit welcher Munition sie schießen (Arrow V Bolt).
VAR INT spell;
Gibt an welchen Spell magische Items ausführen.
VAR INT range;
Gibt den Radius an, in dem eine Nahkampfwaffe trifft.
VAR INT mag_circle;
Welchem Magischen Zirkel ist dieses Item zugeordnet.
VAR STRING description;
Die Beschreibung, die beim Betrachten eines Items auftaucht.
VAR STRING text[ITM_TEXT_MAX];
VAR INT count[ITM_TEXT_MAX];
};
Vars mit denen sich die Werte der Items in den Statusscreen transportieren lassen.
So dann soll jetzt mal ein schönes Schwert entstehen, nach dem Gunther suchen lassen kann.
Damit den Spieler das Schwert auch interessiert sollte es natürlich ein paar nette Werte haben, also soll es 1000 Erz wert sein und einen Schaden von 300 machen, das Visual wird aus den Gothicdaten geklaut, weil visuals erstellen an einer andern Stelle erklärt wird
Ansonsten muss das Schwert natürlich eine Nahkampfwaffe und ein Schwert sein, sollte aus Metall bestehen und vielleicht noch ein bisschen von sich preisgeben in der Beschreibung.
INSTANCE ItMw_1H_GuntherWantedSword (C_Item)
{
name = Milhouse´ Schwert";
mainflag = ITEM_KAT_NF; // Nahkampfwaffe
flags = ITEM_SWD; // Schwert
material = MAT_METAL; // besteht aus Metall
value = 1000; // Wert von 1000
damageTotal = 300;
damagetype = DAM_EDGE; // Verursacht Klingenschaden
range = 200; // Hat einen Radisu von 2m
cond_atr[2] = ATR_STRENGTH; // Was muß man haben, damit man es führen darf
cond_value[2] = 5; // Wie hoch muß man das (s.o.) haben
visual = "ItMw_1H_Sword_Old_01.3DS"; // Visual aus den Gothicdaten "gemopst" (in G1)
visual = "ItMw_065_1h_sword_bastard_03.3DS"; // Visual aus den Gothicdaten "gemopst" (in G2)
description = name;
TEXT[2] = "Damage"; COUNT[2] = 300;
TEXT[3] = "Benötigte Stärke"; COUNT[3] = cond_value[2];
TEXT[4] = "einhand Schwert";
TEXT[5] = "Wert"; COUNT[5] = value;
};
Das Item lässt sich jetzt genauso wie Gunther per Konsole im Spiel einfügen.
Bei den Waffen empfiehlt es sich ein eigenes File (*.d) anzulegen, das dann im Ordner /_work/data/skripts/content/Items abgelegt wird. Hier muss dann allerdings auch dafür gesorgt werden, dass das Programm das neue File liest.
Dafür öffnet man das File /_work/data/skripts/content/gothic.src in diesem File sind alle Pfade angegeben, aus denen Contentskripte geladen werden. Diese Pfade sind relativ zum Pfad /_work/data/skripts/
Hier fügt man für das neue File den Pfad ITEMS\FILENAME.D ein und schon werden die Daten aus dem File gelesen.
Achtung! Diese Datei erfordert zwingend Großbuchstaben. Falls es mal Probleme geben
sollte beim Gamestart kann es sein, das eine Datei Daten aus einer anderen Datei referenziert und deshalb diese vor
einer anderen geparst werden muß. Lösungshinweise gigt es zwei, einmal: keine Wildcards verwenden, sondern die Files
einzeln per Namen parsen und zum anderen ein wenig mit der Reihenfolge rumspielen, aber nur der Daten, die neu erstellt
wurden, weil sonst das ganze File kaputt gehen kann.
Natürlich lassen sich so auch neue Ordner referenzieren, aber das ist dann doch eine Sache zum Ausprobieren und rumspielen.
6. Den "Auftrag erfüllt"-Dialog erstellen
Hier wird wieder ein Dialog angelegt, in dem bestimmte Bedingungen erfüllt sein müssen und an Hand dieser werde noch Belohnungen verteilt, in diesem Fall Experience Points und LearnPoints erhöhen.Da Dialoge oben schon genauer erklärt wurden hier nur noch der Dialog, der zum Auftrag erfüllen vorhanden sein muß.
Achtung! Wenn er per Cut-Copy-Paste übernommen wird dran denken die OU im Spacer neu zu erstellen.
// ************************ EXIT **************************
instance None_999_Gunther_AskForSword_Exit (C_INFO)
{
npc = None_999_Gunther;
condition = None_999_Gunther_AskForSword_Exit_Condition;
information = None_999_Gunther_AskForSword_Exit_Info;
important = 0;
permanent = 1;
description = "ENDE";
};
FUNC int None_999_Gunther_AskForSword_Exit_Condition()
{
return 1; // Dem Spieler dauerhaft die Möglichkeit geben den Dialog zu verlassen
};
FUNC VOID None_999_Gunther_AskForSword_Exit_Info()
{
AI_StopProcessInfos ( self );
};
instance None_999_Gunther_BringSword (C_INFO)
{
npc = None_999_Gunther;
condition = None_999_Gunther_bringSword_Condition;
information = None_999_Gunther_bringSword_Info;
important = FALSE;
permanent = TRUE; // So lange der Auftrag nicht erfüllt ist muß der
// Nsc den Dialog immer wieder bringen, alles
// andere wird in der Bedingung für den Dialog geregelt
description = "Hast Du das Schwert ?";
};
FUNC int None_999_Gunther_bringSword_Condition()
{
// 1.Nur wenn der Held den Auftrag hat kann er ihn auch beenden
// 2.Nur wenn der Auftrag nicht als erfüllt gilt wird der Dialog angeboten
// 3.Schwert instanz abfragen, nur dann macht Auftraglösen Sinn
if (Npc_KnowsInfo ( hero, None_999_Gunther_AskForSword) // (siehe 1)
&! int_GotSword // (siehe 2)
&& Npc_HasItems (other, ItMw_1H_GuntherWantedSword) >= 1) // (siehe 3)
{
return TRUE;
};
return FALSE;
};
func void None_999_Gunther_bringSword_Info()
{
Info_ClearChoices (None_999_Gunther_bringSword);
Info_AddChoice (None_999_Gunther_bringSword,
"Ich hab Dein Schwert gebs dir aber nicht",
None_999_Gunther_bringSword_Yes);
Info_AddChoice (None_999_Gunther_bringSword,
"Ich hab Dein Schwert hier ist es",
None_999_Gunther_bringSword_No);
};
// Der Spieler will das Schwert nicht übergeben
func void None_999_Gunther_bringSword_Yes ()
{
AI_Output( other, self, "None_999_Gunther_bringSword_Info_8_06"); // Ich habs, aber gebs Dir nicht
AI_Output( self, other, "None_999_Gunther_bringSword_Info_8_07"); // Du hast sie ja wohl nicht alle
AI_StopProcessInfos (self);
AI_StartState( self, ZS_Attack, 0, ""); // Gunther ist jetzt ein wenig sauer und greift an (in G1)
B_Attack (self, other, AR_NONE, 1); // Gunther ist jetzt ein wenig sauer und greift an (in G2)
};
// Der Spieler bekundet Interesse
func void None_999_Gunther_bringSword_No ()
{
AI_Output ( other, self, "None_999_Gunther_bringSword_Info_8_08"); // Hier Dein Schwert, [...]
AI_Output ( self, other, "None_999_Gunther_bringSword_Info_8_09"); // Kein Problem [...]
B_LogEntry (GunthersSword, "Ich habe Gunther sein Schwert gebracht");
other.exp = other.exp + 100; // Dem Spieler 100 Experience Points geben
other.lp = other.lp + 10; // Und weil er so nett war auch gleich no ein paar lp
//Npc_GiveItem ( other, MilhouseSchwert, self); // Gunther will ja auch sein Schwert haben
AI_StopProcessInfos (self);
};
Der Dialog wird dann natürlich genau so an- und abgelegt wie der vorherige.
|