Kann es sein, dass man vorher jeden Befehl registrieren muss, denn man verwenden will, oder kann man direkt sagen: füge alle bekannten/geparsten Funktionen aus der Gothic.DAT hinzu?
Ich hatte das viel genereller gedacht. Anstatt all nötigen Funktion einzeln zu registrieren, habe ich eine Funktion geschrieben, die Funktionen aufrufen kann. Dabei ist die Funktionssignatur (Anzahl oder Typ der Argumente) nicht festgelegt, sondern wird automatisch ermittelt.
Das ganze sieht dann so aus.
Hier unsere Funktion, die einen String und zwei Integer als Argumente nimmt.
Code:
func string testFunc(var string str, var int a, var int b){
str = ConcatStrings(str, ": ");
str = ConcatStrings(str, IntToString(a+b));
return str;
};
So wie ich das geschrieben habe gibt es drei Einschränkungen (die man aber umgehen kann):
Die aufzurufene Funktion muss einen String zurückgeben (das kann man aber auch rausnehmen).
Die Argumente dürfen keine Leerzeichen enthalten (die Delimiter kann man aber auch in Kommas oder Semikolons ändern)
Nummern als Argumente werden immer als Integer und nie als String interpretiert
Zu beachten ist noch folgendes:
Es gibt keine Autovervollständigung für etwaige Funktionen (sonst müssten sie tatsächlich alle einzeln registriert werden). Man muss den Funktionsnamen und die Anzahl und Reihenfolge der Argument also genau wissen.
Es kann zu Abstürzen kommen, wenn man sich vertippt.
Für jedes String-Argument kommt eine Warnung im zSpy, aber das ist nicht weiter schlimm. Das liegt, daran wie getestet wird ob es sich um einen String oder um einen Integer handelt.
Code:
/*
* Call a deadalus function with arbitrary signature (arguments) from console
* Limitations:
* - The called function must return a string
* - No spaces in arguments allowed
* - Numbers are always interpreted as integers
*/
func string callFunc(var string command){// Retrieve number of arguments
var int nArgs; nArgs = STR_SplitCount(command, " ")-1;
// Loop over arguments and push them onto the stack
var int i;
repeat(i, nArgs);
// Get current argument
var string arg; arg = STR_Split(command, " ", i+1);
// Check if it is an integer or a string
if (Hlp_StrCmp(IntToString(STR_ToInt(arg)), arg)){
MEM_PushIntParam(STR_ToInt(arg));
} else {
MEM_PushStringParam(arg);
};
end;
// Call the function
MEM_CallByString(STR_Split(command, " ", 0));
// Return the return string of the function
return MEM_PopStringResult();
};
Kann es sein, dass man vorher jeden Befehl registrieren muss, denn man verwenden will, oder kann man direkt sagen: füge alle bekannten/geparsten Funktionen aus der Gothic.DAT hinzu?
Prinzipiell muss man jeden Befehl erst registrieren, aber wie mud-freak schon "angedeutet" hat kann man sich das natürlich so zurechtbauen, dass alle Daedalus-Funktionen über die Konsole verfügbar sind.
Ich habe den ConsoleCommands-Artikel im Wiki jetzt erstellt - für ein richtiges Beispiel habe ich heute keine Zeit mehr. Daher die ganz knappe Version:
Gibt man jetzt "myCommand" (ohne Anführungszeichen, Groß-/Kleinschreibung egal) in der Konsole ein, wird die Funktion sayNo() aufgerufen. Danach wird "No!" in der Konsole angezeigt (weil das eben der Rückgabewert ist).
Gibt man zum Beispiel "myCommand hello" in die Konsole ein, hat der Parameter s den Wert "hello" (im ersten Fall ist er einfach leer bzw. "").
Mein Skript war vielleicht etwas zu vorschnell. Wenn es um einzelne Funktionen geht, ist es besser diese einfach als Konsolenbefehl zu registrieren, wie Lehona schreibt.
Mir ging es um die Möglichkeit, jede Funktion aufrufen zu können, ohne diese vorher registrieren zu müssen. Ich habe gestern noch ein wenig weitergemacht mit der Spielerei. Aus dieser Spielerei könnte ein starkes Debugging-Tool werden, wenn sämtliche Funktionen mit jeglicher Art von Argument aus der Konsole aufrufbar werden. Deshalb will ich noch ein bisschen in der Idee verharren. Ich bin gestern nicht ganz fertig geworden, um den Code zu teilen, aber hier ein paar Sachen, die ich noch hinzugefügt habe:
Man kann nicht nur Strings und Integer als Argumente übergeben, sondern auch Variablen, und auch Instanzen (z.B. PC_Hero). Die werden automatisch erkannt. Damit kann man z.B. problemlos Funktionen aufrufen wie: B_StopLookAt(var C_NPC npc).
Der Returntyp einer Funktion wird automatisch ermittelt (und ggf. auf der Konsole ausgegeben), sodass man nicht daran gebunden ist und tatsächlich jede Funktion aufrufen kann (egal ob void, int, string oder Instanz).
Auch Externals können aufgerufen werden.
Was noch nicht ganz fertig ist, ist die Angleichung an die Skript Syntax um Strings von Variablen zu unterscheiden, also: Funktion(hero, "hero")
Warum ich es noch nicht teile ist, weil Instanzen als Argumente sich gegenseitig überschreiben. Z.B. ein Aufruf von B_Attack(PC_ThiefOw,hero,0,0) funktioniert nicht, da PC_ThiefOW mit hero überschrieben wird. Das muss ich noch korrigieren.
An den Rückgabetyp komm ich übrigens über das Parsersymbol der Funktion (sym.offset >> 12 == zPAR_TYPE_INT usw.). Hat jemand eine Idee, ob man auch die Argumente ermitteln kann? Ich gehe davon aus, dass das nicht geht. So kommt es nämlich immer zu Abstürzen, wenn die Anzahl oder Typ der Argumente nicht stimmt.
@Lehona: Danke für die Einträge zu CC im Wiki! Sieht sehr gut aus. Ich habe einen kleinen "Fehler" entdeckt. Das Argument description wird nicht angezeigt, falls man keine weiteren Parameter eingibt, sondern dient tatsächlich einfach als Beschreibung des Befehls, sollte man help in der Konsole eingeben. Ich weiss nicht, ob ich das im Wiki vielleicht nur missverstanden habe.
Hat jemand eine Idee, ob man auch die Argumente ermitteln kann?
Die Anzahl der Parameter sollte im Symbol der Funktion stehen und die Symbole der Parameter (sind Variablen mit <FuncName>.<ParamName>) sollten direkt darauf folgen und die Funktion als Parent haben.
"Unter diesen schwierigen Umständen bin ich mir sicher, daß diese guten Menschen meinen augenblicklichen Bedarf an deren Gold verstehen werden." -- Connor
Ich habe gerade den Code von vor einem halben Jahr wieder entdeckt und ihn zu Ende geschrieben. Der ist nicht besonders schön und vor allem nicht übersichtlich, aber ich hatte keine Lust mehr den noch weiter zu überarbeiten.
Damit steht nun eine, wie ich finde, sehr mächtige Debugging-Möglichkeit zur Verfügung:
Sämtliche Funktionen (ob Skriptfunktion oder External) lassen sich aus der Konsole aufrufen, egal was für Parametertypen oder Rückgabewerte.
Während man mit den ConsoleCommands schon im laufenden Spiel in alle Variablen hineinschauen und -schreiben kann (dazu muss man sich nur jeweilige Funktionen als ConsoleCommand registrieren), kann man jetzt beliebige Funktionen ausführen, die man nicht vorher zu einem ConsoleCommand zuschneiden muss.
Z.B. kann man NPCs jegliche AI_*-Funktionen ausführen lassen.
Code:
call AI_TurnToNpc pc_thief pc_hero
Nach call, kommt der Funktionsname, anschliessend die Argumente. Jeweils mit Leerzeichen getrennt (schönere Syntax war mir zu viel Arbeit, bzw. ist auch nervig beim Eingeben in der Konsole). Gross- und Kleinschreibung spielt keine Rolle. Mögliche Rückgabewerte der Funktion werden sinnvoll auf der Konsole ausgegeben.
Symbolnamen, so wie hier pc_thief, werden automatisch erkannt, sodass man für Integerparameter sowohl Zahlen als auch Intergerparameter angeben kann, z.B.
Code:
call inttostring 42
call inttostring SPL_Firerain
Ebenso geht das mit Strings
Code:
call print "Suchender"
call print NAME_Dementor
Ein wirklich sinnvolles Beispiel wäre das Triggern von Kapiteln:
Code:
call b_kapitelwechsel 3 CurrentLevel
Andere wirklich nützliche Beispiel konnte ich jetzt noch nicht bringen, aber man könnte diesen Konsolenbefehl auch präventiv in seine Mods einbauen, um Spielern mögliche Workarounds anzubieten, bis ein schwerwiegender Bug per Patch gefixt werden kann.
Skript:
Code:
/*
* Call any function (including externals) from console
*//*
* Initialization function
*/
func void CC_CallInit(){
CC_Register(CC_Call, "call ", "Call any daedalus or external function");
};
/*
* Push symbol of almost any type onto the stack
*/
func int CC_Call_PushSymbol(var int symbID){
var zCPar_Symbol symb; symb = _^(MEM_GetSymbolByIndex(symbID));
if ((symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_INSTANCE){// What is this: See MEMINT_PushStringParamSub()
var int n; n += 1; if (n == 10){ n = 0; };
if (n == 0){ var MEMINT_HelperClass inst0; inst0 = _^(symb.offset); MEM_PushInstParam(inst0); };
if (n == 1){ var MEMINT_HelperClass inst1; inst1 = _^(symb.offset); MEM_PushInstParam(inst1); };
if (n == 2){ var MEMINT_HelperClass inst2; inst2 = _^(symb.offset); MEM_PushInstParam(inst2); };
if (n == 3){ var MEMINT_HelperClass inst3; inst3 = _^(symb.offset); MEM_PushInstParam(inst3); };
if (n == 4){ var MEMINT_HelperClass inst4; inst4 = _^(symb.offset); MEM_PushInstParam(inst4); };
if (n == 5){ var MEMINT_HelperClass inst5; inst5 = _^(symb.offset); MEM_PushInstParam(inst5); };
if (n == 6){ var MEMINT_HelperClass inst6; inst6 = _^(symb.offset); MEM_PushInstParam(inst6); };
if (n == 7){ var MEMINT_HelperClass inst7; inst7 = _^(symb.offset); MEM_PushInstParam(inst7); };
if (n == 8){ var MEMINT_HelperClass inst8; inst8 = _^(symb.offset); MEM_PushInstParam(inst8); };
if (n == 9){ var MEMINT_HelperClass inst9; inst9 = _^(symb.offset); MEM_PushInstParam(inst9); };
return TRUE;
} else if ((symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_INT)
|| ((symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_FLOAT){
MEM_PushIntParam(symb.content);
return TRUE;
} else if ((symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_STRING){
MEM_PushStringParam(MEM_ReadString(symb.content));
return TRUE;
} else if ((symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_FUNC){
MEM_PushIntParam(symbID);
return TRUE;
} else {// zPAR_TYPE_CLASS
// zPAR_TYPE_PROTOTYPE
return FALSE;
};
};
/*
* Console command function to call any deadalus or external function from the console in-game
*/
func string CC_Call(var string query){
if (Hlp_StrCmp(query, "")) || (Hlp_StrCmp(query, " ")){
return "";
};
// Find function name
var string funcName;
var string args;
var int i; i = STR_IndexOf(query, " ");
if (i != -1){
funcName = STR_SubStr(query, 0, i);
args = STR_SubStr(query, i, STR_Len(query)-i);
} else {
funcName = query;
args = "";
};
// Find function symbol by function name
var int fSymbID; fSymbID = MEM_GetSymbolIndex(funcName);
if (fSymbID < 0) || (fSymbID >= currSymbolTableLength){
return ConcatStrings("Function not found: ", funcName);
};
var zCPar_Symbol fSymb; fSymb = _^(MEM_GetSymbolByIndex(fSymbID));
// Verify that it is a function
if ((fSymb.bitfield & zCPar_Symbol_bitfield_type) != zPAR_TYPE_FUNC){
return ConcatStrings("Symbol is not a function: ", funcName);
};
// Get number of function paramerters
var int numParams; numParams = (fSymb.bitfield & zCPar_Symbol_bitfield_ele);
// Get return type
var int retType; retType = fSymb.offset << 12;
// Validate arguments against parameters
if (numParams){// Remaining argument string
var string rArgs; rArgs = args;
var zString zrArgs; zrArgs = _^(_@s(rArgs));
// Parameter types
const string SYMBOL_TYPES[8] = {"void", // zPAR_TYPE_VOID
"float", // zPAR_TYPE_FLOAT
"int", // zPAR_TYPE_INT
"string", // zPAR_TYPE_STRING
"class", // zPAR_TYPE_CLASS
"func", // zPAR_TYPE_FUNC
"prototype", // zPAR_TYPE_PROTOTYPE
"instance"// zPAR_TYPE_INSTANCE
};
// Loop over paramter-argument pairs and see if they match in type and push argument if so
repeat(i, numParams);
// Get next parameter type
var zCPar_Symbol pSymb; pSymb = _^(MEM_GetSymbolByIndex(fSymbID+1+i));
var int pType; pType = pSymb.bitfield & zCPar_Symbol_bitfield_type;
var string pTypeS; pTypeS = MEM_ReadStatStringArr(SYMBOL_TYPES, pType >> 12);
// Set up error message
var string errStr1;
errStr1 = ConcatStrings("Argument #", IntToString(i+1));
errStr1 = ConcatStrings(errStr1, ": ");
// Check if any arguments provided
if (zrArgs.len < 1){
return ConcatStrings("Not enough arguments. Expected ", IntToString(numParams));
};
// Extract start of next argument from query
var int j; j = 0;
var int l; l = MEM_ReadByte(zrArgs.ptr+j);
while(l == 32); // Skip spaces
if (zrArgs.len <= j+1){
return ConcatStrings("Not enough arguments. Expected ", IntToString(numParams));
};
j += 1;
l = MEM_ReadByte(zrArgs.ptr+j);
end;
// Update remaining argument string
rArgs = STR_SubStr(rArgs, j, zrArgs.len-j);
// Check first character
var int firstChar; firstChar = MEM_ReadByte(zrArgs.ptr);
var int argIsStr; argIsStr = (firstChar == 34); // Double quotes
var int argIsNumber; argIsNumber = ((firstChar >= 48) && (firstChar <= 57)) || (firstChar == 45); // Digit
// Match types
if (argIsStr){
if (pType != zPAR_TYPE_STRING){
return ConcatStrings(ConcatStrings(errStr1, "Expected type "), pTypeS);
};
// Iterate over query to find end of string
j = 1;
l = MEM_ReadByte(zrArgs.ptr+j);
while(l != 34); // Double quote
if (zrArgs.len <= j+1){
return ConcatStrings(errStr1, "Unexpected end of string");
};
j += 1;
l = MEM_ReadByte(zrArgs.ptr+j);
end;
// Push string parameter
MEM_PushStringParam(STR_SubStr(rArgs, 1, j-1));
// Update remaining arguments string
rArgs = STR_SubStr(rArgs, j+1, zrArgs.len-(j+1));
} else if (argIsNumber){
var int argIsFloat; argIsFloat = FALSE;
var string argInt; argInt = "";
var string argDec; argDec = "";
// Iterate over query to find argument offset (space) or decimal point
j = 0;
l = MEM_ReadByte(zrArgs.ptr+j);
while(l != 32); // Space
if (MEM_ReadByte(zrArgs.ptr+j) == 46){// Decimal point
if (argIsFloat){
return ConcatStrings(errStr1, "Syntax error '.'");
};
argIsFloat = TRUE;
} else if (MEM_ReadByte(zrArgs.ptr+j) == 45){
if (j != 0){
return ConcatStrings(errStr1, "Syntax error '-'");
};
argInt = "-";
} else if (MEM_ReadByte(zrArgs.ptr+j) < 48) || (MEM_ReadByte(zrArgs.ptr+j) > 57){
return ConcatStrings(ConcatStrings(errStr1, "Syntax error "), STR_SubStr(rArgs, j, 1));
} else {
if (argIsFloat){
argDec = ConcatStrings(argDec, STR_SubStr(rArgs, j, 1));
} else {
argInt = ConcatStrings(argInt, STR_SubStr(rArgs, j, 1));
};
};
if (zrArgs.len <= j+1){
break;
};
j += 1;
l = MEM_ReadByte(zrArgs.ptr+j);
end;
rArgs = STR_SubStr(rArgs, j, zrArgs.len-j);
var int argIntI; argIntI = STR_ToInt(argInt);
// If an integer is expected and a float is provided, it will not be rounded but passed as integer float
if (argIsFloat){
if (pType != zPAR_TYPE_FLOAT){
return ConcatStrings(ConcatStrings(errStr1, "Expected type "), pTypeS);
};
// Shift decimal numbers to the left beyond decimal point
var int argDecF; argDecF = mkf(STR_ToInt(argDec));
repeat(k, STR_Len(argDec)); var int k;
argDecF = mulf(argDecF, castToIntf(0.1)); // n*0.1 = 0.n
end;
// Add integer to decimal points
argIntI = addf(mkf(argIntI), argDecF);
} else if (pType == zPAR_TYPE_FLOAT){// Turn integer into integer float
argIntI = mkf(argIntI);
} else if (pType != zPAR_TYPE_INT){// Check if it might be a valid symbol ID
if (argIntI < 0) || (argIntI >= currSymbolTableLength){
return ConcatStrings(errStr1, "Invalid symbol index");
};
// Check symbol type (exception: expected float, given integer)
var zCPar_Symbol iSymb; iSymb = _^(MEM_GetSymbolByIndex(argIntI));
if ((iSymb.bitfield & zCPar_Symbol_bitfield_type) != pType)
&& (!(((iSymb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_INT)
&& (pType == zPAR_TYPE_FLOAT))){
return ConcatStrings(ConcatStrings(errStr1, "Expected type "), pTypeS);
};
// Self, other and item are difficult to control
if (Hlp_StrCmp(iSymb.name, "self"))
|| (Hlp_StrCmp(iSymb.name, "other"))
|| (Hlp_StrCmp(iSymb.name, "victim"))
|| (Hlp_StrCmp(iSymb.name, "item")){
return ConcatStrings(errStr1, "Symbol is not allowed");
};
// Push symbol
if (!CC_Call_PushSymbol(argIntI)){
return ConcatStrings(ConcatStrings(errStr1, "Don't know how to push type "), pTypeS);
};
continue;
};
// Push integer or float integer parameter
MEM_PushIntParam(argIntI);
} else {// Symbol name
// Get end of argument
j = 0;
l = MEM_ReadByte(zrArgs.ptr+j);
while(l != 32); // Space
j += 1;
l = MEM_ReadByte(zrArgs.ptr+j);
if (zrArgs.len <= j){
break;
};
end;
// Check if valid symbol
var int aSymbID; aSymbID = MEM_GetSymbolIndex(STR_Prefix(rArgs, j));
if (aSymbID < 0) || (aSymbID >= currSymbolTableLength){
return ConcatStrings(errStr1, "Invalid symbol name");
};
// Validate symbol type (exception: expected float, given integer)
var zCPar_Symbol aSymb; aSymb = _^(MEM_GetSymbolByIndex(aSymbID));
if ((aSymb.bitfield & zCPar_Symbol_bitfield_type) != pType)
&& (!(((aSymb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_INT) && (pType == zPAR_TYPE_FLOAT))){
return ConcatStrings(ConcatStrings(errStr1, "Expected type "), pTypeS);
};
// Self, other and item are difficult to control
if (Hlp_StrCmp(aSymb.name, "self"))
|| (Hlp_StrCmp(aSymb.name, "other"))
|| (Hlp_StrCmp(aSymb.name, "victim"))
|| (Hlp_StrCmp(aSymb.name, "item")){
return ConcatStrings(errStr1, "Symbol is not allowed");
};
// Push symbol
if (!CC_Call_PushSymbol(aSymbID)){
return ConcatStrings(ConcatStrings(errStr1, "Don't know how to push type "), pTypeS);
};
// Update remaining arguments string
rArgs = STR_SubStr(rArgs, j, zrArgs.len-j);
};
end;
} else if (!Hlp_StrCmp(args, "")) && (!Hlp_StrCmp(args, " ")){
return "Function does not take arguments";
};
// Check if return type can be popped, otherwise don't allow the call!
if (retType > zPAR_TYPE_CLASS) && (retType < zPAR_TYPE_INSTANCE){
return "Return type of function not supported";
};
// Call function
MEM_CallByID(fSymbID);
// Pop return value if
if (retType == zPAR_TYPE_VOID){
return "return (void)";
} else if (retType == zPAR_TYPE_STRING){
var string resStr; resStr = MEM_PopStringResult();
return ConcatStrings(ConcatStrings("return '", resStr), "'");
} else if (retType == zPAR_TYPE_INT){
var int resInt; resInt = MEM_PopIntResult();
return ConcatStrings(ConcatStrings("return ", IntToString(resInt)), " (int)");
} else if (retType == zPAR_TYPE_FLOAT){
var int resF; resF = MEM_PopIntResult();
return ConcatStrings(ConcatStrings("return ", toStringf(resF)), " (float)");
} else {// zPAR_TYPE_INSTANCE
var MEMINT_HelperClass result; result = MEM_PopInstResult();
// The instance is now CC_CALL.RESULT (no gain in information ever)
// Instead return the pointer
var int rPtr; rPtr = _@(result);
return ConcatStrings(ConcatStrings("return *", IntToString(rPtr)), " (instance)");
};
};
/*
* Example getter and setter functions for strings, integers and floats
*/
func string getS(var string s){
return s;
};
func int getI(var int i){
return i;
};
func float getF(var float f){
MEM_ReadInt(_@f(f));
};
func float getIF(var int f){// From floats.d
f;
};
func int getFI(var float f){// From floats.d
return MEM_ReadInt(_@f(f));
};
func int verifySymbol(var string name, var int type){
var int symPtr; symPtr = MEM_GetParserSymbol(name);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
if ((sym.bitfield & zCPar_Symbol_bitfield_type) == type)
&& ((sym.bitfield & zCPar_Symbol_bitfield_ele) == 1){
return symPtr;
};
};
return 0;
};
func string setS(var string name, var string val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_STRING);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
MEM_WriteString(sym.content, val);
return "Done";
};
return "Invalid symbol";
};
func string setI(var string name, var int val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_INT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = val;
return "Done";
};
return "Invalid symbol";
};
func string setF(var string name, var float val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_FLOAT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = castToIntf(val);
return "Done";
};
return "Invalid symbol";
};
func string setIF(var string name, var float val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_INT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = castToIntf(val);
return "Done";
};
return "Invalid symbol";
};
func string setFI(var string name, var int val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_FLOAT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = val;
return "Done";
};
return "Invalid symbol";
};
In der Init_Global() registrieren:
Code:
CC_CallInit();
Geändert von mud-freak (30.03.2019 um 15:48 Uhr)
Grund: Funktioniert jetzt auch mit Funktionen als Parameter, Syntaxfehler behoben, negative Integer/Floats nun als Argumente unterstützt, Getter- und Setterfunktionen hinzugefügt
Top, danke Mud-Freak, das ist echt 'ne große Hilfe.
Jetzt müssen die Leute das nur noch verwenden, das spart viel Zeit, im vergleich zum Bauen und deployen eines Patches
Ich habe das Skript oben aktualisiert, sodass nun auch negative Integer/Floats als Argumente erlaubt sind. Das hatte ich bei der Implementierung übersehen.
Ließe sich damit auch etwas machen, um beliebige Variablen sehen / setzen zu können? Bisher klang es für mich so, als müsste ich für jede Variable eine Funktion erstellen.
Ließe sich damit auch etwas machen, um beliebige Variablen sehen / setzen zu können? Bisher klang es für mich so, als müsste ich für jede Variable eine Funktion erstellen.
Du kannst dir ja eine Funktion schreiben, die den Wert einer Variable (via Namen) liest/setzt.
Das funktioniert jetzt allerdings nur mit Ints, entweder schreibst du dir für die anderen Typen (Int/String/Instance) jeweils eine eigene Funktion oder du musst in die Symboleigenschaften gucken, was für ein Typ die Variable hat.
Der Vollständigkeit halber, habe ich hier Getter- und Setter-Funktionen für Strings, Integers, Floats und Integerfloats (funktioniert für Variablen und Konstanten). Man kann einen Integerfloat auch bequem als Float definieren. EDIT: Was aber nicht geht sind statische Arrays.
Anwendung:
Code:
call getS str1 // Get string, output: "hello world"
call getI int1 // Get integer, output: 42
call getF float1 // Get float, output: 1.234
call getIF intf1 // Get int as float, output: 2.345
call getFI float1 // Get float as int, output: 1067316150
call setS "str1" "hello world" // Set string
call setI "int1" 42 // Set integer
call setF "float1" 1.234 // Set float
call setIF "intf1" 2.345 // Set integer with float
call setFI "float1" 1067316150 // Set float with integer
Die Notwendigkeit der Anführungszeichen in den Setter-Funktionen lässt sich leider nicht ändern. Ich hänge die Funktionen an das Skript an, damit sie gleich immer dabei sind.
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* Example getter and setter functions for strings, integers and floats
*/
func string getS(var string s){
return s;
};
func int getI(var int i){
return i;
};
func float getF(var float f){
MEM_ReadInt(_@f(f));
};
func float getIF(var int f){// From floats.d
f;
};
func int getFI(var float f){// From floats.d
return MEM_ReadInt(_@f(f));
};
func int verifySymbol(var string name, var int type){
var int symPtr; symPtr = MEM_GetParserSymbol(name);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
if ((sym.bitfield & zCPar_Symbol_bitfield_type) == type)
&& ((sym.bitfield & zCPar_Symbol_bitfield_ele) == 1){
return symPtr;
};
};
return 0;
};
func string setS(var string name, var string val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_STRING);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
MEM_WriteString(sym.content, val);
return "Done";
};
return "Invalid symbol";
};
func string setI(var string name, var int val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_INT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = val;
return "Done";
};
return "Invalid symbol";
};
func string setF(var string name, var float val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_FLOAT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = castToIntf(val);
return "Done";
};
return "Invalid symbol";
};
func string setIF(var string name, var float val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_INT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = castToIntf(val);
return "Done";
};
return "Invalid symbol";
};
func string setFI(var string name, var int val){
var int symPtr; symPtr = verifySymbol(name, zPAR_TYPE_FLOAT);
if (symPtr){
var zCPar_Symbol sym; sym = _^(symPtr);
sym.content = val;
return "Done";
};
return "Invalid symbol";
};
Was noch gefehlt hat war, dass Skriptfunktionen beim Eintippen auto-vervollständigt werden, so wie man das von anderen Konsolenbefehlen gewöhnt ist. Das habe ich oben im Skript hinzugefügt.
Beispiel: Schreibe ich in die Konsole "call hello" wird daraus "call helloworld", sollte eine Funktion mit dem Namen in den Skripten enthalten sein und keine andere Funktion mit "hello" im Namen beginnen.
Die entsprechende Änderung werde ich dann auch bald in den Workaround-Patch aufnehmen.
Was noch gefehlt hat war, dass Skriptfunktionen beim Eintippen auto-vervollständigt werden, so wie man das von anderen Konsolenbefehlen gewöhnt ist. Das habe ich oben im Skript hinzugefügt.
Beispiel: Schreibe ich in die Konsole "call hello" wird daraus "call helloworld", sollte eine Funktion mit dem Namen in den Skripten enthalten sein und keine andere Funktion mit "hello" im Namen beginnen.
Die entsprechende Änderung werde ich dann auch bald in den Workaround-Patch aufnehmen.
Ich habe diese Autovervollständigung wieder aus dem Skript oben heraus genommen, da dadurch Ruckler beim Eintippen in der Konsole entsanden sind (sämtliche Daedalus und externe Funktionen sind halt etwas viele). Falls jemand den entsprechenden Code trotzdem haben will, habe ich ihn hier noch einmal separat.
Code:
// Register auto-complete for all functions
const int once = 0;
if (!once){
repeat(i, MEM_Parser.symtab_table_numInArray); var int i;
var zCPar_Symbol symb; symb = _^(MEM_ReadIntArray(MEM_Parser.symtab_table_array, i));
if ((symb.bitfield & zCPar_Symbol_bitfield_type) == zPAR_TYPE_FUNC){
CC_AutoComplete(ConcatStrings("CALL ", symb.name), "");
};
end;
once = 1;
};
Wer dennoch Autcompletion für alle Funktionen haben möchte und sehr viel Langeweile hat, könnte die entsprechende Enginefunktion hooken und einen eigenen Lookup einbauen. Mit einem PATRICIA-Trie geht der Lookup in logarithmischer Zeit, unabhängig von der Menge der registrierten Funktionen und die Implementierung ist gar nicht mal so schwierig (gibt auch einige Beispiele im Netz)