Smollett Posted January 15, 2013 Share Posted January 15, 2013 I'm am making a mod that initializes on character creation, and as they level up they are granted a more powerful version of a spell. I have most if not all the Quest tabs filled out, but I'm having issues with the scripting. I have placed the main script below, but essentially I run an OnInit (game starter) update function which checks the character's level, spell knowledge, and quest stage. At level 10 the "quest" begins with a stage 1 spell, at 20 its stage 2 spell and so on. ProblemI have a main script with the update function to check the players level and setup the spell and quest stage accordingly. It works when first initialized. However I have a secondary script attached to the player (alias Tab) that checks when you load a game, and when you level up (if I read creationkit.com correctly). These call the update function to check if the character has reached the next tier. Unfortunately, these don't seem to work and neither the level up nor the OnGameLoad() seem to call the update script. More frustrating is that this is a method used in a few mods I've seen which work perfectly, so I'm a little lost in what could be the issue. Again it could be as simple as a checkbox in the dialog tabs too. Obviously there are areas of improvement (and you are free to point them out) since i'm rather new to scripting in Papyrus but I've examined a lot of script from other mods and figured out a lot just by taking them apart and watching how they run. Also I have messagebox's here there and yonder for debugging. So: the script that is in my "Script Tab" that runs the OnInit and the update function is immediately below and the player script attached via the Quest Alias Tab is further below it:-------------------------------------------------------------------------------------------------------------------------------------------------------- Scriptname AAAMageLife extends Quest {SCript for moving up the level ladder} Quest Property qst Auto ; Quest Spell Property lvl1spl auto ; level1 Spell Property lvl2spl auto ; level2 Spell property lvl3spl auto ; level3 Spell property lvl4spl auto ;level4 FormList Property FRMLST Auto ;Formlist with the above spells in them Actor Property me Auto ; make life easier to refer to the char as "me" ; On the very first run, re-baseline it all! Event OnInit() updateplz() Utility.wait(1.0) qst.setstage(updatestage(me)) EndEvent Function updateplz() debug.messagebox("I am updating") me.removespell(GetKnownSpell(me, FRMLST)) me.addspell(updatespell(me)) endFunction ;;;;;;;;;;;;;;;;;;;;;;; Function finds the right spell for the level and returns it to the update function Spell Function updatespell(Actor plr) int lvl = plr.getlevel() Spell givespell if (lvl<=10) Return Givespell as Spell elseif (lvl>=10)&&(lvl<20) givespell = lvl1spl elseif (lvl>=20)&&(lvl<30) givespell = lvl2spl elseif (lvl>=30)&&(lvl<40) givespell = lvl3spl elseif (lvl>=40) givespell = lvl4spl endif debug.messagebox("I gave you a spell called " + givespell) Return givespell as Spell EndFunction ;;;;;;;;;;;;;;;;;;;;;;; Function removes all spells contained in the formlist Spell Function GetKnownSpell(Actor Subject, FormList SpellFormList) Global int kspell = 0 while kspell < SpellFormList.GetSize() if Subject.HasSpell(SpellFormList.GetAt(kspell)) Spell TS = SpellFormList.GetAt(kspell) as Spell Subject.removespell(TS) endif kspell += 1 endwhile return None EndFunction int Function updatestage(Actor plr) int lvl = plr.getlevel() int Stage if (lvl<=10) Stage = 5 elseif (lvl>=10)&&(lvl<20) Stage = 10 elseif (lvl>=20)&&(lvl<30) Stage = 20 elseif (lvl>=30)&&(lvl<40) Stage = 30 elseif (lvl>=40) Stage = 40 endif debug.messagebox("I am returning a stage of " + stage) Return stage as int EndFunction ---------------------------------------------------------------------------------------------------------------------------- Scriptname AAAMagelifeincrease extends ReferenceAlias ; This script merely calls the original quest script to re-run the level checker AAAMageLife Property up Auto Event OnStoryIncreaseLevel(int lvl) debug.messagebox("you leveled up!") up.updateplz() EndEvent Event OnPlayerLoadGame() debug.messagebox("you've loaded a game and I caught it") up.updateplz() EndEvent Link to comment Share on other sites More sharing options...
T3nd0 Posted January 18, 2013 Share Posted January 18, 2013 (edited) Ok, a few observations. Spell givespell if (lvl<=10) Return Givespell as Spell You're essentially returning an "empty" spell here. Also, no need for casts. me.removespell(GetKnownSpell(me, FRMLST)) GetKnownSpell might return none. Better to check for that to minimize Papyrus errors. Return stage as int You declared stage as Integer, no need for a cast. Event OnStoryIncreaseLevel(int lvl) debug.messagebox("you leveled up!") up.updateplz() EndEvent To recieve events from the story manager, your quest must be set up in a special way. I guess this is why all fails. Read here: http://www.creationkit.com/Category:Story_Manager Edited January 18, 2013 by T3nd0 Link to comment Share on other sites More sharing options...
Smollett Posted January 18, 2013 Author Share Posted January 18, 2013 ok, I'll put in a simple return in the spell block and see about cleaning up the casts. So far there are no errors (other than the linkage broken) but cleaning the code is the right thing to do. I have read that page but I'm a rock in understanding some of its nuances. However I do see how this could tie in to perhaps connecting the alias script in with the mainscript. Thanks for the check! -S edit: I put in the changes but it didn't compile. It demands the cast, and it demands that I return an empty spell. That's ok in my book since I don't know that it breaks anything (I can check above in the function call) and if I decide to update it in the future then the placeholder is there. Also, I noted that my alias is "player" but for brevity, I used "me" in my script code. tests show it doesn't do much but I think I'll keep the same nomenclature just in case. Link to comment Share on other sites More sharing options...
Smollett Posted January 18, 2013 Author Share Posted January 18, 2013 Ok, I got this thing to work partially. By setting up the Story Manager and the quest start event I was able to ping the quest when the player levels up. On the very first level up, the quest activates, and it goes through the OnInit, and the script runs when the game loads.However on subsequent levels, the quest isn't getting re-pinged or it does nothing. The "Run Once" flag is unchecked and I'm looking at examples but I can't see anything that is close to the situation. Any Ideas? -S Link to comment Share on other sites More sharing options...
T3nd0 Posted January 19, 2013 Share Posted January 19, 2013 About the empty spell: I think returning (and checking for) "None" would be better. So you at least have a method of checking if that first case kicked in. So, what exactly happens on subsequent level ups? Link to comment Share on other sites More sharing options...
Smollett Posted January 19, 2013 Author Share Posted January 19, 2013 To answer your question, on a second level up, nothing happens. At every tenth level nothing happens. Since the code has changed significantly, I'll re-post it. Everything works perfectly on the first level up. The quest starts, immediately goes to the right stage, the appropriate spells are added and all is right with the world, so at this point I think it's the link between Story Manager and the quest. However, I could be overlooking something so I include the new code. Just for the record I am using "player.advskill Onehanded xxxx" in the console, and I'm pretty sure this should adequately simulate normal game leveling. Some notes about the new code vs. the old. Because I am using the Story Manager to ping the quest on level up, I decided to do away with the OnInit and replace it with the OnStoryIncreaseLevel. It'll start when the player first levels up and not at the very beginning. 6 of 1, half dozen of the other really. You'll also note I removed the secondary functions and with help from others I was able to shorten a lot of the code. Again, there are improvements to make but I think the script is pretty well condensed enough that it won't be a hog. So the new code is: Scriptname AAAMageLife extends Quest {SCript for moving up the level ladder} Quest Property qst Auto ; Quest reference Spell Property lvl1spl auto ; Level 1 spell Spell Property lvl2spl auto ; Level 2 spell Spell property lvl3spl auto ; Level 3 spell Spell property lvl4spl auto ; Level 4 spell Spell property extraspell Auto ; extra spell if I want to add it later on (thinking about it...) FormList Property FRMLST Auto ; Formlist with the above spells Actor Property me Auto ; make my life easier Event OnStoryIncreaseLevel(int flvl) debug.messagebox("you leveled up!") updateplz(flvl) EndEvent ; Function which removes the older spell, and adds the new one IF the player is above the threshhold. Function updateplz(int lvl) Spell givespell int qstage debug.messagebox("I'm in the update loop. Your level is "+lvl) ; Remove the spells in the formlist to re-baseline int kspell = 0 while kspell < FRMLST.GetSize() if me.HasSpell(FrmLst.GetAt(kspell)) Spell TS = FrmLst.GetAt(kspell) as Spell me.removespell(TS) endif kspell += 1 endwhile ; add the spells necessary and set the quest stage if lvl<10 qstage = 0 elseif lvl<20 givespell = lvl1spl qstage = 10 elseif lvl<30 givespell = lvl2spl qstage = 20 elseif lvl<40 givespell = lvl3spl qstage = 30 elseif lvl>=40 givespell = lvl4spl qstage = 40 endif me.addspell(givespell) if !(me.hasspell(extraspell))&&(qstage >=20) me.addspell(extraspell) endif If GetStage() != qStage SetStage(qStage) EndIf debug.messagebox("The update is done. I added " + givespell + " and set the quest stage to " + Getstage()) endFunction Edit: By "Nothing Happens" I mean that the messageboxes I put into the code to alert me when things are going on do not show up, nor do the spells change. Edit#2: Ah HA! I got it to work. I went back over the skill increase quests (they were the closest to what I wanted to do) and noted that at the very end of the OnStoryIncreaseLevel event, there was a Stop(). Essentially since the quest hadn't stopped, the Story Manager didn't re-initialize the quest, therefore nothing was happening. Now I just need to clean up a few things and should be ready! Thanks for the help T3! Link to comment Share on other sites More sharing options...
steve40 Posted January 19, 2013 Share Posted January 19, 2013 (edited) It's rather redundant having the spells in a formlist AND as separate properties. Redundant properties consume more resources.It would be more efficient if you only ran the while loop when the player needs to gain a spell, rather than running it EVERY time the player levels up.The while loop can be made more efficient by reducing the number of function calls to the formlist. Scriptname AAAMageLife extends Quest {SCript for moving up the level ladder} Quest Property qst auto ; Quest reference FormList Property FRMLST auto ; Formlist with all the level spells. FRMLST[0] == lvl1spl, FRMLST[1] == lvl2spl,... Spell Property extraspell auto Actor Property PlayerREF auto ; use the same name as in the CK so the property will autofill Event OnStoryIncreaseLevel(int flvl) Debug.MessageBox("[AAAMageLife:OnStoryIncreaseLevel]You leveled up! Your level is "+flvl) updateplz(flvl) EndEvent Function updateplz(int lvl) { Function which removes the older spell, and adds the new one IF the player is above the threshhold. } Debug.MessageBox("[AAAMageLife:upateplz]Your level is "+lvl) int givespell = -1; the index of the spell in FRMLST int qStage ; determine the spells necessary and the quest stage to set If lvl >= 10 && lvl < 20 givespell = 0 qStage = 10 ElseIf lvl >= 20 && lvl < 30 givespell = 1 qStage = 20 ElseIf lvl >= 30 && lvl < 40 givespell = 2 qStage = 30 ElseIf lvl >= 40 givespell = 3 qStage = 40 EndIf ; add the new spell if the player doesn't already have it Spell GS = FRMLST.GetAt(givespell) as Spell ; the spell to give If givespell > -1 && !PlayerREF.HasSpell(GS) ; Remove the spells in the formlist to re-baseline int i = 0 int j = FRMLST.GetSize() ; only call this once for efficiency While i < j Spell TS = FRMLST.GetAt(i) as Spell ; only call this once for efficiency If PlayerREF.HasSpell(TS) PlayerREF.RemoveSpell(TS) EndIf i += 1 EndWhile PlayerREF.AddSpell(GS) EndIf If qstage >=20 && !PlayerREF.HasSpell(extraspell) PlayerREF.AddSpell(extraspell) Debug.MessageBox("[AAAMageLife:upateplz]Added extra spell " + extraspell) EndIf If GetStage() != qStage SetStage(qStage) EndIf Debug.MessageBox("[AAAMageLife:upateplz]Added spell " + givespell + ", set stage " + Getstage()) EndFunction Edited January 20, 2013 by steve40 Link to comment Share on other sites More sharing options...
Smollett Posted January 20, 2013 Author Share Posted January 20, 2013 Thanks Steve,Now that I have it working, I was going to look at options to level up every 10 levels. It'd be better if I could tell the Manager not to ping the quest until certain levels but if someone loads it up at, say level 11, then you'd want to go ahead and add all the stuff. The only way to do that is obviously inside the script which gets run anyway. Idea....maybe I can let the OnGameLoad event handle that scenario and let the SM ping at every tenth level.... As for the formlist, this is true. I did it that way because at the time I wasn't sure if the spells would stay in the formlist right, so it was important to give a level 10 spell to the level 10 player and not the level 40 spell which would occur if the first spell in the list is level 40. Right now I'm mulling over just throwing it all into an array and do it all behind the scenes and just remove/add from a hidden array but I'm thinking its probably not any different one way or another. I did this for an oblivion mod where I threw all the armor into an array and it mimicked what Skyrim's "outfits" are now. Now that I have this running I can toy with ways to make it faster/cheaper/etc. About your script:1) Debug.MessageBox("[AAAMageLife:OnStoryIncreaseLevel]You leveled up! Your level is "+flvl) Does this essentially cancel the messagebox unless you level up? I've removed the messageboxes since they only served to help me debug, but I could use the same idea elsewhere. 2) Something I learned while coding this is using "Elseif" can help you out when it comes to exclusion/inclusion thus reducing the "AND"/"OR" type if statements. I rebuilt the if statements to take the reduced reading into account. If we have the Elseif in there you are essentially kicked out of the if tree when it becomes true, so all you need is "< X" in there and it shouldn't go to the next Elseif. There are exceptions to this but in this case as I go through the logic it works pretty well and it works in game. Finally, all this has given me a couple of other juicy ideas to add to it. In fact, I think I might re-write the whole thing now that I know how it all goes together and make it a true monster....why not?-S edit: I took out the "lvlXspell" entries and I put an array into the code instead of a formlist. It works like a charm and actually condensed my code slightly from the methods used for a formlist. It also allows me to populate the array in a sub-dialog box in the script 'properties' which makes it essentially a formlist without a having a database entry. Link to comment Share on other sites More sharing options...
Recommended Posts