Try / OpenGothic

Reimplementation of Gothic 2 Notr
MIT License
1.07k stars 78 forks source link

Add firedelay to triggers #575

Closed thokkat closed 2 months ago

thokkat commented 3 months ago

Trigger attribute fireDelay determines how much time should pass between trigger call and start. Currently only the time between calls is checked leading to erroneous behavior, e.g. the first call never waits.

Try commented 3 months ago

Hi, @thokkat and thanks for finding!

Currently only the time between calls is checked leading to erroneous behavior, e.g. the first call never waits

Fist call has wait in current implementation, since time start at 0 and emitTimeLast is initialized to zero. In what case trigger doesn't work for you? I'm assuming that it break when player enters a new non-starting location, for first time, so it should be enough to set emitTimeLast = world.tickCount(); in constructor.

thokkat commented 3 months ago

Currently the last successful activation time is saved and next activation is delayed. So if time distance is large enough trigger would always activate instantly. Instead I want to change it so it always delays before activation regardless when last activation was. Took it from spacer help file:

[TOPIC MEMBER 'zCTrigger::fireDelaySec']
FLOAT                sec
Wieviel Zeit nach dem Aktivieren des Triggers muss vergehen, bevor er wirklich feuert. 
\\ How much time must pass after activating the trigger before it actually fires.

In what case trigger doesn't work for you?

Here in vanilla cannons pop up over time but in OG all are set up at the beginning of cutscene.

script:

    wld_sendtrigger("KAMERA_KANONENEVENT_KANONEN_TRIGGER");
    ai_setwalkmode(self, npc_run);
    ai_gotowp(self, "PINTA_DSCHUNGEL_40");
    ai_playani(self, "T_PLUNDER");
    wld_sendtrigger("PINTA_KANONE_01_TRIGGER");
    ai_gotowp(self, "PINTA_DSCHUNGEL_41");
    ai_playani(self, "T_PLUNDER");
    wld_sendtrigger("PINTA_KANONE_02_TRIGGER");
    ai_gotowp(self, "PINTA_DSCHUNGEL_42");
    ai_playani(self, "T_PLUNDER");
    wld_sendtrigger("PINTA_KANONE_03_TRIGGER");
    ...

Cannon triggers have fireDelaySec values set to align with plunder animation.

thokkat commented 3 months ago

Ok actually emitTimeLast refers to retriggerWaitSec instead of fireDelaySec. Adjusted accordingly.

https://zk.gothickit.dev/engine/objects/zCTrigger/

  • retriggerWaitSec The number of seconds that have to elapse after processing an event before the trigger will process additional events. All events received by the trigger during that time are ignored.
  • fireDelaySec The number of seconds to wait before emitting the OnTrigger event after processing.
thokkat commented 2 months ago

Added a pointer to reference trigger for Triggerevents to assure uniqueness. Pointer is not serialized, so in case of a loaded game Triggerevents only have the string reference like before and pick up pointer before first testing for firedelay. But this should be extremely rare and thus ok I guess.

thokkat commented 2 months ago

Deferred triggers are now tracked in AbstractTrigger class as you suggested. I moved events with a delay applied in triggerlist there as well to be able to assure uniqueness. In case of user save game action events are pushed back to triggerEvents vector which keeps current save game behavior.

thokkat commented 2 months ago

Since delay from triggerlist is per string target but fireDelay is per trigger target I think it's best to keep it as is and push delayed triggers back in World::execTriggerEventis. Only a check for target uniqueness has been added. Triggers with a fireDelay are then pushed to triggersDeferred and wait until execution time has come.

Try commented 2 months ago

@thokkat After reading change multiple time is still look over-engineered and incomplete. Can you please send me a link to mod+save that you using to test this feature?

thokkat commented 2 months ago

Short explanation how I think it should be: wait triggerlist delay -> check if trigger is already allowed (retriggerWaitSec), if not discard -> wait fireDelay -> fire If trigger is on any delay don't allow retrigger. But tested only fireDelay here.

mod: https://www.worldofgothic.de/dl/download_404.htm I used 1.1 patch. Second link are voice lines (not needed).

save files: saveslot.zip

In quick save talk to npc and use dialog option 1.

There are more bugs in case you wanna look into them as well.

Try commented 2 months ago

Save files are not compatible apparently:

unable to open Zen-file: "insel3.zen"
loading error: Cannot init RTN_START_415: not an instance

I'm assuming that problem is me installing mod on top of English game. You have steam+German as a base, right?

thokkat commented 2 months ago

Old Gothic II Gold cd version which has only german language available. When I shared non mod save some time ago we had same problem, hoped it wouldn't occur again.

For non delay bugs I can give some marvin goto commands, but delay requires some quests/dialogs before so npc is at right position and has trigger dialog available.

thokkat commented 2 months ago

This is how you can get to cutscene by "normal" playing:

  1. In Karibik.ini change start world to World=Karibik\INSEL3.ZEN
  2. We want to use storyhelper (has talk perc but doesn't talk) and have access to all dialog options:

    In GameScript::npc_knowsinfo return always true and in Npc::startDialog add

  if(hnpc->id==3100) {
    setOther(&pl);
    aiPush(AiQueue::aiProcessInfo(pl));
    return;
    }
  1. Start game. End first dialog and go to jungle in the center of island. Clean everything e.g. with firerain (itsc_firerain) so npc later don't get distracted.
  2. Set breakpoint in Marvin::printVariable , use marvin p pinta_pflanzen_onetime and overwrite variable value to 1.
  3. Use marvin insert karibik_storyhelper and select last dialog option Ins 4.Kapitel (Vor Kanonenevent). Augustus and other npc should now be teleported close to your location. Use marvin set time to force npc to their waypoints.
  4. Talk to Augustus and start dialog Es kann losgehen!.

For other bugs:

Try commented 2 months ago

unable to open Zen-file: "insel3.zen" When I shared non mod save some time ago we had same problem, hoped it wouldn't occur again.

Finally found time to look into. Also found the problem with saves: command line been missing -game:Karibik.ini

I've pushed my solution to deferred triggers to deferred-triggers branch, commit d3196b65

Try commented 2 months ago

No focus canon : same start world, goto pos -1500 200 2900

fixed it on the master branch

Save1 has lava which should kill the player. Problem is

I would say the read problem here is misalignment on how BBox is aligned to npc-model here. In vanilla, apparently, origin point of npc is body-center. In OpenGothic origin is in between the foots. While OpenGothic solution is better on handling ground-collision and spawns, it leads to numerious hacks and issues, like translateY. This can be addressed as part of https://github.com/Try/OpenGothic/issues/182

thokkat commented 2 months ago

This is how processing should look like for a triggerlist after some more testing:

  1. Triggerlist call
  2. Discard if one of it's targets is still waiting for delay to pass (tested for LP_ALL), othewise go next
  3. execute target after it's delay has passed
  4. process triggers that match target name
  5. discard if trigger is not ready (retriggerDelay), otherwise go next
  6. discard if trigger already has a delayed event, otherwise go next
  7. set delayed event and wait for fireDelay
  8. release delayed event, set last trigger time and fire

deferred-triggers branch, commit https://github.com/Try/OpenGothic/commit/d3196b652819e3585e2a33fb437502dce70af4ee

Some comments based on the above:

Try commented 2 months ago

Some comments based on the above

Thanks for checking!

Why vector for delayedEvents? Trigger can only have one delayed event.

If retriggerDelay is smaller than fireDelay, then there can be multiple deferred event in-flight

thokkat commented 2 months ago

If retriggerDelay is smaller than fireDelay, then there can be multiple deferred event in-flight

Just tested again to be sure with a firedelay of 5s. If trigger is on firedelay and trigger is retriggered no second/third/... trigger execution happens. Only after these 5s a retrigger is possible.

Try commented 2 months ago

Thanks for double checking! Fixed in b1cb4274

thokkat commented 2 months ago

One last remark, emitTimeLast = world.tickCount() needs to be in implProcessEvent. Test was 5s retriggerDelay, 5s fireDelay. Constant poking gave first execution after 5s, but all following took 10s.

I think with the above it's good for merge. Haven't done ingame testing though because after driver update aquireNextImage fails at game start with khr out of date.

Try commented 2 months ago

One last remark

Fixed; This PR is closed by: https://github.com/Try/OpenGothic/pull/592