Jump to content

Dynamic Script Attachment Problem


jags78

Recommended Posts

Hi guys and gals

 

I'm making some tests with dynamic script attachments. I chose the 2 quest method, but I stuck somewhere... I'm simply trying to punch a dude in the face and trigger the OnHit() event notifying me "I hit a human..."

 

Here is what I got:

 

Quest 1 - Alias Quest

 

rqRHcNu.png

P3uApRa.png

 

Human02-10 where duplicated from Human01

 

Human01 Reference Alias

W8GNEBA.png

 

test:Human Code

Scriptname test:Human extends ReferenceAlias

Actor Property PlayerRef Auto Const

Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, \
  bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)

	If akAggressor == PlayerRef
		Debug.Notification("I hit a human...")
	EndIf
EndEvent

Quest 2 - Update Quest

 

XstpAf2.png

 

In the Script tap I added a script called test:updatealias... here the code

Scriptname test:updatealias extends Quest

Quest Property jags_aliasquest Auto Const

Event OnQuestInit()
	StartTimer(0.5, 42)
EndEvent

Event OnTimer(int aiTimerID)
	If aiTimerID == 42
		jags_aliasquest.Stop()
		
		; loop prevention
		Int c = 0
		While !jags_aliasquest.IsStopped() && c < 50
			Utility.Wait(0.1)
			c += 1
		EndWhile

		Debug.Notification("aliasquest stopped")

		jags_aliasquest.Start()

		; loop prevention
		c = 0
		If !jags_aliasquest.IsRunning() && c < 50
			Utility.Wait(0.1)
			c += 1
		EndIf

		Debug.Notification("aliasquest started")

		StartTimer(10, 42)
	EndIf
EndEvent

All properties are "attached" to the CK-Objects and the scripts compile fine.

 

Test-envirement is an empty cell (from the mod 'white test room')

console commands:

killall

player.placeatme 20749 10 (spawns 10 raiders)

 

Here the problem... I only get the notifications "aliasquest stopped" and "aliasquest started" in a loop as intended, but never a notification "I hit a human..." when I punch or shoot a raider.

 

Forums read for this test-mod:

 

Has anyone have a clue what I'm doing wrong??

 

Thx in advanced.

Link to comment
Share on other sites

OnHit is a ScriptObject event, not an Actor or ObjectReference event, so the ObjectReference quest alias attached scripts will never receive them.

 

You need a script to RegisterForHitEvent for each actor reference and then listen for the OnHit events which is a total pain the the arse https://www.creationkit.com/fallout4/index.php?title=RegisterForHitEvent_-_ScriptObject
Self.RegisterForHitEvent(akTarget = ThisActor, akAggressorFilter = pPlayerREF, abmatch = true)
Self.RegisterForHitEvent(akTarget = pPlayerREF, akAggressorFilter = ThisActor, abmatch = true)

And then you have to manage unregistrations when the targets OnUnload() or OnDeath() if OnHit has not triggered to avoid building up a dirty load of orphan event registrations on the script:

Self.UnRegisterForHitEvent(pPlayerREF, ThisActor) 
Self.UnRegisterForHitEvent(ThisActor, pPlayerREF)
I have found that using a RefCollectionAlias for akTarget or akAggressorFilter to try and cut down on actor management is inconsistent for hit detection, even when the system is lightly loaded.
Wherever possible I avoid the pain of managing RegisterForHitEvent+OnHit and try to use the Perk Entry point ApplyCombatHitSpell attached to actors to trigger a script.
Link to comment
Share on other sites

The quest that holds the aliases is marked as "Run Once", but you are restarting it, so I think that what actually happens is that the counter (c < 50) stops the while loops instead of the quest stopping or starting.

Link to comment
Share on other sites

The quest that holds the aliases is marked as "Run Once", but you are restarting it, so I think that what actually happens is that the counter (c < 50) stops the while loops instead of the quest stopping or starting.

Hmm... Never had a problem to restart a quest although this flag was set. I'll remove it and try again.

 

 

 

OnHit is a ScriptObject event, not an Actor or ObjectReference event, so the alias attached scripts will never receive them.

 

You need a script to RegisterForHitEvent for each actor reference and then listen for the OnHit events which is a total pain the the arse https://www.creationkit.com/fallout4/index.php?title=RegisterForHitEvent_-_ScriptObject
Wherever possible I avoid using RegisterForHitEvent+OnHit and try to use the Perk Entry point ApplyCombatHitSpell attached to actors to trigger a script.

 

I'm actually using the Perk Entry Point ApplyCombatHitSpell on the mod I'm writing... It works for Melee, Guns, Bashing... but NOT for Grenades, right?? That's why I even started testing with the dynamic script attachements...

 

If I'm right with the Grenade thingy, I'll have to wrap my head around your suggested "pain in the arse" approach... Do you know of any example online? I'm not really grasping how to approach that method.

Link to comment
Share on other sites

This is a script which prevents actors spawned out of the players line of sight taking or dealing damage until the player can see them to spectate or participate in the fight. It registers a separate script to receive the events to avoid script queues and bottlenecks.

 

The spawning script function:

If (pPlayerREF.GetDistance(ThisActor) > 3000) && (pPlayerREF.HasDirectLOS(ThisActor) == FALSE) 
   ;(ThisActor as Actor).SetGhost(True)	; no, need OnHit events to fire
   (ThisActor as Actor).AddPerk(pSKK_REMNoDamagePerk)
   (pSKK_REM as SKK_REMCombatScript).RegisterForDirectLOSGain((pPlayerREF as Actor), akTarget = ThisActor) 
   (pSKK_REM as SKK_REMCombatScript).RegisterForDistanceLessThanEvent(pPlayerREF, ThisActor, 1542)
   (pSKK_REM as SKK_REMCombatScript).RegisterForHitEvent(akTarget = ThisActor, akAggressorFilter = pPlayerREF, abmatch = true)
   (pSKK_REM as SKK_REMCombatScript).RegisterForHitEvent(akTarget = pPlayerREF, akAggressorFilter = ThisActor, abmatch = true)
EndIf	

This is the SKK_REMCombatScript event handling script function. RegisterForHitEvent is only for one hit, so if conditions are not met then you must re-register. The highly specific registrations SHOULD prevent any hits that do not involve the player from triggering, but, guess what ... they can. So a quality script should be defensive and allow re-registrations for unwanted hit conditions:

Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack,  bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)

If (akTarget as Actor).HasPerk(pSKK_REMNoDamagePerk) && (akAggressor == pPlayerREF)
   EnableCombat(akTarget)
ElseIf (akAggressor as Actor).HasPerk(pSKK_REMNoDamagePerk) && (akTarget == pPlayerREF)
   EnableCombat(akAggressor)
ElseIf (akTarget as Actor).HasPerk(pSKK_REMNoDamagePerk) ;Player is not yet involved
   Self.RegisterForHitEvent(akTarget = akTarget, akAggressorFilter = pPlayerREF, abmatch = true)
   Self.RegisterForHitEvent(akTarget = pPlayerREF, akAggressorFilter = akTarget, abmatch = true)
ElseIf (akAggressor as Actor).HasPerk(pSKK_REMNoDamagePerk) ;Player is not yet involved
   Self.RegisterForHitEvent(akTarget = akAggressor, akAggressorFilter = pPlayerREF, abmatch = true)
   Self.RegisterForHitEvent(akTarget = pPlayerREF, akAggressorFilter = akAggressor, abmatch = true)
EndIf

EndEvent
Link to comment
Share on other sites

Your snippets are VERY usefull. Thank you very much!!

 

To be honest, I already failed much earlier in the process... I didn't realize that there was an entier ReferenceAlias functions collection. For some reason I thought the ReferenceAlias within the CK was just a Placeholder for the Object-Type given to the RefAlias Script. I thought it just had to match the defined conditions. I was wrong.

 

Some reading and playing around later and I found following functions that triggered an action on the raiders found close to the player:

Here a working code example

Event OnAliasInit()
	Actor Human = GetActorRef() as Actor ; cast probably not needed
	Human.UnequipAll()
EndEvent

1 second after spawning the raiders, all stood there in their undies... :smile:

 

I'll post my finding about the grenades in here if I remember to...

Edited by jags78
Link to comment
Share on other sites

I got the OnHit() Event working on a minimal state (no filtering appart the akTarget).

 

I've added a eventhandler script to the second quest (the update quest):

Scriptname test:EventHandler extends Quest

ObjectReference Human

; copied this method of passing a script from 'Stealthy Takedowns', I really like his approach
EventHandler Function GetScript() Global
	return (Game.GetFormFromFile(0x00000F9A, "jags78_aliastest.esp") as Quest) as EventHandler
EndFunction

Function RegisterHuman(ObjectReference akHuman)
	Human = akHuman
	RegisterForHitEvent(Human)
EndFunction

Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack,  bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)
	Debug.Notification("OnHit()")
	RegisterForHitEvent(Human)
EndEvent

And here the the RefAlias Script:

Scriptname test:Human extends ReferenceAlias

Event OnAliasInit()
	ObjectReference oRef = GetReference()
	test:EventHandler.GetScript().RegisterHuman(oRef)
EndEvent

I diminished the timer on the update quest script to 1 second...

 

IMPORTANT: DieFeM was right, you have to remove the "Run Once" flag on the alias quest...

 

Works for me, thx to you two for your help. :thumbsup:

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...