Sjogga Posted September 12, 2012 Share Posted September 12, 2012 That's more of a quest tutorial, rather than a scripting tutorial. Sure there are always some scripting to be done for every quest, but it's only advanced if you're going to create a scene. Link to comment Share on other sites More sharing options...
Allannaa Posted September 12, 2012 Author Share Posted September 12, 2012 (edited) That's more of a quest tutorial, rather than a scripting tutorial. Sure there are always some scripting to be done for every quest, but it's only advanced if you're going to create a scene. Okay, that's quest writing, that makes sense. But what about -- The cow that "gives" milk? (That was a PM conversation between Sjogga and me 'cause I wanted the cow to, well, give milk) That was scripting, correct? So tutorials that explain how to go about that kind of thing, and of course how to "attach" such a script to the cow (or whatever) is, I think, what I'm wanting to add to our list here. (Or obviously, links to same.) Edited September 12, 2012 by Allannaa Link to comment Share on other sites More sharing options...
Sjogga Posted September 12, 2012 Share Posted September 12, 2012 The Creation Kit wiki has several good tutorials for the complete beginner. I'll just briefly go through the basics.I have a background as a C++ programmer, and thus I already knew the basics. I just had to learn the papyrus scripting language.Here is my guide to get into scripting. Part 1, the basics: These are fundamental if you are new to scripting. You're not gonna be able to test these in-game. Variables: A variable is a container of data. There are several different types of variables. I'm not gonna cover everyone, just the basic.- Integers (Int): Integers holds whole numbers, and thus cannot have any decimals. (e.g -1, 0, 1, 2, 3 etc)- Floats (Float): Floats are similar to integers, but can hold decimals. (e.g. 1.25, 3.14, 10.9)- Strings (String): Strings hold text. They are rarely used in scripting.- Booleans (Bool): Booleans are either TRUE or FALSE.- There are also variables for each Form type in the Creation Kit. Examples include Weapons (Weapon), ingredients (Ingredient), soul gems (SoulGem) etc. To create a variable, type in the Variable Type, followed by a Variable Name:Int myIntegerIn the example above, Int is the type, and myInteger is the Variable name. A variable without data assigned to it is useless. To assign data use the "=". Note that this needs to be to the right of the variable name Int myInteger myInteger = 0 Note that once we've created a variable, we do not need to enter the variable type again. Often you want to assign a Default Value to the variables you create:Int myInteger = 0Note how me merged the two previous lines of code into one. It's not always necessary to do this . Before we continue with some basic math examples, I want you to read this article to become familiar with operators. I don't expect you to learn everything right away, but I want you to have some idea of what I'm doing and why it works. The important parts are: Math operators, Comparison Operators and Assignment Operators. Done? Let's continue. Basic examples: ;example 1 int x = 10 int y = 15 int sum = x + y ;example 2 float f = 1.0 float g = 0.5 float sum = f + g ;example 3 float f = 1.5 int i = 10 float sum = f + (i as float) ;example 4 Actor ourCharacter = Game.GetPlayer() The first two examples are fairly simple math examples. In the first example, sum is assigned the sum of x and y, which is 25.In the second example sum is assigned the value of f and g, which is 1.5. The third is a bit weird. Normally you can't assign an int-type variable to a float. We have to type-cast it into a float first. This is done by typing as <variable type>. The fourth is even more complicated. First of the variable type is an Actor. Second, it is assigned some strange value. Which leads us to the next topic: Functions:GetPlayer() is a function. Functions often return values, in this case the player. As you might also notice, there is another part to this: Game. . This means that the function GetPlayer() is a part of another script, in this case the Game Script. There are hundreds of functions already in the game, all parts of various other scripts. A basic sum-function: int x = 10 int y = 15 int xPlusY = sum(x, y) Function sum(int i, int j) int value = i + j return value endFunction Lets break it down shall we.x and y are assigned values, 10 and 15. We send x and y into the sum-function. Everything within the parenthesis are called parameters. A quick look at the function itself:To create a function, you need to write Function <function name> (<parameters>). Like variables, the function name can be anything. You can have any amount of parameters, even none at all. Remember to separate the parameters with a comma ( , ). The variable types you send into a function must match the variable types declared in the function. For example, we cannot send in a float variable into our sum-function unless we type-cast it. Following that is the basic operation we did earlier. The return part returns value, which is then assigned to the xPlusY variable Events: For a script to be executed, something needs to happen within the game. These things are called Events.Scripted spells often use Event OnEffectStart(Actor Target, Actor Caster)This basically means that when the magic effect of this spell begins, we run our code. Events are added to you code similar to functions. You cannot create your own Event however. Event <EventName> ;do something endEvent Let's look at an example from The Dawnspire. event OnHit(ObjectReference akAggressor, <followed by lots of irrelevant parameters>) self.PushActorAway((akAggressor as actor), force) endEvent This script is attached to an object. When this object is hit by something, the "hitter" (akAgressor) gets blown away. (You may notice the Forcevariable. This is declared earlier in the code)There isn't much else to tell about events. Useful tools If and While: The If statement is a way to determine id certain conditions are met within your script. For example, we might have a spell that removes dead bodies when cast on, well dead bodies. Event OnEffectStart(Actor Target, Actor Caster) If(Target.isDead() == TRUE) Target.delete() endIf endEvent isDead() and delete() are a functions already in the game. isDead() returns a boolean value, either TRUE or FALSE. Take a look at this again.You'll see a lot of if-comparison within my scripts (which I'll post later). The while loop: Assume we wanted to display message boxes ingame, each counting upwards from 0 to 10.It would look something like this: debug.messagebox("0") debug.messagebox("1") debug.messagebox("2") debug.messagebox("3") debug.messagebox("4") debug.messagebox("5") debug.messagebox("6") debug.messagebox("7") debug.messagebox("8") debug.messagebox("9") debug.messagebox("10") The while-loop can merge this into five lines of code: int i = 0 while(i<=10) debug.messagebox(i) i += 1 endWhile Lets examine what I just did.Everything between "while" and "endWhile" is repeated until the statement withit the parenthesis are false. Basically, as long as i is smaller than or equal to 10 the loop will continue. As such, a message box will pop up, displaying the value of i (0) during the first iteration of the loop. After that, i will increase by one and the loop starts over again. It begins to check if the statement is false. It is not as i is 1, which is less than 10. 1 is displayed in a message box, and i increases by one. This continues until i becomes 11 at which point the loop stops. This is pretty much everything I'm gonna cover in this part. I'll post the next part when I have time to write it. I still suggest that you read Bethesdas Official Tutorials Link to comment Share on other sites More sharing options...
Allannaa Posted September 13, 2012 Author Share Posted September 13, 2012 Sjogga, that was AWESOME! As soon as I remember how to link from a post to a post, I'll add this to our front page. Link to comment Share on other sites More sharing options...
Allannaa Posted September 14, 2012 Author Share Posted September 14, 2012 (edited) The only issue I have with tutes of this sort tho, is, it assumes the person really wants to learn to code -- That isn't always the case. Sometimes, the reader just wants to know how to do something specific -- How to add an item to inventory, how to make a quest update after a bad guy is killed, and so on. I'm NOT saying it's a bad thing to explain how to code, btw. I just wonder if there are also tutes anyone has found which don't explain "why", and just say "do this to make this happen" type things. I read the pages linked by Sjogga above, and basically none of it meant anything to me because the examples were too abstract... or because I'm dumb as a rock, take your pick, but I'm not the only one. For instance -- "The cast operator attempts to cast the value from the left expression to the type to the right of it, and returns the resulting value. Examples: ; cast a to a float and assign it to ii = a as float" What on earth does that mean? To me "cast" means cast a spell... is that what this code snippet does? And what happened to simple "code" like "If (player has killed baddie)... Then (quest update setstage 100)" type stuff? I guess what I'm saying is, does a person really need to know what all these things mean in terms of computer language? Isn't there just some simple way of saying "Type this to make this happen, and attach it to this NPC or to this item". Like the "make anything a chair" or "display your claws" tutorials -- we didn't have to understand all these esoteric terms. We just had to know, Do this, This, and This. Edited September 14, 2012 by Allannaa Link to comment Share on other sites More sharing options...
IsharaMeradin Posted September 14, 2012 Share Posted September 14, 2012 With papyrus scripting, the problem lies in the fact that what works in one situation may not work completely or at all in another situation.The reason you didn't have to know about the "esoteric terms" with say the "display your claws" tutorial was that sjogga had a pre-written script to put in place. If there is no script in existence to do what you want & most times that is the case then you need to unfortunately know how to write the script from the ground up. You may be lucky to find another mod that does something similar and convert that script to your purposes but that still requires knowing how to write the script. Even luckier still would be asking in the forums and having someone write a script for you, that however is highly unlikely as in many cases writing a working script requires having all other aspects of the mod in place.Also some modders like to hold back little secrets, tricks that they've learned. Many mods don't ship with the source files for that precise reason. Link to comment Share on other sites More sharing options...
KiwiHawkNZ Posted September 14, 2012 Share Posted September 14, 2012 What kind of scripting tutorial do you want? It's a pretty broad subject. Kia ora I been looking for and never found any tut's on how to set up a ring puzzle door and a claw thatsnot gonna break any doors in game I'm thinking it's in the scripts The other thing I tried (nevr worked) I got the secretwall activator (one from whitrun stock) and tried to use a pull chain to activate itto no availe Just a couple of thoughts on stuff people might like to know While I'm at it OP you need scan over this blog mate http://hoddminir.blogspot.se/2012/04/beta-hoddminir-world-space.html think you'll smile man I realy do A Navnesh tutorial http://www.theengineeringguild.co.uk/index.php?option=com_content&view=article&id=384:skyrim-navmeshing-tutorial&catid=20&Itemid=115 Edit:Things I never found tutorials for Region generation (they all sem to breack into LOD tut's) how to generate grass, rock, trees etclike we could in Oblivion Making a Race (standalone that CAN wear armors) Link to comment Share on other sites More sharing options...
IsharaMeradin Posted September 15, 2012 Share Posted September 15, 2012 Things I never found tutorials for<snip>Making a Race (standalone that CAN wear armors) Can you be more specific on that? All the custom races I've ever tried have been fully able to wear armor both stock and custom. Link to comment Share on other sites More sharing options...
Allannaa Posted September 15, 2012 Author Share Posted September 15, 2012 (edited) With papyrus scripting, the problem lies in the fact that what works in one situation may not work completely or at all in another situation.The reason you didn't have to know about the "esoteric terms" with say the "display your claws" tutorial was that sjogga had a pre-written script to put in place. If there is no script in existence to do what you want & most times that is the case then you need to unfortunately know how to write the script from the ground up. Okay, Ishara, that makes sense -- but -- is there a way for things to be explained, which will make sense to non-programmers (or in my case, idiots)? Honestly, I still don't know if the snippet I copied, has something to do with casting a spell or what. Seriously, like I said, Sjogga's tutorials are awesome (even this one that I don't understand) but surely there are some commonalities among "modder-written" scripts, like the ones I mentioned -- advance a quest, add or take an item, mark a map, and so on...? Yes, all mods are different, and yes, everyone's stuff does something a little new or whatnot but still -- aren't there those commonalities I mentioned? Frankly -- I can get all the damn rocks I want to talk, I can bring back all the amulets from all the dead thieves in Skyrim, and so on. But I can't extend that to other situations, and all the code definitions in the universe aren't going to help me, when the definitions are so "weird" to an average person. (For instance, and this is NOT NOT NOT a slam at Sjogga) I still think "int" or "integer" refers to some off the wall math term that may mean "a number", and I still think "cast" has something to do with a spell. You see what I'm getting at? I'm going to keep looking for "scripting for beyond dumb dummies" to post here for us all, but meantime, I'm also going to keep re-reading Sjogga's already-posted stuff. Which reminds me, there are still other bits of the "list of stuff we want to learn" that need finding -- Don't be shy folks, if you wrote or found one, link it in and we'll add it! (And aside to Kiwi-Hawk, I thought I was the only person on earth who still said Kia Ora!) Edited September 15, 2012 by Allannaa Link to comment Share on other sites More sharing options...
IsharaMeradin Posted September 15, 2012 Share Posted September 15, 2012 I still think "int" or "integer" refers to some off the wall math term that may mean "a number"you are correct. it means a WHOLE number. Think of Papyrus scripting in this way.... Everything flows downhill, but you can't go uphill. --- aka variables, values, properties can be used in lower 'nests' of events, functions & if statements... but you can't refer to them at a higher level. You can use zip lines to refer to things on another hill --- aka import from another script the 'cast' in the example you were referring to is not 'casting' a spell... Tho if you want to think of it in spell terms... you'd use the spell transmute to change one variable from one type into another type. Basically Papyrus is dumb when it comes to math and doesn't understand that a whole number can be computed with a decimal number. You have to tell papyrus to use one form or the other. Thus you 'cast' it like casting metal into a mold. It's still the same thing (a number) but its just in a new form. Why its 'dumb' like that, I don't know. I'm going to use the following script to explain the first two statements as best I can.... I'm no papyrus guru. I just know what I've done that works and what I've done that doesn't.... Scriptname ResourceFurnitureScript extends ObjectReference Conditional {script for furniture which the player can use to get resources} formlist Property requiredItemList Auto {required for player to use - optional} Message Property FailureMessage Auto {Message to say why you can't use this without RequiredWeapon} MiscObject Property Resource Auto {what you get from using this furniture} int Property ResourceCount = 1 Auto {how many resources you get per use} int property MaxResourcePerActivation = 6 auto {How many times can this object be used before the player has to re-activate?} int counter ; count up how many resources have been gathered on this go. faction property CurrentFollowerFaction auto {Used to handle player followers using the furniture object} objectReference property NPCfollower auto hidden {hidden property to track followers who used this} Event OnLoad() BlockActivation(true) endEvent Event OnUnload() ; safety measure UnregisterForEvents(game.getplayer()) if NPCfollower UnregisterForEvents(NPCfollower) endif endEvent auto STATE normal Event OnActivate(ObjectReference akActionRef) gotoState("busy") ; debug.trace(self + "OnActivate") if akActionRef == Game.GetPlayer() || (akActionRef as actor).isInFaction(CurrentFollowerFaction) ; debug.trace("akActionRef is either player or a follower") if (akActionRef as actor) != game.getPlayer() ; debug.trace("It's a follower - store in NPCfollower property") ; if not the player, must be the follower NPCfollower = akActionRef endif bool allowActivation = true ; check if player has required item if requiredItemList if akActionRef.GetItemCount(requiredItemList) == 0 if akActionRef == game.getPlayer() ; only require the axe item for the player allowActivation = false ; debug.trace("allowActivation = "+allowActivation) FailureMessage.Show() endif endif endif if allowActivation ;my added stuff hope it works -- IsharaMeradin if akActionRef == Game.GetPlayer() int playersMaxCarryWeight = Game.GetPlayer().GetActorValue("carryweight") as int ;gets the player's max carry weight int playersInventoryWeight = Game.GetPlayer().GetActorValue("inventoryweight") as int ;gets the player's current inventory weight int FirewoodWeight = 5 ;change the above value if a mod changes the weight of firewood -- vanilla default is 5 int FirewoodToGet =( (playersMaxCarryWeight - playersInventoryWeight) / FirewoodWeight) - 2 ;determine base amount of firewood that will fit in player's inventory ;determine what value to use for the # of firewood to get. if (FirewoodToGet <= 0) MaxResourcePerActivation = 6 ;change the value above if you want a different base to collect when encumbered ;can also change the assigned value at the top of the script if you want the base to collect ;for followers to be different as well. else MaxResourcePerActivation = FirewoodToGet endif endif ;end my added stuff -- IsharaMeradin RegisterForEvents(akActionRef) ; debug.trace(self + "player/follower activation START") Activate(akActionRef, true) ; debug.trace(self + "player/follower activation END") endif else ; ;debug.trace(self + "non-follower NPC activation START") ; just activate it Activate(akActionRef, true) ; ;debug.trace(self + "non-follower NPC activation END") endif gotoState("normal") endEvent endState STATE busy ; do nothing endState Event OnAnimationEvent(ObjectReference akSource, string asEventName) ; debug.trace(self + ": animation event received=" + asEventName) if asEventName == "AddToInventory" ;add stamina event here -- IsharaMeradin int PlayerBaseStamina = Game.GetPlayer().GetBaseActorValue("stamina") as int if PlayerBaseStamina >= 100 && PlayerBaseStamina <110 float DrainPlayerBaseStamina = 69 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 6 wood @100 elseif PlayerBaseStamina >= 110 && PlayerBaseStamina <120 float DrainPlayerBaseStamina = 72 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 8 wood @110 elseif PlayerBaseStamina >= 120 && PlayerBaseStamina <130 float DrainPlayerBaseStamina = 76 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 10 wood @120 elseif PlayerBaseStamina >= 130 && PlayerBaseStamina <140 float DrainPlayerBaseStamina = 80 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 12 wood @130 elseif PlayerBaseStamina >= 140 && PlayerBaseStamina <150 float DrainPlayerBaseStamina = 85 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 14 wood @140 elseif PlayerBaseStamina >= 150 && PlayerBaseStamina <160 float DrainPlayerBaseStamina = 90 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 16 wood @150 elseif PlayerBaseStamina >= 160 && PlayerBaseStamina <170 float DrainPlayerBaseStamina = 94.5 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 18 wood @160 elseif PlayerBaseStamina >= 170 && PlayerBaseStamina <180 float DrainPlayerBaseStamina = 100 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 20 wood @ 170 elseif PlayerBaseStamina >= 180 && PlayerBaseStamina <190 float DrainPlayerBaseStamina = 104.5 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 22 wood @ 180 elseif PlayerBaseStamina >= 190 && PlayerBaseStamina <200 float DrainPlayerBaseStamina = 109.5 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 24 wood @190 elseif PlayerBaseStamina >= 200 && PlayerBaseStamina <210 float DrainPlayerBaseStamina = 115 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 26 wood @200 elseif PlayerBaseStamina >= 210 && PlayerBaseStamina <220 float DrainPlayerBaseStamina = 120 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 28 wood @210 elseif PlayerBaseStamina >= 220 && PlayerBaseStamina <230 float DrainPlayerBaseStamina = 125 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 30 wood @220 elseif PlayerBaseStamina >= 230 && PlayerBaseStamina <240 float DrainPlayerBaseStamina = 130 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 32 wood @230 elseif PlayerBaseStamina >= 240 && PlayerBaseStamina <250 float DrainPlayerBaseStamina = 135.5 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 34 wood @240 elseif PlayerBaseStamina >= 250 && PlayerBaseStamina <260 float DrainPlayerBaseStamina = 141 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 36 wood @250 elseif PlayerBaseStamina >= 260 && PlayerBaseStamina <270 float DrainPlayerBaseStamina = 146.25 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 38 wood @260 elseif PlayerBaseStamina >= 270 && PlayerBaseStamina <280 float DrainPlayerBaseStamina = 151.5 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 40 wood @270 elseif PlayerBaseStamina >= 280 && PlayerBaseStamina <290 float DrainPlayerBaseStamina = 156.5 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 42 wood @280 elseif PlayerBaseStamina >= 290 && PlayerBaseStamina <300 float DrainPlayerBaseStamina = 161.75 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 44 wood @290 elseif PlayerBaseStamina >= 300 float DrainPlayerBaseStamina = 167.1 Game.GetPlayer().DamageActorValue("stamina", DrainPlayerBaseStamina) ;nets 45 wood @300 endif ;end stamina event... -- IsharaMeradin akSource.AddItem(Resource, ResourceCount) ; increment counter by however many items we just received ; debug.trace("Pre-add counter = "+counter) counter = (counter + resourceCount) ; debug.trace("Post-add counter = "+counter) ;add stamina event exit here -- IsharaMeradin int PlayerCurrentStamina = Game.GetPlayer().GetActorValue("stamina") as int if PlayerCurrentStamina <= 0 counter = MaxResourcePerActivation ;if not enough stamina we set things to exit endif ;end stamina event exit.... -- IsharaMeradin if counter >= MaxResourcePerActivation ; if we've bagged our limit, kick the player out. Reset timer for next activation ; debug.trace("Woodpile - player has gotten "+counter+" logs this go. Kicking out.") counter = 0 (akSource as actor).PlayIdle(IdleWoodchopExit) unregisterForEvents(akSource) endif elseif asEventName == "IdleFurnitureExit" ; debug.trace("Resource Object Unregistering: "+self) ; reset the counter if I exit manually counter = 0 UnregisterForEvents(akSource) endif endEvent bool isRegisteredForEvents = false function RegisterForEvents(objectReference whoToRegister) ; centralize this isRegisteredForEvents = true RegisterForAnimationEvent(whoToRegister, "AddToInventory") RegisterForAnimationEvent(whoToRegister, "SoundPlay . NPCHumanWoodChop") RegisterForAnimationEvent(whoToRegister, "IdleFurnitureExit") endFunction function UnregisterForEvents(objectReference whoToUnregister) ; centralize this ; It is perfectly safe to unregister for events you never registered for, however ; this function is called as part of OnUnload, and if this object isn't persistent ; then it may be deleted by the time OnUnload runs, and these function calls will ; fail. Since RegisterForAnimationEvent persists us, we know it will be safe to ; call Unregister if we've previously Registered, even if called as a part of ; OnUnload if isRegisteredForEvents isRegisteredForEvents = false UnRegisterForAnimationEvent(whoToUnregister, "AddToInventory") UnRegisterForAnimationEvent(whoToUnregister, "IdleFurnitureExit") endif endFunction Idle Property IdleWoodchopExit Auto This is the script used to determine how the wood chopping block behaves for the player. You'll notice if you compare to the original that I've added quite a few lines. But what is important to your questions is that the integers and properties assigned at the top of the script can be freely used anywhere in the script. However, the integers (whole number values) and floats (decimal values) that I added in cannot be used in functions, events or even if statements that are not below their point of definition. This is a good thing when modifying an existing default script because when it gets removed from the game, there are no events, functions or statements that are trying to call up anything related to the changes. Where you see lines like this:int playersMaxCarryWeight = Game.GetPlayer().GetActorValue("carryweight") as intThe Game.GetPlayer().GetActorValue() is actually telling the wood chopping script to "zip over" to the Game script, use the GetPlayer() function to pull up the player's data and then use the GetActorValue() function to get that player's value for the specified actor value, in this case carry weight. This is just one way to import information from another script. Other scripts will use the Import command at the top of the script to allow usage of that external script throughout the entire script. Useful for fully custom scripts. and I probably made the mud even muddier... :P Link to comment Share on other sites More sharing options...
Recommended Posts