Finally, time for some scripting In my last post I left off with adding a custom Magic Effect called ZombieInfectionEffect to unarmed fer...

Zombie Walkers - Part 3

Finally, time for some scripting

In my last post I left off with adding a custom Magic Effect called ZombieInfectionEffect to unarmed feral ghoul attacks. Magic effects have a whole lot of properties (too many to describe here). In my case my magic effect exists to add the Infection disease built into Fallout 4's survival difficulty. This requires scripting which is part of the reason I chose to add it to my first release. As a programmer, I was anxious to get into some coding after all these GUI changes.

When you open a Magic Effect and look in the lower right corner of the window you'll see a section for scripts with a label of Script Name. From here you can click the Add button to create a new one, Remove to delete a script, or Properties to set properties of the selected script which we'll come back to later.



Creating a new script will add the new script and bring you straight to the Properties view. Now this confused me at first. I thought maybe scripting was GUI-driven at first when I saw this (it's not). For me this is backwards and I'd rather start working on the flow of the script first which will flush out the properties I need. I close this window and then right click on the new script and select Edit Source.



Scriptname ZombieInfectionScript extends activemagiceffect
Hurray! Source code. Well, one line anyway. Unfortunately, this is point where you start to wonder how you even get started. 

OnEffectStart and Logging

I'll skip ahead a bit and say that I looked at existing magic effects in the CK to get some samples and at least get to a point where I realized the function you need to use to get code to run "on effect start" and how to log data.


Scriptname ZombieInfectionScript extends activemagiceffect
; Called when an Actor is hit by a zombie attack
Event OnEffectStart(Actor akTarget, Actor akCaster)
    ; log function called
    Debug.OpenUserLog("joefor")
    Debug.TraceUser("joefor", "[ZombieInfectionScript] called!" )
EndEvent
In addition to using the special Debug class to open and trace to a user log, you must enable logging in your Fallout4.ini file (C:\Users\{username}\Documents\My Games\Fallout4\Fallout4.ini on Windows 10). Search for the bEnableLogging and bEnableTrace properties and set each to 1.

Save the script with Ctrl-S and verify the script compilation at the bottom of the script window.


Now you can test the function logs by doing the following:

  1. File -> Save to save the mod (.esp file)
  2. File -> Create Archive to generate the archive that contains the script (.ba2 files)
  3. Run the game
  4. Open the console and spawn a zombie using "player.placeatme 000758AD"
  5. Let the zombie hit you
  6. Pause the game and look for a log file (C:\Users\{username}\Documents\My Games\Fallout4\Logs\Script\User\joefor.0 on Windows 10)
  7. Verify the log statement appears in the file once for each time the Zombie hit you

Now for the fun stuff

Now we get to actually write the logic to give the player an Infection. But... how do we do that?

It was at this point that took me a lot of research and trial and error. In the end I looked at existing game files for an example. The breakthrough came when I stumbled upon a Quest called HC_Manager. This quest has a script associated with it called Hardcore:HC_ManagerScript with all kinds of juicy code to implement the new survival mode released after game launch. From this over 3k line script I learned a few things:
  • there is no search function using the built-in script editor! (copy/paste to Notepad++ or something)
  • diseases are Potions that apply MagicEffects
  • to display a message to the player you need to use a predefined Message object
  • you can get a reference to the player with Game.GetPlayer()
  • you can get the current difficulty level with Game.GetDifficulty()
This basically gave me all the pieces I needed to flush out the script logic.


Scriptname ZombieInfectionScript extends activemagiceffect
; Define properties
Potion Property effectToAdd Auto Const Mandatory
MagicEffect Property magicEffectToApply Auto Const Mandatory
Message Property messageToDisplay Auto Const Mandatory
; Called when an Actor is hit by a zombie "bite" attack
Event OnEffectStart(Actor akTarget, Actor akCaster)
; Get Player Actor
Actor player = Game.GetPlayer()
; Check if target is player
if( akTarget  == player )
; Only proceed if difficulty set to survival
if ( Game.GetDifficulty() == 6 )
; Display message only if you don't already have the disease
if( !player.HasMagicEffect(magicEffectToApply) )
messageToDisplay.show()
endif
; Add infection to player (equip potion - force it down their throat!)
player.equipitem(effectToAdd, false, true)
endif
endif
EndEvent
Basically the above is doing a couple checks to only proceed if the target of the effect is the Player and the Difficulty is Survival (6 ... hard-coded this way in the HC_Manager script so I went with it).

The HasMagicEffect function is useful in that it gives you a way to see if the Player already has an Infection (or other effect). This allowed me to avoid spamming the user with popup messages every time they were hit by a Zombie.

The EquipItem function forces the player to activate the special Potion item immediately as if they clicked on it in their inventory.

I said I'd get back to Properties. Well, when you save the script and click on the Properties button you can now see the properties and assign them values. In this case I basically assigned properties for the Infection Potion, Infection Magic Effect, and the Message to display ("you feel ill") from the values bound in the HC_Manager script.


From here you can repeat the test from above while playing at Survival difficulty and verify that you receive the Infection disease when a Zombie hits you. 

Special Bug Note: my initial release did not have the difficulty check. You can apply Infection to players on non-survival difficulties so be careful. Apparently doctors can still cure Infection on those difficulties though (I didn't test this myself but it was mentioned in the mod comments).

Wrap up

This concludes at least most of the changes that went into releasing the initial version of my Zombie Walkers mod. The mod is far from complete and I've already released additional features like NPC Resurrection and Radstag replacement via a new Holotape. I'll continue blogging on specific items and already have some rants in store for NPC Resurrection so stay tuned.

Back to Part 2

0 comments: