Ich glaub wir sollten den Skriptbin mal besser organisieren. Hier mischen sich Skript und Diskussion dazu, sodass der eigentliche Sinn der puren Sammlung zerstört wird.
Das heißt nicht, dass ich Diskussionen zu Skripten schlecht finde. Ich finde nur, dass wir die Diskussion von der Sammlung trennen sollten.
Und ein alphabetisches Verzeichnis wäre auch ganz gut. Sollen wir einen neuen Thread machen, in den Strikt nur die eigentlichen Skripte kommen und wo der Startpost das Verzeichnis enthält? Was meint ihr dazu?
Du sprichst mir aus der Seele, bloß wer will das schon machen? Es sind ja nun wirklich viele Scripte auf zig Seiten verteilt und dann müsste der jenige auch min 5-10 Jahre noch aktiv im Forum sein, um alles weiterhin zu verwalten.
genau aus diesem Grund habe ich angefangen mir vorerst mein eigenes "script bin" zu schreiben
Darin kann ich zumindest schnell suchen und ggf. auch noch Beispiele einbinden, tags, kategorien und andere nette schmankerl inbegriffen
Dort schmeiße ich mir dann immer mal wieder das ein oder andere gefundene Skript rein, mit quelle, datum, author, allem pi-pa-po
Dort wird es wahrscheinlich ähnlich sein wie mit der ursprünglichen Absicht dieses Threads: Ein Repository mit allen Skripten (oder auch nur eine einfache Verlinkung aller Skripte im Einleitungsthread) ist einfach eine Aufgabe, die gewisse Pflege und Zeit beansprucht. Solange es nur von einer Person verwaltet wird, kommt irgendwann der Punkt, dass sie nicht mehr so aktiv selbst moddet oder anderweitig keine Zeit dafür mehr hat. Abgesehen von einer Art Reviewing-Process, ob ein gepostetes Skript als bugfrei mit guten Gewissen in so einer Liste angeboten werden sollte.
Eine vollständige Verlinkung der Skripte im Einleitungspost (wie ein Inhaltsverzeichnis) ist sicher schön, aber die erfordert wie deine praktische Datenbank gewisse "lebenslange" Pflege. Am schönsten wäre es, wenn (d)eine Datenbank die Skripte automatisch aus diesem Thread (oder sogar dem Forum) filtern und auflisten könnte, sodass sie niemand pflegen muss und irgendwann niemand mehr Zugriff auf die Pflege hat. Dabei wäre meine Sorge, dass schon wieder ein neuer "SkriptBin" entsteht. Nicht zu vergessen, dass es im Wiki eine Liste von nützlichen Skripts gibt, im Editing FAQ meines Wissens nach auch, im Einleitungspost von Ikarus und in dem von LeGo auch. Wenn neben dem SkriptBin noch weitere Sammelungen entstehen, wird es irgendwann ganz im Gegenteil schwieriger Skripte zu finden, weil man im schlimmsten Falle alle dieser Sammlungen durchsuchen muss.
Ich finde das jetzige Format vom Skriptbin ehrlich gesagt gar nicht so schlecht und sehe kein Handlungsbedarf. Die Suche von gewissen Skripten (oder eine Verlinkung dazu) lässt sich vom gesamten Forum auf einen einzigen Thread reduzieren. Das finde ich eine enorme Hilfe. Und selbst, wenn es zwischen den Skript-Posts keine Diskussionen gäbe wäre das Finden von Skripten ebenso "viel Arbeit" als ohne.
Dort wird es wahrscheinlich ähnlich sein wie mit der ursprünglichen Absicht dieses Threads: Ein Repository mit allen Skripten (oder auch nur eine einfache Verlinkung aller Skripte im Einleitungsthread) ist einfach eine Aufgabe, die gewisse Pflege und Zeit beansprucht. Solange es nur von einer Person verwaltet wird, kommt irgendwann der Punkt, dass sie nicht mehr so aktiv selbst moddet oder anderweitig keine Zeit dafür mehr hat. Abgesehen von einer Art Reviewing-Process, ob ein gepostetes Skript als bugfrei mit guten Gewissen in so einer Liste angeboten werden sollte.
...
Die sache ist die, das dieser "script bin" welchen ich da habe mit "Hugo" realisiert ist.
An sich kann man problemlos ein GitHub Repo davon aufmachen und zu jederzeit dinge aktualisieren oder das ding einfach forken und einen neuen Github.io Link irgendwo hin setzen.
Gemeinsame kollaboration sind super einfach (vorausgesetzt git kenntnisse, oder halt eine belegschaft welche über Issues Skripte aktualisiert/korrigiert)
Und auch das wegsterben ist kein problem, da man dies einfach forken könnte und weitermachen kann wo es gestoppt ist.
Ich selbst schreibe da nur skripte rein, welche ich mal benutzt habe. Vielleicht setzte ich das ding irgendwann mal global auf wie beschrieben.
- Benötigt Ikarus
- Getestet mit G2 (G1 sollte auch funktionieren)
Vob_KnockbackNpc.d
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
Author : Kirides
Date : 13.03.2021
Version: 1
Topics:
- https://forum.worldofplayers.de/forum/threads/1574543-NPC-zuverl%C3%A4ssig-wegschleudern-%28%C3%A4hnl-Windfaust%29
Functions:
Wld_KnockbackNpc(int posPtr, C_NPC targetNpc, int strength)
Vob_KnockbackNpc(zCVob vob , C_NPC targetNpc, int strength)
Npc_KnockbackNpc(C_NPC src , C_NPC targetNpc, int strength)
Wld_KnockbackNpc:
Takes in the source position zVEC3 as a pointer, aswell as
the target npc and the strength of the pushback
Vob_KnockbackNpc:
Takes in any zCVob for source position, aswell as the target
npc and the strength of the pushback
Npc_KnockbackNpc
Takes in any npc for source position, aswell as the target
npc and the strength of the pushback
Changes:
-
*/
func void Wld_KnockbackNpc(var int posPtr, var C_NPC targetNpc, var int strength) {
if (!Hlp_IsValidNpc(targetNpc)) { return; };
// do not apply to enemy that is on the ground
if (Npc_GetBodyState(targetNpc) == BS_LIE) {
return;
};
// Get engine types, so we get access to their world position and AI
var oCNpc oTar; oTar = MEM_CpyInst(targetNpc);
if (!oTar.human_ai) { return; };
var int humAI; humAI = oTar.human_ai;
var int strF; strF = mkf(strength);
const int zVEC3__NormalizeSafe_G1 = 4900544; // 0x4AC6C0
const int zVEC3__NormalizeSafe_G2 = 4819488; // 0x498A20
const int oCAiHuman__StartFlyDamage_G1 = 6381888; // 0x616140
const int oCAiHuman__StartFlyDamage_G2 = 6936896; // 0x69d940
const int sizeof_zVEC3 = 12; // 0x000C
// Get vector "away from source"
var int dirVec[3];
var int dirPtr; dirPtr = _@(dirVec);
// Get source direction
MEM_CopyBytes(posPtr, dirPtr, sizeof_zVEC3);
// substract source from target position to get the "away direction"
dirVec[0] = subf(oTar._zCVob_trafoObjToWorld[zCVob_trafoObjToWorld_X], dirVec[0]);
dirVec[1] = subf(oTar._zCVob_trafoObjToWorld[zCVob_trafoObjToWorld_Y], dirVec[1]);
dirVec[2] = subf(oTar._zCVob_trafoObjToWorld[zCVob_trafoObjToWorld_Z], dirVec[2]);
// Normalize the direction vector
const int call = 0;
if (CALL_Begin(call)) {
CALL__thiscall(_@(dirPtr), MEMINT_SwitchG1G2(zVEC3__NormalizeSafe_G1, zVEC3__NormalizeSafe_G2));
call = CALL_End();
};
// Just to clarify intent: Keep "minimum fall height until damage"
// Set to "TRUE" to enable fall damage without minimum height
oTar.overrideFallDownHeight = FALSE;
// Start actualy flying damage, well technically not really damage
// This applies the direction force and sets the animations
const int call2 = 0;
if (CALL_Begin(call2)) {
CALL_PtrParam(_@(dirPtr));
CALL_FloatParam(_@(strF));
CALL__thiscall(_@(humAI), MEMINT_SwitchG1G2(oCAiHuman__StartFlyDamage_G1, oCAiHuman__StartFlyDamage_G2));
call2 = CALL_End();
};
};
func void Vob_KnockbackNpc(var zCVob vob, var C_NPC targetNpc, var int strength) {
var int pos[3];
pos[0] = vob.trafoObjToWorld[zCVob_trafoObjToWorld_X];
pos[1] = vob.trafoObjToWorld[zCVob_trafoObjToWorld_Y];
pos[2] = vob.trafoObjToWorld[zCVob_trafoObjToWorld_Z];
Wld_KnockbackNpc(_@(pos), targetNpc, strength);
};
func void Npc_KnockbackNpc(var C_NPC src, var C_NPC targetNpc, var int strength) {
if (!Hlp_IsValidNpc(src)) { return; };
var zCVob vob; vob = MEM_CpyInst(src);
Vob_KnockbackNpc(vob, targetNpc, strength);
};
ich grabe hier mal ein bisschen, da ich vor einem kleinen Rätsel stehe:
Ich möchte mit den Ikarus-Skripten für VOBs entfernen und einfügen ein Boot einfügen, mit dem man zu einer Insel kommt. Die Idee ist, das Boot an zwei Punkten jeweils wechselseitig zu entfernen und einzufügen, wenn der Player das wünscht (mit ner Überblendung und so, tut jetzt aber nichts zur Sache). Das funktioniert auch an sich wunderbar, das Boot wechselt den Standort. Allerdings wird es nur beim ersten einfügen, das in der Startup gesetzt wird korrekt am WP platziert, danach wird es bei jeder Platzierung zwar am korrekten Standort eingefügt, allerdings auf dem Grund des Meeres. Dabei sind die Befehle und Parameter (FALSE bei align to floor) bei StartUp und in der Funktion genau gleich.
Hier die Skripte:
In der Startup:
Und nur beim ersten Einfügen bei Spielstart landet das Boot "schwimmend" auf der Höhe des WP, bei allem, was dann durch den PC getriggert wird auf Grund.
Jemand eine Idee?
PS: In der InsertAnything.d hab ich in der GetTrafoFromWP Funktion das AlignWPToFloor schon in eine if-Abfrage geschoben, wo ich dann immer FALSE übergebe, siehe unten. Vorher war nämlich das Boot auch am Anfang schon unter Wasser...
Code:
func int GetTrafoFromWP(var String waypoint, var int setToGround) {
// Hole Waypoint
const int zCWayNet__GetWaypoint = 8061744; //0x7B0330
CALL__fastcall(_@(MEM_Waynet), _@s(waypoint), zCWayNet__GetWaypoint);
var int wpPtr; wpPtr = CALL_RetValAsInt();
if (!wpPtr){ MEM_Warn("GetTrafoFromWP: Waypoint not found."); return 0; };
// Leiste Spacernacharbeit und richte den Waypoint richtig aus (Höhe)
//AlignWPToFloor(wpPtr);
// Baue Traformationsmatrix von Position und Ausrichtung
var zCWaypoint wp; wp = _^(wpPtr);
var int pos[3]; MEM_CopyWords(_@(wp.pos), _@(pos), 3);
if (setToGround)
{
AlignWPToFloor(wpPtr);
pos[1] = subf(pos[1], mkf(50)); }; // Korrigiere Hoehe
return PosToTrf(_@(pos), _@(wp.dir), 1, 0);
};
Ich würde das Vob an beiden Orten per Spacer setzen und dann über Ikarus nur das Vob ein und ausblenden. In LoA platzieren wir so ab einem späten Kapitel einige Schiffe im Meer. Ich fand das deutlich angenehmer im Spacer die Schiffe ganz genau auszurichten, als das mit einem WP zu machen.
Du meinst, die Visibility? Haben die denn dann nicht trotzdem noch Kollision, so dass man unter Umständen gegen ein unsichtbares Objekt schwimmt zum Beispiel?
Und brauche ich dafür nicht auch einen Pointer auf das VOB? Wo kriege ich den her, wenn nicht durch das Einfügen des VOBs wie oben?
Du meinst, die Visibility? Haben die denn dann nicht trotzdem noch Kollision, so dass man unter Umständen gegen ein unsichtbares Objekt schwimmt zum Beispiel?
Und brauche ich dafür nicht auch einen Pointer auf das VOB? Wo kriege ich den her, wenn nicht durch das Einfügen des VOBs wie oben?
Die Kollision kannst du via Script ausschalten (cdDynamic im zCVob bitfield, glaube ich). Vobs können einen Namen haben, und dann kannst du mit MEM_SearchVob (oder so) einen Pointer auf das Vob bekommen
Vielen Dank, das schau ich mir mal an. Das VOBs Namen haben, war mir schon klar, allerdings wusste ich nicht, woher man die Pointer dann bekommt^^. Dann such ich mal nach der Funktion.
Ich hatte gestern den Code nicht zur Hand, deshalb jetzt hier verspätet die passenden Funktionen:
Code:
func int ShowVob(var string vobname)
{
var int ptr; ptr = MEM_SearchVobByName(vobname);
if (ptr)
{
var zCVob myvob; myvob = _^(ptr);
myvob.bitfield[0] = myvob.bitfield[0] | zCVob_bitfield0_showVisual;
return true;
};
return false;
};
func int HideVob(var string vobname)
{
var int ptr; ptr = MEM_SearchVobByName(vobname);
if (ptr)
{
var zCVob myvob; myvob = _^(ptr);
myvob.bitfield[0] = myvob.bitfield[0] & ~zCVob_bitfield0_showVisual;
return true;
};
return false;
};
Zu Spielbeginn sind die im Spacer eingefügten Vobs da -- du möchtest also vermutlich zumindest für eins der beiden Boote HideVob in der Startup aufrufen und dann jeweils beim Wechsel einmal HideVob(<Ursprung>) und ShowVob(<Ziel>). Bei uns beispielsweise: ShowVob("K5_SHIP_1");. Die Funktion gibt, zu Testzwecken zurück, ob solch ein Vob gefunden wurde (true) oder nicht (false). Falls du das nicht brauchst, einfach aus int ein void machen und die returns entfernen.
Wenn ich mich richtig erinnere, reichte ~zCVob_bitfield0_showVisual schon aus, damit man nicht mehr mit der leeren Luft kollidiert. Im Spacer sollte also dynamische Kollision eingestellt sein. Darfst du aber gerne zur Sicherheit noch mal testen .
Vielen Dank, habs gerade eingefügt und klappt super
War schon ein wenig am verzweifeln, hab in den ikarus skripten rumgesucht, aber schlussendlich viel einfacher als gedacht^^. Die Funktionen klappen genauso, wie ich wollte, danke nochmal!
Ich möchte das Script screenFade nutzen und konnte es auch schon im Spiel ausprobieren. Aber wie realisiere ich, dass sich der Bildschirm erst nach einer bestimten Aktion (Dialog, Animation, toter NPC oder ähnliches) abgespielt wird? Ich habe es im Dialoge mit IF versucht und mit Events hat es ebenfalls nicht funktioniert.
Gedacht habe ich es mir so:
Code:
....
AI_Output (self, other, "DIA_BDT_99000_NPC1_DIALOG_1_3"); //Hallo
//Held wird erschlagen
AI_GotoNpc(self, hero);
AI_PlayAni (self, "T_1HATTACKMOVE");
AI_WaitTillEnd(hero, self);
AI_PlayAni (hero, "T_STAND_2_WOUNDED");
//Begleiter des Helden wird erschlagen
B_Attack (self, MIL_99000_NPC2, AR_KILL, 1); // <---- Der Begleiter
AI_StopProcessInfos(hero);
if (Npc_IsDead (MIL_99000_NPC2)) // <---- Wenn der Begleiter tot ist, soll sich der Bildschirm abdunkeln
{
ScreenFade (5000, 10000, 5000);
};