elseagoat Posted March 18, 2012 Share Posted March 18, 2012 Ok, so I am trying to make a rune spell that summons skeletons to attack the enemy for a few seconds, but I found that to do this, if I want multiple skeletons to appear, then I need to mess with papyrus scripts. So I am guessing the best way to do this would be to go and copy an existing rune spell, magic effect, and projectile and then tweak all the settings so it looks good and doesn't explode. But as for the magic effect, it needs to be a script instead of a damage actor value (Still guessing here, I just started modding yesterday, no previous experience.) So I decided to take a look at how to do these scripts and it seems rather daunting. Tried the CK wiki, and it didn't help me a whole lot, I have been trying to sift through all the information for several hours over the course of the past two days and can't seem to wrap my head around it. I do better with this kind of things when I see them working in action. So I dug up a script in the CK and was hoping someone could walk me through it (as it looks basic enough and instead of writing from scratch, maybe I could modify this one to suit my purposes and learn how the script works in the process.) So, bit by bit, here are my questions: Scriptname summonSkeletonLegionSCRIPT extends ActiveMagicEffect This part obviously defines the script name and type and whatnot, I am guessing I would also use "extends ActiveMagicEffect" for what I want to do since the magic effect would be the thing that I would attach the script to in order to summon the skeletons? ACTORBASE PROPERTY skeletonType AUTO ACTIVATOR PROPERTY summonPortal AUTO I don't really understand this at all. What are these lines doing? What are ACTORBASE, PROPERTY, and AUTO? And what are skeletonType and summonPortal? (I am wondering if I am supposed to replace those two with the skeleton actor that I have set up to be summoned and I have no clue about the summonPortal). OBJECTREFERENCE skeleton1 OBJECTREFERENCE skeleton2 OBJECTREFERENCE skeleton3 Again, I am very confused here. What do these lines do, and what is skeleton1 - skeleton3 referring to? OBJECTREFERENCE portal1 OBJECTREFERENCE portal2 OBJECTREFERENCE portal3 Becomes pretty apparent here that the above 6 lines are linked to the two lines preceding with skeletonType and summonPortal, but I am unclear as to what the relationship is. Could it be that the lines above are defining skeleton 1-3 and portal 1-3 as variables and the lines below in the event are "putting information into" the variables, and the OBJECTREFERENCE bits are somehow creating the references in the game itself (spawning the skeles and portals?) EVENT onEffectStart(ACTOR akTarget, ACTOR akCaster) skeleton1 = game.getPlayer().placeAtMe(skeletonType) portal1 = skeleton1.placeAtMe(summonPortal) skeleton2 = game.getPlayer().placeAtMe(skeletonType) portal2 = skeleton2.placeAtMe(summonPortal) skeleton3 = game.getPlayer().placeAtMe(skeletonType) portal3 = skeleton3.placeAtMe(summonPortal) So here is an EVENT, and I get what events are, and onEffectStart (I get that too), but the (ACTOR akTarget, ACTOR akCaster) I do not get. How is this particular script using the target and caster of this spell? Is this just mandatory to type every time I use onEffectStart or something? If so, why? utility.wait(5) portal1.disableNoWait() portal2.disableNoWait() portal3.disableNoWait() endEVENT I am not sure what this does. EVENT onEffectFinish(ACTOR akTarget, ACTOR akCaster) skeleton1.disableNoWait() skeleton2.disableNoWait() skeleton3.disableNoWait() endEVENT This obviously does something similar to the lines above, but again, I do not understand what. Obviously the "disableNoWait()" is happening when the effect (which I assume is the Magic Effect) finishes instead of when it starts. But other than that, I am clueless as to what this is doing and how. Also, I would like to know how I could modify this so that the skeletons are spawned at the location of the Rune when it is triggered, because it appears that the skeletons spawn at the player in this script, and also, how can I remove the skeletons from the game after, say, 20 seconds? Any reply is much appreciated. I apologize for my ignorance and the wall of text, but I feel like this will be the fastest way for me to begin understanding Papyrus! Thanks! Link to comment Share on other sites More sharing options...
fg109 Posted March 18, 2012 Share Posted March 18, 2012 (edited) You are the perfect test subject for my (WIP) article on scripting! Ahem! Anyway, you are correct in thinking that for a magic effect script, you need "extends ActiveMagicEffect" after the script name. ACTORBASE PROPERTY skeletonType AUTO ACTIVATOR PROPERTY summonPortal AUTO These are properties, I explain them in my article. To explain them takes a while, so please read the article instead if you really want to know. The names skeletonType and summonPortal are arbitrary. You can change them to anything you like (as long as you change the rest of the script to match). OBJECTREFERENCE skeleton1 OBJECTREFERENCE skeleton2 OBJECTREFERENCE skeleton3 OBJECTREFERENCE portal1 OBJECTREFERENCE portal2 OBJECTREFERENCE portal3 These are variables, also explained in my article. Again, the names are arbitrary. EVENT onEffectStart(ACTOR akTarget, ACTOR akCaster) skeleton1 = game.getPlayer().placeAtMe(skeletonType) portal1 = skeleton1.placeAtMe(summonPortal) skeleton2 = game.getPlayer().placeAtMe(skeletonType) portal2 = skeleton2.placeAtMe(summonPortal) skeleton3 = game.getPlayer().placeAtMe(skeletonType) portal3 = skeleton3.placeAtMe(summonPortal) I haven't gotten to events and functions yet. :sweat: Anyway, when the game sends your script an event, it also sends along some parameters relating to the event. In the case of OnEffectStart, it sends along the actor that gets hit by the effect, and the actor that cast the effect (which could both be the same actor). Again, the names akTarget and akCaster are arbitrary. When you use the PlaceAtMe() function, it returns a reference to the object that was just spawned. So it's assigning the references to the skeletons as skeleton1, skeleton2, and skeleton3. The skeletons are also spawning some portals after they get spawned, and assigning the references to portal1, portal2, and portal3. utility.wait(5) portal1.disableNoWait() portal2.disableNoWait() portal3.disableNoWait() endEVENT This part is telling the script to wait for 5 seconds (if you've experience in programming, you can think of 'utility' as a library) and then disable the portals so that they're no longer visible. EVENT onEffectFinish(ACTOR akTarget, ACTOR akCaster) skeleton1.disableNoWait() skeleton2.disableNoWait() skeleton3.disableNoWait() endEVENT This is the event that happens when the magic effect is finished, whether it's been dispelled or because the duration ran out. So, to do what you want, you need to create a property pointing to your rune (let's call it Rune01). Then you replace all mentions of 'Game.GetPlayer()' with 'Rune01' in the script. After that, you just need to put this in a spell that is 'fire and forget' and targets 'self'. When you put this effect into a spell, you can adjust the duration to whatever you want. After that, you just set the player to cast the spell at him/herself when she triggers your trap. Don't worry, using the Cast() function does not actually show any casting going on, so the player doesn't even notice if he/she cast the spell. Edited March 19, 2012 by fg109 Link to comment Share on other sites More sharing options...
elseagoat Posted March 19, 2012 Author Share Posted March 19, 2012 THANKS! This is so helpful. Ok, so here is what I have so far. UndeathRune-this is the spell. In the effects list, it has... RuneUndeathFFLocation-which is the magic effect. Rundown of the checkboxes and dropdowns here: Script, Fire and Forget, Target Location. Checkboxes are: Hostile, FX Persist, Snap to Navmesh (I figure this will keep me from putting the rune somewhere where the skeletons would spawn and get "trapped" in the environment?) No Recast, No Death Dispel. It also references the following projectile... RuneUndeathProjectile-which is the projectile I made for it that closely resembles existing rune projectiles. The reason I made this is because I saw that the projectile, when set to "Lobber" gave me a checkbox for explosion that allows for a proximity trigger. Now, I assume this proximity trigger has nothing to do with the magic effect itself, and only triggers the explosion object, but I couldn't be sure so I left it in there and made a dummy explosion that basically does nothing. I was considering attaching an enchant effect to the explosion that creates a "haunt" magic effect that spawns a skeleton to chase the target that was haunted, thus more targets caught in the blast = more skeletons, but then I thought I would be running in circles because that meant I needed a second magic effect and that just seemed redundant. But it also seems more interesting, so I may try that later, because variable numbers of skeletons depending on the number of enemies that walk into the trap seems cool. I also made a skeleton actor. I checked the box to make it leveled, thinking that this will make it so the spell will "scale" so that as you level up, so do the skeletons spawned by the spell, keeping it useful at all levels. Also check the "summonable" checkbox, but I do not know if this will mean anything since I am spawning them with a script. So the script I have so far, which I know probably wont work yet is this: Scriptname runeofundeathScript extends ActiveMagicEffect ACTORBASE PROPERTY RuneOfUndeathSkeleton AUTO ACTIVATOR PROPERTY SummonTargetFXActivator AUTO PROJECTILE PROPERTY RuneUndeathProjectile AUTO OBJECTREFERENCE skeleton1 OBJECTREFERENCE skeleton2 OBJECTREFERENCE skeleton3 OBJECTREFERENCE portal1 OBJECTREFERENCE portal2 OBJECTREFERENCE portal3 OBJECTREFERENCE rune1 EVENT onEffectStart(ACTOR akTarget, ACTOR akCaster) skeleton1 = rune1.placeAtMe(RuneOfUndeathSkeleton) portal1 = skeleton1.placeAtMe(SummonTargetFXActivator) skeleton2 = rune1.placeAtMe(RuneOfUndeathSkeleton) portal2 = skeleton2.placeAtMe(SummonTargetFXActivator) skeleton3 = rune1.placeAtMe(RuneOfUndeathSkeleton) portal3 = skeleton3.placeAtMe(SummonTargetFXActivator) utility.wait(5) portal1.disableNoWait() portal2.disableNoWait() portal3.disableNoWait() utility.wait(15) skeleton1.disableNoWait() skeleton2.disableNoWait() skeleton3.disableNoWait() endEVENT I am assuming that when you say:The names skeletonType and summonPortal are arbitrary. You can change them to anything you like (as long as you change the rest of the script to match).That means that I change the name to an object as it appears in the object window? Otherwise I do not understand how to define exactly what NPC I want to summon and what activator to spawn as a "portal." For the variables, I think I just understand that you name them to whatever you want. The only type of coding or scripting I have ever done was expression2 back in Garry's Mod, and if variables are anything in Papyrus like they were in that, then I am on the right track. So, to do what you want, you need to create a property pointing to your rune (let's call it Rune01). Then you replace all mentions of 'Game.GetPlayer()' with 'Rune01' in the script. When you say this, I am unclear as to how to define the property pointing to the rune. What exactly is the rune here? Is it an activator or something? I am having a hard time grasping what exactly makes a rune spell work like a proximity mine instead of a normal projectile based destruction spell like a firebolt. I am just guessing here, going to put the projectile as the property. But I still don't see how the game understands when I say "rune1" that I am talking about "RuneUndeathProjectile/" I am reading your thing now. Really appreciate the help man. Link to comment Share on other sites More sharing options...
fg109 Posted March 19, 2012 Share Posted March 19, 2012 (edited) OK, after reading your description, I realize that what I told you is not really going to work. I think I got caught up with those script fragments you posted and forgot what you wanted in the first place. So you want to be able to shoot a spell at an area, and have multiple skeletons show up. OK, that's good. I think what you need is actually an object reference script, not a spell script. Because I don't think spell scripts run on anything other than actors. You know how to link your spell to an explosion? Well Explosion objects have a field called "Placed Object", which means they spawn this object when they explode. Now this would be it if you wanted a single skeleton, but you wanted multiple. So you should choose to spawn an activator. This activator is going to be your rune. The activator's script is pretty simple: Scriptname MyRuneScript extends ObjectReference ACTORBASE PROPERTY skeletonType AUTO OBJECTREFERENCE skeleton1 OBJECTREFERENCE skeleton2 OBJECTREFERENCE skeleton3 EVENT OnLoad() skeleton1 = game.getPlayer().placeAtMe(skeletonType) skeleton2 = game.getPlayer().placeAtMe(skeletonType) skeleton3 = game.getPlayer().placeAtMe(skeletonType) RegisterForSingleUpdate(30) endEVENT EVENT OnUpdate() skeleton1.disableNoWait() skeleton2.disableNoWait() skeleton3.disableNoWait() skeleton1.Delete() skeleton2.Delete() skeleton3.Delete() Disable() Delete() endEVENT It's an adaption of the script you posted before. As soon as the your rune (the activator) shows up, it will spawn 3 skeletons. Then in 30 seconds, both the rune and the skeletons will be disabled and deleted. To change the duration, you just need to change the RegisterForSingleUpdate() to some other number. EDIT: And that script you posted up actually looks pretty good, except for some reason I thought it was going to be set off as a trap. So since rune1 is supposed to be dynamic and not at a fixed location, it's not going to work for what you want. Edited March 19, 2012 by fg109 Link to comment Share on other sites More sharing options...
elseagoat Posted March 19, 2012 Author Share Posted March 19, 2012 (edited) That's perfect! Well, I do have questions though. Specifically about what you said with magic effect scripts only working on actors. Why is that? This is the most confusing part to me, why I can't simply script a magic effect to do whatever I want and instead have to go through a different type of object to get that result. Originally, I was thinking of using: MagicEffect Function GetBaseObject() To define the magic effect as the location for PlaceAtMe for the skeletons to spawn. But obviously you may have a much better grasp on how this works so maybe you could explain why it wouldn't work to do that? Also, I am still confused about how the rune spell works in general. If I use the method you describe, then I need to basically have no magic effect whatsoever. Let me explain what I mean. I have the spell. The spell uses the magic effect I made. The magic effect uses the projectile I made. The projectile uses the explosion I made. The explosion creates the activator I made. The activator runs the script. But then at the magic effect "stage" of this series of objects, I am forced to give it an effect archetype. Should I just set the archetype to script and attach no script to the magic effect? Edited March 19, 2012 by elseagoat Link to comment Share on other sites More sharing options...
fg109 Posted March 19, 2012 Share Posted March 19, 2012 (edited) Well, I do have questions though. Specifically about what you said with magic effect scripts only working on actors. Why is that? This is the most confusing part to me, why I can't simply script a magic effect to do whatever I want and instead have to go through a different type of object to get that result. If you notice, the OnEffectStart and OnEffectFinish have actors as parameters. So logically that means only actors can run magic effect scripts. I have no idea why that the devs designed it that way. But I've tried casting spells at lots of different objects, and they don't run the magic effect scripts. Originally, I was thinking of using: MagicEffect Function GetBaseObject() To define the magic effect as the location for PlaceAtMe for the skeletons to spawn. But obviously you may have a much better grasp on how this works so maybe you could explain why it wouldn't work to do that? Well I'm pretty sure I don't understand what you mean. You can't actually get a location off of a magic effect, because it doesn't exactly manifest itself in the game world. Also, I am still confused about how the rune spell works in general. If I use the method you describe, then I need to basically have no magic effect whatsoever. Let me explain what I mean. I'm sure there are many ways to create rune spells, I just thought this way worked best for what you were describing. For example, you could have instead used Hazards. You can think of them as trap spells. They cast a spell on you if you touch them. Just think, you could create a huge hazard and make it so that a skeleton pops out every time someone tries to walk through it. I have the spell. The spell uses the magic effect I made. The magic effect uses the projectile I made. The projectile uses the explosion I made. The explosion creates the activator I made. The activator runs the script. But then at the magic effect "stage" of this series of objects, I am forced to give it an effect archetype. Should I just set the archetype to script and attach no script to the magic effect? Well, yes if you wanted it to have no extras. It you want to add a magic effect to it, that's fine too. It just depends on whether or not you're satisfied with just summoning skeletons. Edited March 19, 2012 by fg109 Link to comment Share on other sites More sharing options...
DangerManzo Posted March 19, 2012 Share Posted March 19, 2012 I was in the same place as you a few weeks ago, but I feel miles ahead of where I started. So I feel like I can offer some advice from the point of view of someone just starting.The CK wiki seems unorganized at first, and yes, there isn't a very good tutorial on everything, just a basic "hello" script. But the search is great and most of the functions and commands are explained very well.The best way I found to get started is to start with simple things. You can have a larger idea in mind, but think of the individual steps and do each one by one. For example, start with just summoning a skeleton, then go to activating a rune, ect. It only took me a few more days until everything started to "click" and I'm certainly no expert but I can read a script and get an idea of what it is going to do. The Bethesda developers did a pretty good job of adding comments to their scripts that explain what is happening so they are a great resource to learn from once you understand the basics. Link to comment Share on other sites More sharing options...
elseagoat Posted March 19, 2012 Author Share Posted March 19, 2012 (edited) Thanks guys. Just one more question though, in the example script you posted, the second modified one, it appears the skeletons will spawn at the players location when the trap is triggered. How can I make them spawn at the location of the trap instead? Should I use: ACTIVATOR PROPERTY spawningactivator AUTO where spawningactivator is the Activator object that I created with the script, created when the explosion happens, and then skeleton1 = spawningactivator.placeAtMe(skeletonType) so on and so forth? If this does work, would it create problems if I were to cast the spell, then once it is triggered, recast it again before the 30 seconds is up? My previous attempt was not successful. The spell did nothing at all when I cast it, no rune was created, so I am trying again by simply making a fire rune that is identical, only that the explosion creates the activator as well that spawns the skeletons, just so I can get the script working before I start messing with the art and values of the spell. EDIT: I think I am overthinking this. Perhaps simply using skeleton1=placeAtMe(skeletonType) will use the objects location, since the object is what the script is attached to? Will try that. Edited March 19, 2012 by elseagoat Link to comment Share on other sites More sharing options...
fg109 Posted March 19, 2012 Share Posted March 19, 2012 Yes, you have it right at the end. I forgot to take out the game.getplayer() while I was copy-pasting. :wallbash: Link to comment Share on other sites More sharing options...
elseagoat Posted March 19, 2012 Author Share Posted March 19, 2012 Yes, you have it right at the end. I forgot to take out the game.getplayer() while I was copy-pasting. :wallbash: Starting to get it! Thank you so much for the help. Link to comment Share on other sites More sharing options...
Recommended Posts