überspringe Navigation

Schwarzer_Moderkäfer

Der Server lief in den letzten Wochen friedlich vor sich hin, ich dachte mir nichts Böses und beschäftigte mich damit, einige Programmierprobleme zu lösen:

1. „Wald ohne Wiederkehr“ verwendet variable Gebietsübergänge, das Script wurde bisher durch einen simplen Trigger ausgelöst. Wenn ich aber mitten im Abenteuer wieder einsteigen will, brauche ich eine andere Form des Auslösers (zB OnModuleLoad).

2. Die HTF-Scripte (Hunger, Durst, Erschöpfung) müssen so abgeändert werden, dass NPCs, die ich als Spielleiter in Besitz nehme, nicht beim ersten Heartbeat (~ 6 sec) tot umfallen.

3. Es muss eine bessere Verzahnung zwischen persistenten (in der SQL-Datenbank gespeicherten) und nicht-persistenten (im Modul gespeicherten) Variablen erreicht werden, damit der Wiedereinstieg in die Abenteuer auch nach längerer Pause problemlos möglich ist und dabei nicht zu viele Scripte an der Performance nagen.

———

Wie so oft, wenn ich frohgemut mehrere Bereiche simultan bearbeite— als ich das neue Modul geladen hatte und es ausprobieren wollte, FUNKTIONIERTE GAR NICHTS MEHR!
Der Spielleiter-Client brachte das Modul zum Absturz. Variablen wurden weder gespeichert noch korrekt ausgelesen. Das wichtige Start-Script, welches beim Einstieg in die Spielwelt die richtigen Verknüpfungen herstellen soll, brach scheinbar zufällig ab.

Ich war schon ganz verzweifelt und kurz davor, alles hinzuschmeißen, habe dann aber im Laufe der letzten Tage die Fehlerquellen isoliert und beseitigt.
Ich hatte die NWN-Installation auf eine andere Partition des Servers verschoben, da mir auf C:\ wegen der Windows-Updates der Platz ausging, und das schon wieder vergessen. Dadurch gingen alle Kompatibilitätseinstellungen verloren und die SQL-Schnittstelle verwies auf das falsche Verzeichnis. Die Scripte fragten teilweise Variablen ab, die in bestimmten Problemkonstellationen zuvor nicht definiert worden sein konnten, da die ursprünglichen Funktionen auf der Annahme einer einzelnen Spielsession oder eines fortlaufenden Moduls basierten. Hier habe ich Sicherungen eingebaut, damit es auch bei Modul-Reset und Verwendung Eurer eigenen hochstufigen Chars keine Aussetzer mehr gibt.

Unten ein Einblick in eines der wichtigsten Scripte, die bei Spielstart ausgeführt werden müssen.

//Wird am Startpunkt ausgefuehrt;
//neue Spieler mit 0 XP bekommen Startausruestung (und werden ggf zum RP-Rassenstartpunkt geportet;)
//Spieler, die schon XP haben, werden zu ihrer letzten Location geportet
//und wichtige Variablen (HTF, Guns) werden gesetzt

#include „nw_i0_plot“
//#include „crp_inc_control“ (in CRP_INC_MOVE enthalten)
#include „colors_inc“
#include „x2_inc_switches“
#include „crp_inc_move“

//Datenbankschnittstelle integrieren
#include „aps_include“

object oPC = GetEnteringObject();

//Alte Erde Wetter-System:
object oArea = GetArea(oPC);

int nEinmal = GetLocalInt(oPC, „Gestartet?“);

//object oItem = GetFirstItemInInventory(oPC);//wir verzichten darauf, das Gepaeck zu leeren

//Startpunkt „Ursprung“ fuer ganz neue Spieler festlegen
//object oStartALL = GetObjectByTag(„11_WP_URSPRUNG“);

//Textnachricht fuer wiederkehrende Spieler
string sSpieler = GetPCPlayerName(oPC);
string sWelcomeBack = ColorTokenWhite() +(„Schoen, Dich wiederzusehen, „) +sSpieler +(„!“) +ColorTokenEnd();

object oMod = GetModule();

//HTF-System
string sHunger = „HUNGER“;//Name of Module Hunger Value
string sThirst = „DURST“;//Name of Module Thirst Value
string sFatigue = „ERSCHOEPFUNG“;//Name of Module Fatigue Value
string sAlcTotal = „ALKOHOL“;//Used for Alcohol System
int nHungerHP = GetLocalInt(oMod,sHunger);// See htf_mod_onload
int nThirstHP = GetLocalInt(oMod,sThirst);// See htf_mod_onload
int nFatigueHP = GetLocalInt(oMod,sFatigue);// See htf_mod_onload
//Wasserflasche
object oCanteen;
int nCanteenLoads;// Zahl der Ladungen

//Gun-System (um Feuerwaffen im Gepaeck kuemmert sich „u_guns_on_enter“)
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
int nWeaponType = GetBaseItemType(oWeapon);
object oAmmunition;

/////////////////////////////////////////////////////////////
//
// Schlafmangel initialisieren:
//
/////////////////////////////////////////////////////////////
void ResetWeariness (object oPC)
{
int nSchlafSet = GetPersistentInt(oPC, „WearyStarted“);

if (nSchlafSet != 1)
{
SetLocalInt(oPC, „Schlafmangel“, 0);
SetPersistentInt(oPC, „WearyStarted“, 1);
SetPersistentInt(oPC, „Schlafmangel“, 0);
SendMessageToPC(oPC, „AE-Schlafmangel-System initialisiert.“);
}
else SendMessageToPC(oPC, „AE-Schlafmangel-System aktiv.“);
}

//////////////////////////////////////////////////////////
//
// HAUPTTEIL
//
//////////////////////////////////////////////////////////

void main()
{
//abbrechen, falls das Script bereits einmal fuer diese Spielfigur ausgefuehrt wurde:
if (nEinmal == 1)
{
return;//abbrechen und nichts weiter machen
}
///////////////////////////////////////////////

//ansonsten geht es weiter:
//Sichergehen, dass das Script nur einmal beim Start des Spielers ausgefuehrt wird, auch falls etwas schiefgeht:
SetLocalInt(oPC, „Gestartet?“, 1);

//Portal Atrium gilt immer als bekannt:
SetLocalInt(oPC, „KenntPO_ATRIUM“, 1);
SetLocalInt(oPC, „KenntAtrium“, 1);

if (!GetIsDM(oPC) && GetXP(oPC)== 0)
{
SetLocalString(oPC, „RespawnWP“, „AE_RESP_GENERAL“);//der allgemeine Respawn-Punkt, falls nicht anders definiert

//Heimatportal fuer Schriftrolle des Stadtportals einmalig setzen:
SetLocalString(oPC, „MyHomePortal“, „PO_ATRIUM“);

//den Schlafmangel-Ausgangswert setzen:
//ExecuteScript(„ae_weary_set_0“, oPC);// wird durch entspr Funktion ersetzt:
ResetWeariness(oPC);

//Willkommensnachricht definieren:
sWelcomeBack = ColorTokenWhite() +(„Viel Spass bei ‚Geschichten von der Alten Erde‘, „) +sSpieler +(„!“) +ColorTokenEnd();

object oSoulStone = GetItemPossessedBy(oPC, „U_SEELENSTEIN“);
if (!GetIsObjectValid(oSoulStone))
{
CreateItemOnObject(„u_seelenstein“, oPC, 1,““);
}
//fuer ALLE gueltige Startausruestung geben:
CreateItemOnObject(„u_messer1“, oPC, 1); // Behelfswaffe
CreateItemOnObject(„watercanteen“, oPC, 1); //Feldflasche
CreateItemOnObject(„torch“, oPC, 1); //CRAP-HTF-Fackel

//prueft, ob in „CRP_INC_CONTROL“ Hunger und Erschoepfungssystem aktiviert sind:
int nHUNGERSYSTEM = GetLocalInt(oMod,“HUNGERSYSTEM“);
int nFATIGUESYSTEM = GetLocalInt(oMod,“FATIGUESYSTEM“);

//HTF-Werte auf Ausgangswerte zuruecksetzen:

//Ausgangswerte definieren:
int nHUNGER = 6000;//Default 6000
int nTHIRST = 5500;//Default 5500
int nFATIGUE = 5000;//Default 5000

if ((nHUNGERSYSTEM=1) || (nFATIGUESYSTEM=1))
{
SetLocalInt(oPC,sHunger,nHungerHP);
SetLocalInt(oPC,sThirst,nThirstHP);
SetLocalInt(oPC,sFatigue,nFatigueHP);
SetLocalInt(oPC,sAlcTotal,0);
SetCampaignInt(„HTF“,sHunger,nHungerHP,oPC);
SetCampaignInt(„HTF“,sThirst,nThirstHP,oPC);
SetCampaignInt(„HTF“,sFatigue,nFatigueHP,oPC);
SetCampaignInt(„HTF“,sAlcTotal,0,oPC);
}

//Zum Rassenstartpunkt teleportieren: (wird derzeit nicht verwendet)
//ClearAllActions(TRUE);
//ApplyEffectToObject(DURATION_TYPE_INSTANT, eTransport, oPC, 3.0);
//AssignCommand(oPC, ActionJumpToObject(oStartALL, FALSE));

//Ruhm (=Ansehen in der Welt) auf 0 setzen
SetPersistentInt(oPC, „Ruhm“, 0);

//beim ersten Mal leeren Wert fuer Aktive Quest setzen:
SetPersistentString(oPC, „AktuelleQuest“, „“);

//Rastplatz zuruecksetzen:
SetPersistentInt(oPC, „SichereRast“, FALSE);

//verhindern, das obiges erneut ausgefuehrt wird:
SetPersistentInt(oPC, „Schonmal“, 1);

SetXP(oPC, 1);//egal, wie vorher die XP waren, jetzt auf 1 gesetzt
DelayCommand(2.0, FloatingTextStringOnCreature(sWelcomeBack, oPC, TRUE));
}

if (!GetIsDM(oPC) && GetXP(oPC) >= 2)//falls die Figur aus anderem Spiel importiert wurde
{
int nHasPlayed = GetPersistentInt(oPC, „Schonmal“);
if (nHasPlayed != 1)
{
//Willkommensnachricht definieren:
sWelcomeBack = ColorTokenWhite() +(„Vielen Dank fuer Dein Interesse an dieser Spielwelt, „) +sSpieler +(„!“) +ColorTokenEnd();

//der allgemeine Respawn-Punkt, falls nicht anders definiert
SetLocalString(oPC, „RespawnWP“, „AE_RESP_GENERAL“);

//Heimatportal fuer Schriftrolle des Stadtportals erstmalig setzen:
SetLocalString(oPC, „MyHomePortal“, „PO_ATRIUM“);

//beim ersten Mal leeren Wert fuer Aktive Quest setzen:
SetPersistentString(oPC, „AktuelleQuest“, „“);

//Ruhm (=Ansehen in der Spielwelt) auf 0 setzen:
SetPersistentInt(oPC, „Ruhm“, 0);

//Rastplatz-Ausgangswert setzen:
SetPersistentInt(oPC, „SichereRast“, FALSE);

//verhindern, das obiges erneut ausgefuehrt wird:
SetPersistentInt(oPC, „Schonmal“, 1);

//einmalig fuer Figuren aus anderen Spielen den Schlafmangel-Ausgangswert setzen:
ResetWeariness(oPC);
}

//vergewissern, dass der Spieler einen Seelenstein hat:
object oSoulStone = GetItemPossessedBy(oPC, „U_SEELENSTEIN“);
if (!GetIsObjectValid(oSoulStone))
{
CreateItemOnObject(„u_seelenstein“, oPC, 1,““);
}

//aus der SQL-Datenbank die Daten vom letzten Speicherstand abfragen(Persistenz, Exploit stoppen)

//Ruhm aus der Datenbank fuer das laufende Spiel eintragen:
int nFame = GetPersistentInt(oPC, „Ruhm“);
SetLocalInt(oPC, „Ruhm“, nFame);//(macht nichts, wenn dieser Wert fehlt, weil ohne Datenbank gespielt wird)

//HTF-System
int nHunger = GetPersistentInt(oPC, sHunger);
int nThirst = GetPersistentInt(oPC, sThirst);
int nFatigue = GetPersistentInt(oPC, sFatigue);
int nAlcTotal = GetPersistentInt(oPC, sAlcTotal);

//Rastplatz abfragen
int bRastplatz = GetPersistentInt(oPC, „SichereRast“);
SetLocalInt(oPC, „Rastplatz“, bRastplatz);

//aus der Datenbank die derzeit aktive Quest abfragen und einsetzen:
string sActiveQuest = GetPersistentString(oPC, „AktuelleQuest“);
SetLocalString(oPC, „AktuelleQuest“, sActiveQuest);

//Respawn-Punkt
string sRespawnWP = GetPersistentString(oPC,“RespawnWP“);
//falls es fehlt, weil ohne Datenbank gespielt wird:
if (sRespawnWP == „“) sRespawnWP = „AE_RESP_GENERAL“;
SetLocalString(oPC, „RespawnWP“, sRespawnWP);

//Wasserflasche-Ladungen
oCanteen = GetItemPossessedBy(oPC, „WaterCanteen“);
if(GetIsObjectValid(oCanteen))
{
nCanteenLoads = GetPersistentInt(oPC, „CHARGES“);
SetLocalInt(oCanteen, „CHARGES“, nCanteenLoads);
}
else if(!GetIsObjectValid(oCanteen))
{
CreateItemOnObject(„watercanteen“, oPC, 1);
}

//Gun-System
//int nGunTest = GetPersistentInt(oPC, „xGun“);
int nGunBulletTest = GetPersistentInt(oPC, „xGunBullet“);
int nGunWasLoaded = GetPersistentInt(oPC, „xLoadStatus“);
//Armor-Encumbrance
if ((GetStringLeft(GetResRef(oWeapon), 4) == „gun_“) || (GetStringLeft(GetResRef(oWeapon), 4) == „pst_“))
{
SetLocalInt(oWeapon, „nGun“, 1);
}

int nMode = GetPersistentInt(oPC, „EQUIP_MODE“);

//die Werte aus der Datenbank fuer das laufende Spiel eintragen
SetLocalInt(oPC,sHunger,nHungerHP);
SetLocalInt(oPC,sThirst,nThirstHP);
SetLocalInt(oPC,sFatigue,nFatigueHP);
SetLocalInt(oPC,sAlcTotal,0);
//und in die Kampagnen-Datenbank
SetCampaignInt(„HTF“,sHunger,nHungerHP,oPC);
SetCampaignInt(„HTF“,sThirst,nThirstHP,oPC);
SetCampaignInt(„HTF“,sFatigue,nFatigueHP,oPC);
SetCampaignInt(„HTF“,sAlcTotal,0,oPC);

if (nWeaponType == BASE_ITEM_HEAVYCROSSBOW || nWeaponType == BASE_ITEM_LIGHTCROSSBOW)
{
oAmmunition = GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC);
}
if (nWeaponType == BASE_ITEM_SLING)
{
oAmmunition = GetItemInSlot(INVENTORY_SLOT_BULLETS, oPC);
}

//SetLocalInt(oWeapon, „nGun“, nGunTest);
SetLocalInt(oAmmunition, „nGunBullet“, nGunBulletTest);
SetLocalInt(oWeapon, „nLoadStatus“, nGunWasLoaded);

// Armor-Encumbrance auf Spieler anwenden
SetLocalInt(oPC, „EQUIP_MODE“, nMode);
switch(nMode)
{
case 1: EffectArmorEncumbrance(OBJECT_SELF); return;
case 2: RemoveArmorEncumbrance(OBJECT_SELF); return;
}

////Anti-Cheat, damit nicht ein Toter importiert wird:
//int bCheatingDeath = GetPersistentInt(oPC, „IchBinTot“);
//if (bCheatingDeath == TRUE)
// {
//WP auf den Gefaengnisinseln definieren:
// object oWPKarzeri = GetWaypointByTag(„WP_KARZERI“);
// sWelcomeBack= ColorTokenRed() +(„Du solltest doch tot sein, oder, „) +sSpieler +(„?“) +ColorTokenEnd();
// AssignCommand(oPC, ActionJumpToObject(oWPKarzeri, FALSE));
// DelayCommand(4.0, FloatingTextStringOnCreature(sWelcomeBack, oPC, TRUE));
// }

//else if (bCheatingDeath == FALSE)
// {

// Falls schonmal mit Seelensein gespeichert wurde
// an den letzten Standort transportieren zum Weiterspielen

//aus der SQL-Datenbank den letzten Spieler-Standort abfragen
//(macht nichts, wenn dieser Wert fehlt, weil ohne Datenbank gespielt wird)
int bSoulUsed = GetPersistentInt(oPC, „SoulstoneUsed“);

if(bSoulUsed == TRUE)
{
location locPC = GetPersistentLocation(oPC, „LetzteLocation“);
SetLocalInt(oPC, „Teleported?“, 1);
AssignCommand(oPC, JumpToLocation(locPC));
}
DelayCommand(4.0, FloatingTextStringOnCreature(sWelcomeBack, oPC, TRUE));
// }

}

}

2 Comments

  1. Na, danke für den Einblick; ich hoffe, dass der Meister da den Überblick behält.
    Und zu Bugs fällt mir der schöne Film zu „Starship Troopers“, der gerade die Brainbugs so eindrucksvoll darstellt.

  2. Abschreckend, nicht wahr?
    Seit 2012 musste ich notgedrungen Fortschritte beim Scripting machen (man kann es an den unterschiedlich „eleganten“ Bestandteilen der Scripte sehen, die sich über ein paar Jahre weiterentwickelt haben), aber es ist trotzdem viel einfacher, ein neues Script zu schreiben, als Fehler in einem bestehenden zu beheben. Ist wie beim Hausbau.


Verfasse einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s