Jump to content

Scripting Help With Giving Group Orders Through Menu


unknownhero2827

Recommended Posts

I'm trying to give my followers group orders through a menu, activated by a spell. So far, I can only get to the menu part. Honestly I barely have any idea how to do this.

Begin ScriptEffectStart
MessageBox "Group Orders." "Follow" "Stay" "Nevermind"
End
Begin Gamemode
if GetButtonPressed > -1
if GetButtonPressed == 0 ;Follow
set IFQuest.IFGroupOrders to 1
elseif GetButtonPressed == 1 ;Stay
set IFQuest.IFGroupOrders to 2
elseif GetButtonPressed == 2 ;Nevermind
set IFQuest.IFGroupOrders to 0
endif
endif
End
This is my spell script, of course. I don't know if it's laid out right. You can see that I'm trying to make it change a short in a quest script. From the quest script, I'm using that as what my followers' object script refers to when given a group order.
if IFQuest.IFGroupOrders == 1
RemoveScriptPackage
AddScriptPackage IFFollowPackage
elseif IFQuest.IFGroupOrders == 2
RemoveScriptPackage
AddScriptPackage IFStayPackage
endif

 

 

That's the object script. Another problem is I don't know if my spell script is constantly setting the IFGroupOrders short. Oh and I have the different lines indented and stuff (idk if it actually makes a difference other than it's easier to navigate).

 

If there's a better way to try and do this, like bypassing the quest script altogether, that'd be nice. Thanks for any help in advance.

 

EDIT: I apologize for my lack of... terminology.

Edited by unknownhero2827
Link to comment
Share on other sites

Script effects use something like

Begin ScriptEffectUpdate
    ...
End

And not GameMode. I have managed to do messageboxes with spells, so it should theoretically work.

Edit: Also, the GetButtonPressed returns the correct button only once. Grab it once, and then use it. Like this:

short iButton

...

Begin ScriptEffectUpdate

    set iButton to GetButtonPressed

    If ( iButton < 0 )
        Return
    EndIf
    ...
    Your scripts here with iButton as the button pressed
    ...
    set iButton to -1

End

Also, you could try learning how arrays work. OBSE adds them. In my follower management mod, there is an array that stores information about the followers. When issuing a command, you could then cycle through the array and access each follower that way instead of using a quest script and actor scripts. Just an idea, might take a little learning.

Edited by PhilippePetain
Link to comment
Share on other sites

I've successfully gotten the menu to work. But, the orders are only working with 1 actor at a time, unless i have it like this:
Actor's Object Script
if IFQuest.MenuResult == 1
RemoveScriptPackage
MoveTo Player
AddScriptPackage IFFollowPackage
elseif IFQuest.MenuResult == 2
RemoveScriptPackage
AddScriptPackage IFFollowPackage
elseif IFQuest.MenuResult == 3
RemoveScriptPackage
AddScriptPackage IFStayPackage
elseif IFQuest.MenuResult == 4
RemoveScriptPackage
AddScriptPackage IFRelaxPackage
elseif IFQuest.MenuResult == 10
RemoveScriptPackage
AddScriptPackage IFTravelAnvilPackage
elseif IFQuest.MenuResult == 11
RemoveScriptPackage
AddScriptPackage IFTravelBravilPackage
elseif IFQuest.MenuResult == 12
RemoveScriptPackage
AddScriptPackage IFTravelBrumaPackage
elseif IFQuest.MenuResult == 13
RemoveScriptPackage
AddScriptPackage IFTravelCheydinhalPackage
elseif IFQuest.MenuResult == 14
RemoveScriptPackage
AddScriptPackage IFTravelChorrolPackage
elseif IFQuest.MenuResult == 15
RemoveScriptPackage
AddScriptPackage IFTravelImperialCityPackage
elseif IFQuest.MenuResult == 16
RemoveScriptPackage
AddScriptPackage IFTravelLeyawiinPackage
elseif IFQuest.MenuResult == 17
RemoveScriptPackage
AddScriptPackage IFTravelSkingradPackage
elseif IFQuest.MenuResult == 20
ForceAV Aggression 50
ForceAV Confidence 50
elseif IFQuest.MenuResult == 21
ForceAV Aggression 5
ForceAV Confidence 50
elseif IFQuest.MenuResult == 22
ForceAV Confidence 0
endif
Quest Script:
if GroupMenu == 1
MessageBox "Group Orders" "Regroup" "Follow" "Stay" "Relax" "Travel" "Behavior" "Nevermind"
set Choosing to 1
set GroupMenu to 0
elseif (Choosing == 1)
set Choice to GetButtonPressed
if (Choice == -1)
elseif (Choice == 0)
set MenuResult to 1
elseif (Choice == 1)
set MenuResult to 2
elseif (Choice == 2)
set MenuResult to 3
elseif (Choice == 3)
set MenuResult to 4
elseif (Choice == 4) ;Travel
set Choosing to -10
elseif (Choice == 5) ;Behavior
set Choosing to -11
elseif (Choice == 6)
endif
elseif (Choosing == -10)
MessageBox "Group Orders: Travel" "Anvil" "Bravil" "Bruma" "Cheydinhal" "Chorrol" "Imperial City" "Leyawiin" "Skingrad" "Nevermind"
set Choosing to 10
elseif Choosing == 10
set Choice to GetButtonPressed
if (Choice == -1)
elseif (Choice == 0)
set MenuResult to 10
elseif (Choice == 1)
set MenuResult to 11
elseif (Choice == 2)
set MenuResult to 12
elseif (Choice == 3)
set MenuResult to 13
elseif (Choice == 4)
set MenuResult to 14
elseif (Choice == 5)
set MenuResult to 15
elseif (Choice == 6)
set MenuResult to 16
elseif (Choice == 7)
set MenuResult to 17
elseif (Choice == 8)
endif
elseif (Choosing == -11)
MessageBox "Group Orders: Behavior" "Aggressive" "Defensive" "Evasive" "Nevermind"
set Choosing to 11
elseif (Choosing == 11)
set Choice to GetButtonPressed
if (Choice == -1)
elseif (Choice == 0)
set MenuResult to 20
elseif (Choice == 1)
set MenuResult to 21
elseif (Choice == 2)
set MenuResult to 22
elseif (Choice == 3)
endif
endif

 

 

 

This means that the actors continuously get reassigned the command over and over again. It does make them all "obey", but the script is still rubbish right now.

 

I'll try looking into arrays, though I'm sure it'll take me a lot longer than I'd like to figure out what I'm looking at.

Link to comment
Share on other sites

But it will make writing easier. Write it once, then use it for one follower, two followers, twenty followers, a hundred followers, whatever! As long as the game can handle it, though. Definitely recommended. I would call it the lazy one's choice were it not for the learning if one is completely new to arrays. And I also asked for help on the forums. Or at least I received help, so I must have asked. :smile:

 

I dare not comment on the quest script implementation. If it works for you, that is fantastic. I assume you know how to stop the quest when not necessary? So as to spare game resources?

 

Also, the actor script could do with some improvement maybe. Or maybe not. In high level processing, an actor's script is run every frame and it happens for each individual actor. You could use a variable to restrict the amount of processing when not relevant. But I am not sure how it would work with that thingy. Using an array could solve everything, and you could just have one quest script with an array (quest script update interval irrelevant) with a spell that handles commands.

 

A simple version could maybe work a bit like the following (not your script, but the idea) if using OBSE and arrays?

 

Quest script

ScriptName SomeQuestScript

float       fQuestDelayTime
int         iTemp
Array_var   aFollowers

Begin GameMode

    If ( GetGameLoaded == 0 ) ; Only run the rest of the script when a save has been loaded
        Return
    EndIf

    let iTemp := ar_Size aFollowers ; Check if array has been initialised

    If ( iTemp < 0 ) ; If not, initialise it
        let aFollowers := ar_Construct Array
    EndIf

End

Spell script effect for control spell

ScriptName SomeControlSpellScript

int         iButton
ref         rTemp
Array_var   aTemp

Begin ScriptEffectStart

    MessageBox "Option A" "Option B" "Option C"

End

Begin ScriptEffectUpdate

    let iButton := GetButtonPressed   ; Get the pressed button

    If ( iButton < 0 )  ; If not relevant, skip the rest of the script
        Return
    EndIf

    let aTemp := ar_Construct Array ; Initialise temp array

    ; This following could also be done with indexes, which is what I do.
    ; Indexes work better if you have added several StringMap type arrays to
    ; aFollowers and therfore cannot directly access followers the following way.

    ForEach aTemp <- SomeQuest.aFollowers  ; For every element thingy in aFollowers
        let rTemp := *aTemp   ; Get the actor reference from the element
        If ( iButton == 0 )
            ; Choice A, for example:
            rTemp.MoveTo Player
            rTemp.RemoveScriptPackage
        ElseIf ( iButton == 1 )
            ; Choice B
        ElseIf ( iButton == 2 )
            ; Choice C
        EndIf
    Loop

    let iButton  := -1       ; Prevent running this again
    let aTemp    := ar_Null  ; Just emptying the temp array, probably not necessary

End

And how to add a follower to the array of followers (just place it somewhere):

    int iTemp
    ref rTarget

    ...

    let rTarget := TargetToRecruit
    let iTemp   := ar_Find rTarget SomeQuest.aFollowers 

    If ( iTemp < 0 ) ; Only if the actor is not already in the array
        ar_Append SomeQuest.aFollowers rTarget
        rTarget.AddScriptPackage SomePackage
    EndIf

And for dismissing the actor

    int iTemp
    ref rTarget

    ...

    let rTarget := TargetToDismiss
    let iTemp   := ar_Find rTarget SomeQuest.aFollowers

    If ( iTemp >= 0 )
        ar_Erase SomeQuest.aFollowers iTemp
    EndIf

    rTarget.RemoveScriptPackage

So it would work completely without touching actors' scripts, making it universally compatible. :smile:

 

Those examples are guaranteed to be 100% made-up-on-the-fly and fully untested! But just to give you an idea of how it could work. The OBSE command documentation has a pretty good explanation of how arrays work. :tongue:

Edited by PhilippePetain
Link to comment
Share on other sites

I've decided to just go with group orders through dialogue for now. Thanks for your help on my multiple posts. I'll have to take some time to review your example, and read up on more tutorials.

 

There's so much to learn. Have to ask though, is the knowledge going to be largely applicable to future/other games?

Link to comment
Share on other sites

Well... I have not tried any scripting for Morrowind, but it seems to be a bit different if the tuorials I have read are correct. But I cannot comment on it. Even with MWSE, it would seem more limited to me. Or at least one would need to use more workarounds for things that are present in Oblivion with OBSE as normal commands.

 

For Skyrim... umm... it uses the new Papyrus thing. Basic Oblivion scripting will not be of much help. It will be of some, but knowing how Oblivion scripting works will not make you a master of Papyrus. If you get far enough in Oblivion scripting so that you end up using OBSE event handlers and user-created functions, they are most likely the best thing OBSE has to offer on your way to Papyrus.

 

But both Oblivion scripting and Papyrus are pretty simple, maybe. At least the basics. Papyrus just changes the syntax a bit and overhauls how scripts interact with the rest of the game, as they sort of just ripped the old scripting system out and pasted Papyrus on top of the game. Or that is how I view it. It does allow for more modularity or such, reusing scripts and whatnot, but it is a bit different. :tongue:

 

For example if you can reference an actor in Oblivion the following way:

MyActorRef.AddItem WeapIronLongsword 10

But Papyrus cannot directly reference anything in the game. :sad: So in Papyrus, you would need to make a property for the actor ref and the sword, fill them with the actor and the sword (possible in the CK menu, is pretty easy) and then use the properties in your script.

Actor Property MyActor Auto
Weapon Property MyWeapon Auto

...

MyActor.AddItem(MyWeapon, 10, false)

So... well... it is not that difficult. The basics of arrays (indexes and such) are not too different, though. Once you get the basic principles, you will have very little to learn about the... well... principles. :tongue: Syntax can change, but the ideas remain.

 

The thing with Papyrus and OBSE event handlers and user-created functions is that once you get the idea of how to call a user-created function, and know how event handlers work, you will know the basic principles of working with Papyrus. If you had a script like this as on OBSE user-created function:

ScriptName MyOBSEFunction

float fNumber
float fMultiplier
float fOutput

Begin Function { fNumber fMultiplier }

    let fOutput := ( fNumber * fMultiplier )
    SetFunctionValue fOutput

End

And you would want to use it, to get the value of, for example 2.0 x 1.5, you would call it like (in Oblivion script):

float fNumber

...

let fNumber := Call MyOBSEFunction 2.0 1.5

And it would return to you the value. For Papyrus, you could have them both in the same script. But the idea of calling a function and possibly supplying some arguments, also probably getting something back from it, are the same. Like (if it were an activator that returns the result as a messagebox):

ScriptName MyPapyrusScript Extends ObjectReference

Function FnMyPapyrusFunction(Float fNumber, Float fMultiplier)
    Float fOutput = (fNumber * fMultiplier)
    Return fOutput
EndFunction

Event OnActivate(ObjectReference akActionRef)
    If(akActionRef == Game.GetPlayer())
        Float fNumber = FnMyPapyrusFunction(2.0, 1.5)
        Debug.MessageBox("Result is " + fNumber)
    EndIf
EndEvent

And you can already see the Event OnActivate in there. Which works like an OBSE event handler, but is a script block that is executed upon the relevant event by the game. If you had an OBSE event handler, your event handler could look something like this (as an example, not a working one):

ScriptName MyOBSEEventHandler

ref rArgumentA
ref rArgumentB

Begin Function { rArgumentA rArgumentB }
    ; Code here
End

And the event handler could receive the arguments from Oblivion when the event it is registered for is fired. In Papyrus, there is no need to place separate event handlers (except for maybe SKSE mod events, but you will not need to know about them now). Every event in Papyrus scripts will be called when appropriate and the game will supply it the necessary arguments. To put it simply.

 

I hope that does not make you too confused. I only know Papyrus and the Oblivion scripting thingy (plus a little Python). And I am pretty bad at explaining things. :tongue:

 

About future games, I have no idea. Also I have no idea about other games. I like to make mods for the Elder Scrolls series only, because it is still simple enough for me. But I know Fallout 3 and Fallout New Vegas work in a similar way, although both are from the same publisher. Also, unless Bethesda goes completely nutters, I suspect Fallout 4 will use something similar to Papyrus.

 

So if you learn scripting for Oblivion, it can

  • Help you learn the very basic ideas behind arrays, strings and different variable types and how to manipulate them
  • Help you learn about variable operators and comparison operators
  • Help you understand the idea behind If-ElseIf-Else-EndIf and loops (While, ForEach, Label-GoTo)
  • Help you learn how the game (scripts and such) works under the hood (handy for anyone using mods)
  • Make it easier to move on to Fallout 3 or New Vegas, I think
  • Make it easier to learn Papyrus, but only if you get far enough with Oblivion + OBSE, and you will still need to do some reading and such (still not a piece of cake)

The Oblivion scripting is not the game code, it is code that the game runs. It is not a real programming language, and is pretty specific to those few games listed. Future titles from Bethesda will probably use different language thingy, hopefully a new version of their Papyrus language used for Skyrim, which is still not a real scripting language as it is, though.

 

Hopefully that helps a little. I might be wrong, so anyone is free to correct. :smile:

Edited by PhilippePetain
Link to comment
Share on other sites

  • Recently Browsing   0 members

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