|
-
Zitat von Oparilames
Ok danke Lehona dann muss ich warten, bis ich meine dNdR-CD wiederbekomme. (Eigentlich war Samstag abgemacht ...)
Übrigens noch eine Sache: Wie auch hier nachzulesen, werden bei den FrameFunctions Milisekunden als Parameter angegeben, nicht Sekunden. Wenn ein Delay dauerhaft/auf längere Sicht kleiner ist als die Dauer eines Frames kann es ebenfalls zu Abstürzen kommen (endlose Rekursion).
-
Grüßt euch. Ich habe mit den FrameFunctions ein Problem.
Ich versuche bereits seit Tagen einen alten, ziemlichen Spaghetti-Code zu entwirren. Er regelte das Intro zu RatotS und lief über Triggerscript-Events. (Genauergesagt ein riesiges)
Nun bin ich an einer Stelle wo ich mich wirklich frage, ob ich bei den FFs irgendetwas nicht verstanden habe.
In einem Auswahldialog wählt man den Schwierigkeitsgrad (mache ich evtl. noch über ein Menü, mal sehen.)
Code:
// Nachdem der Monolog beendet ist, soll das Intro beginnen.
FUNC VOID PC_MOD_IntroMonolog_End_Info()
{
PrintS("PC_MOD_IntroMonolog_End_Info"); // wird nicht angezeigt
B_ENDMONODIALOG (); // äquivalent zu B_ENDMOBSIDIALOG
if (MOD_In_DialogOption_Number==MOD_IntroGucken)
|| (MOD_In_DialogOption_Number==MOD_IntroSpielen)
{
Print("Gothic"); // Wird aufgerufen
MOD_INTRO_Act1();
//Wld_SendTrigger ("MOD_INTRO_TRIGGER"); // alter Aufruf
//AI_Function(PC_HERO,MOD_INTRO_Act1); // Funktioniert nicht
//FF_ApplyOnceExt(MOD_INTRO_Act1,150,-1); // Geht ebensowenig
};
};
Dann das eigentliche Intro:
Code:
func void MOD_INTRO_Act1()
{
CreateInvItems(PC_HERO, Itmi_Gold,10);
if (MOD_INTRO_ORKS_GESPAWNT == 0)
{
MOD_INTRO_ORKS_GESPAWNT += 1;
Wld_SendTrigger ("MOD_INTRO_CAMERA1");// wird ausgeführt, aber ab hier wird alles ignoriert
AI_PlayAni (INTRO_ORK, "T_PERCEPTION");
Npc_ExchangeRoutine (INTRO_Ork, "Start");
AI_ContinueRoutine (INTRO_Ork);
AI_GotoWP (INTRO_ORK2, "MOD_INTRO_ORKS_STEHEN_VOR_HUETTE_2");
//2 Sekunden warten
FF_ApplyOnceExt(MOD_INTRO_Act1,200,-1);
}else
if (MOD_INTRO_ORKS_GESPAWNT == 1)
&& (Npc_GetDistToWp (INTRO_ORK, "MOD_INTRO_ORKS_STEHEN_VOR_HUETTE") <= 550)
&& C_BodyStateContains(INTRO_ORK,BS_STAND)
{
MOD_INTRO_ORKS_GESPAWNT += 1;
AI_PlayAni (INTRO_ORK, "R_ROAM2");
AI_PlayAni (INTRO_ORK, "T_WARN");
//3 Sekunden warten
FF_ApplyOnceExt(MOD_INTRO_Act1,300,1);
} else
if(MOD_INTRO_ORKS_GESPAWNT == 2)
{
MOD_INTRO_ORKS_GESPAWNT += 1;
Wld_SendTrigger ("MOD_INTRO_CAMERA2"); //MOD: Fahrt zum Stuhl
AI_PlayAni (INTRO_Ork2, "T_DIALOGGESTURE_02");
AI_Output (BAU_000_Ehefrau,BAU_000_Ehefrau,"SVM_17_WhatWasThat"); //Was war das?!
Wld_SendTrigger ("MOD_INTRO_TRIGGER_FUER_PHASE1_VON_ERSTEM_GESCHOSS");
Npc_ExchangeRoutine (INTRO_Ork2, "Start");
AI_ContinueRoutine (INTRO_Ork2);
//1 Sekunde warten
FF_ApplyOnceExt(MOD_INTRO_Act1,75,1);
} else
// und so weiter
//FFs mit einer -1-Dauer dürfen nicht entfernt werden!
if (MOD_INTRO_ORKS_GESPAWNT!=1)
//&& (!Npc_IsInState(PC_HERO,ZS_Talk))
{
FF_Remove(MOD_INTRO_Act1);
};
Edit:-->
HookEngine-Befehle werden zu diesem Zeitpunkt bereits akzeptiert.
Der Startup-Ausschnitt:
Code:
// *********
// GLOBAL
// *********
func void STARTUP_GLOBAL()
{
// wird fuer jede Welt aufgerufen (vor STARTUP_<LevelName>)
Game_InitGerman();
};
func void INIT_GLOBAL()
{
// wird fuer jede Welt aufgerufen (vor INIT_<LevelName>)
Game_InitGerman();
LeGo_Init(LeGo_All);
if(HooksPlaced==0) //Gothic ist neu gestartet worden
{
HooksPlaced = 1;
};
};
// *********
// MOD Welt
// *********
FUNC VOID INIT_MOD_STARTGEBIET ()
{
HookEngine(oCNpc__SetAsPlayer, 6, "MOD_AfterStartup");
Set_MainSubtitleCondition(false,false);
Wld_SetTime (19,47);
};
FUNC VOID STARTUP_MODWORLD()
{
// ------ StartUps der Unter-Parts ------
STARTUP_NewWorld_Part_City_01();
Wld_InsertNpc (INTRO_ORK, "MOD_INTRO_ORKS_TAUCHEN_AUF");
Wld_InsertNpc (INTRO_ORK2, "MOD_INTRO_ORKS_TAUCHEN_AUF_2");
Wld_InsertNpc (BAU_000_EHEFRAU, "NW_TROLLAREA_TROLLLAKECAVE_03A"); //Irgendwohin, ist ja egal wohin.
};
FUNC VOID INIT_MODWORLD()
{
B_InitMonsterAttitudes ();
B_InitGuildAttitudes();
B_InitNpcGlobals ();
B_ENTER_NEWWORLD ();
// ------ INITS der Unter-Parts ------
INIT_SUB_NewWorld_Part_City_01();
INIT_MOD_STARTGEBIET();
};
const int MOD_OldStartMethod=true; // statt Menü ein Monolog
func void MOD_AfterStartup()
{
if(MOD_StartupHook>=1)
{
return;
};
MOD_StartupHook=1;
MEM_SendToSpy(zERR_TYPE_WARN, "MOD_AfterStartup()");
if(MOD_OldStartMethod)
{
PC_Hero.aivar[AIV_INVINCIBLE]=TRUE;
PLAYER_MOBSI_PRODUCTION = MOBSI_MOD_IntroMonolog;
Ai_ProcessInfos (PC_Hero);
}; //Anderer Fall noch nicht implementiert
//UnHookEngine möglich? Anscheinend nicht /:
//HookEngine(oCNpc__SetAsPlayer, 6, "");
};
Oparilames nachdem er seinen Gesellenbrief erhalten hat:
»Das war's mit dir, du Mistvieh!«
Geändert von Oparilames (24.03.2013 um 12:56 Uhr)
-
Ich kann deinem Script nicht so ganz folgen. Wozu überhaupt dieser Hook? Du könntest einmalig eine FF benutzen um den Monolog in Gang zu bringen, das sollte reichen.
Was mir noch spontan auffällt ist dass du beim ersten If in MOD_INTRO_Act1
Code:
//2 Sekunden warten
FF_ApplyOnceExt(MOD_INTRO_Act1,200,-1);
Stehen hast. Die -1 könnte das ganze etwas durcheinander bringen.
Ich an deiner Stelle würde das alles in separate Funktionen packen, so kannst du den ganzen Prozess wesentlich besser verfolgen.
Achja.. Und das sind Milisekunden die du angeben musst. Zwei Sekunden == 2000ms.
MfG Gottfried
-
Trialoge
Ich hätte da einen Fehler zu verkünden, laut meinem Teamkollegen gibt es ein Problem, wenn ein Trialogteilnehmer ein "Geist" (NPC_FLAG_GHOST) ist. Dann werden die Trialogteilnehmer abwechselnd zum Geist. Ansich sollte das kein großes Problem sein, das zu beheben oder?
-
Zitat von Umfi
Ansich sollte das kein großes Problem sein, das zu beheben oder?
Jain. Ich kann bei Gelegenheit einmal einen Blick darauf werfen, kann aber nicht garantieren dass ich das beheben kann.
Npc_Flag_Ghost ist eine etwas kritische Angelegenheit wenn ich das richtig in Erinnerung habe.
MfG Gottfried
-
@Gottfried die ganze Kamerafahrten und Moveraktionen, werden erst nach
dem Monolog gestartet.
Der Hook soll eine Triggerschleife ersetzen, die damals das Intro ingame
ablaufen ließ. Jedes Event musste zeitlich abgepasst und erst dann abgefeuert
werden, damit z.B. ein Stein eine Seitenwand eines Hauses einreißen konnte.
Das mit den einzelnen Funktionen könnte ich mal versuchen.
Bei dem ms-Fehler habe ich ausprobieren wollen, ob man 200 Millisekunden
ausreichen. Ich habe in der TriggerScript-Version mittels RefuseTalk
die Zeit eingegrenzt und das ging damals nur Sekundengenau.
Oparilames nachdem er seinen Gesellenbrief erhalten hat:
»Das war's mit dir, du Mistvieh!«
-
Zitat von Oparilames
Der Hook soll eine Triggerschleife ersetzen, die damals das Intro ingame
ablaufen ließ. Jedes Event musste zeitlich abgepasst und erst dann abgefeuert
werden, damit z.B. ein Stein eine Seitenwand eines Hauses einreißen konnte.
Warum benutzt du dann keine FF dafür die nur einmalig feuert? Du kannst bereits in der Startup einer Map eine FF eintragen, das sollte keine Probleme machen.
Hooken würde ich immer nur dann wenn es sich wirklich nicht vermeiden lässt - in dem Fall gehts einfacher ohne.
MfG Gottfried
-
Hmm das ließe sich machen. Aber ich frage mich dennoch, weshalb
MOD_INTRO_Act1 abbricht. Liegt das am internen Aufbau der FFs?
Andererseits könnte man auch eine Klasse für FFs schreiben mit
einer Pause/Wait-Funktion. (Also damit meine ich nicht euch,
soetwas ist denke ich schnell erledigt)
Oparilames nachdem er seinen Gesellenbrief erhalten hat:
»Das war's mit dir, du Mistvieh!«
-
Ich denke das Problem dürfte hierbei liegen:
Code:
FF_Remove(MOD_INTRO_Act1);
Da du gleichzeitig FF_ApplyOnce benutzt. FF_ApplyOnce legt fest dass die gegebene Funktion nur einmalig in der FF-Liste stehen darf.
Bei der ersten Abfrage setzt du MOD_INTRO_Act1 mit -1 (= unendlich) Wiederholungen ein - jeder weitere Aufruf an FF_ApplyOnce wird nun abgebrochen weil die Funktion bereits in der Liste steht und noch läuft. Wenn darauf dann FF_Remove aufgerufen wird wird die Funktion vollständig getötet.
Verstehst du die Problematik?
MfG Gottfried
-
Hi,
ich hab da ein kleines Problem mit den Trialogen. Offenbar werden beim Wechsel des Gesprächspartners die Flags von self gespeichert. Npc 1 ist in meinem Dialog ein Geist mit dem Flag NPC_FLAG_GHOST , wenn ich nun mit Npc 2 reden möchte bekommt er auch den ghost Flag.
-
Zitat von König Rhobar123
Hi,
ich hab da ein kleines Problem mit den Trialogen. Offenbar werden beim Wechsel des Gesprächspartners die Flags von self gespeichert. Npc 1 ist in meinem Dialog ein Geist mit dem Flag NPC_FLAG_GHOST , wenn ich nun mit Npc 2 reden möchte bekommt er auch den ghost Flag.
Schau mal zwei, drei Posts weiter nach oben und sieh dir meine Antwort dazu an
MfG Gottfried
-
Hatte ich übersehn , sry. @Post: Das heißt nun soviel wie "Ist nicht möglich" oder aber Zeit und Lust fehlt dazu ?
-
Zitat von König Rhobar123
Hatte ich übersehn , sry. @Post: Das heißt nun soviel wie "Ist nicht möglich" oder aber Zeit und Lust fehlt dazu ?
Ich habe mich vor einiger Zeit mal ganz kurz mit dem Flag auseinandergesetzt (Ich wollte einfach NPCs halbtransparent rendern lassen), aber die Engine ist da ein wenig... merkwürdig, vielleicht waren meine Tests aber auch fehlerhaft. Auf jedenfall hat das Flag selber afaik keine Auswirkung, das ist wohl bloß während der Erzeugung relevant, dementsprechend müssen wir dafür eine Sonderbehandlung einführen. Prinzipiell nicht kompliziert, wenn man weiß, was genau durch das Flag geändert wird... Weiß aber keiner (von uns)
-
Um das Ghost-Flag zu übernehmen genügt es npc.visualAlpha und npc.visualAlphaEnabled zu setzen. Der Wert für npc.visualAlpha steht in der ini unter INTERNAL.GhostAlpha.
Teleport zu sich selbst tuts auch (beim Helden: AI_Teleport(hero, "PC_HERO");), aber das ist vielleicht nicht seiteneffektfrei für eure Anwendung.
Ich schätze mal ihr werden wohl zusätzlich zum Visual einfach noch die beiden Alpha Attribute mit swappen wollen.
-
Zitat von Sektenspinner
Um das Ghost-Flag zu übernehmen genügt es npc.visualAlpha und npc.visualAlphaEnabled zu setzen.
Da bin ich irritiert. Wie & Wo nutze ich das denn jetzt um meinem Npc nicht den Ghost flag geben zu müssen?
Zitat von Lehona
Gar nicht, die Antwort war an Gottfried und mich gerichtet
Zitat von Lehona
erstmal
Hoffentlich ^^
Geändert von König Rhobar123 (02.04.2013 um 00:23 Uhr)
-
Zitat von König Rhobar123
Da bin ich irritiert. Wie & Wo nutze ich das denn jetzt um meinem Npc nicht den Ghost flag geben zu müssen?
Gar nicht, die Antwort war an Gottfried und mich gerichtet Für dich gibt es erstmal keine direkte Lösung.
-
Ich lade das mal hier ab, für Interessierte.
Nach Aufruf von PreventArrowTriggerCollision können Pfeile nicht mehr mit zCTrigger und zCTriggerScript Objekten kollidieren die respondToObject auf FALSE gesetzt haben.
Das erlaubt es Triggerzonen aufzustellen die auf Berührung reagieren, die Spieler und Npcs beim Bogenschießen aber nicht behindern (die Pfeile rattern da gerne mal lautstark zu Boden).
Zonen die auf Pfeile reagieren sind weiterhin möglich (Tempelschalter), bei diesen muss respondToObject auf TRUE gelassen werden.
Code:
//##############################################################
// Call this in INIT_GLOBAL after LeGo_Init.
//##############################################################
func void PreventArrowTriggerCollision() {
const int done = false;
if(!done) {
//6A1490: virtual int __thiscall oCAIArrow::CanThisCollideWith(class zCVob *)
HookEngineF(6952080, 7, CanThisCollideHook);
done = true;
};
};
//##############################################################
// The Handler
//##############################################################
func void CanThisCollideHook() {
var int vtbl; vtbl = MEM_ReadInt(MEM_ReadInt(ESP + 4));
const int zCTrigger_vtbl = 8627196;
const int zCTriggerScript_vtbl = 8582148;
if(vtbl != zCTrigger_vtbl)
&&(vtbl != zCTriggerScript_vtbl) {
//It is no Trigger
return;
};
var int arg_0; arg_0 = ESP + 4;
var zCTrigger trigger; trigger = _^(MEM_ReadInt(arg_0));
if(trigger.bitfield & zCTrigger_bitfield_respondToObject) {
//It is supposed to collide with arrows
return;
};
//I think this should not collide.
//The Engine inself excludes the shooter, so lets make it look like
//the arrow collides with the shooter
// vob = this.owner
MEM_WriteInt(arg_0, MEM_ReadInt(ECX + 92));
};
Geändert von Sektenspinner (05.04.2013 um 15:09 Uhr)
-
D.h. wenn ein Trigger ohne RespondToObject potentiell mit einem Pfeil kollidiert, gibst du der Engine einfach vor dass momentan auf Pfeil kollidiert mit dem Schützen geprüft wird?
-
Zitat von Lehona
D.h. wenn ein Trigger ohne RespondToObject potentiell mit einem Pfeil kollidiert, gibst du der Engine einfach vor dass momentan auf Pfeil kollidiert mit dem Schützen geprüft wird?
Exakt. Habe auf die schnell keine bessere Möglichkeit gesehen die Funktion false zurückgeben zu lassen.
Das ganze müsste auch sicher sein, denn der Schütze existiert immer (wenn nicht stürzt die Funktion auch ohne mein Eingreifen bereits ab).
In Returning scheint das Problem recht oft aufzutreten, mal schauen ob sich jetzt einer erbarmt, den Code nutzt und die respondToObject flags setzt.
-
Zitat von Sektenspinner
Exakt. Habe auf die schnell keine bessere Möglichkeit gesehen die Funktion false zurückgeben zu lassen.
Das ganze müsste auch sicher sein, denn der Schütze existiert immer (wenn nicht stürzt die Funktion auch ohne mein Eingreifen bereits ab).
Performanter wäre wohl einfach eine Manipulation des Bytecodes, aber ich denke nicht, dass die Funktion so viel Rechenleistung kostet. Andererseits weiß ich auch nicht, wie oft sie aufgerufen wird.
Berechtigungen
- Neue Themen erstellen: Nein
- Themen beantworten: Nein
- Anhänge hochladen: Nein
- Beiträge bearbeiten: Nein
|
|