Jump to content

Tutorials WITHOUT "youtoob"


Allannaa

Recommended Posts

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 by Allannaa
Link to comment
Share on other sites

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 myInteger

In 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 = 0

Note 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

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 i

i = 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 by Allannaa
Link to comment
Share on other sites

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

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 thats

not 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 it

to 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 etc

like we could in Oblivion

 

Making a Race (standalone that CAN wear armors)

Link to comment
Share on other sites

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 by Allannaa
Link to comment
Share on other sites

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 int

The 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

  • Recently Browsing   0 members

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