Jump to content

Cipscis

Premium Member
  • Posts

    1103
  • Joined

  • Last visited

Everything posted by Cipscis

  1. Hey cdagger! That's a pretty neat looking script - it will cause a door to open and close automatically for the player, but I can see also how it would be broken for NPCs. First off, just something that's unrelated to your problem, the best function to use for opening or closing a door is SetOpenState. While using Activate will normally work as well, SetOpenState was actually written for this purpose, and can be used to open/close doors that are in different cells as well. Activate is the correct choice when working with teleporting doors, though. A couple of things in this script could be further optimised as well: - First - the "else" statement and "return[/url] command in the OnActivate block don't actually do anything, so it would be best to remove them. The same applies for " == 1" in the condition used here. Because GetLocked will only ever return true of false (1 or 0), it is faster to simply check if its return value is true, as opposed to checking if "its return value is equal to 1" is true. - Second, the last two "if" statements in the GameMode block contain the same code, so they can be combined by using the logical OR ("") and AND (" && ") to combine their conditions: if ( GetOpenState == 3 && GetDistance player < 200 ) || ( GetOpenState == 1 && GetDistance player > 200 ) Now, on to your issue. There are two things preventing this script from working properly for NPCs, each in a different way: - The most obvious one is that the GetDistance conditions "hard code" the "player" reference. If you want to change this, it is possible for the door to open for any nearby Actor that fits certain criteria, but it is much more complicated to do this. - The less obvious one is the OnActivate block. When a reference with a script containing an OnActivate block attached to it is activated by the appropriate Actor (which is not specified in this case, so any Actor counts), the OnActivate block will run instead of the reference's normal activation occurring. To fix this, the Activate function should be called at some point in the OnActivate block (providing that you want the regular activation to occur at some point, at least). This is the current relevant code: Begin OnActivate if GetLocked Activate endif EndAs you can see, the door's regular activation will only occur if it is activated while GetLocked returns true. If you want NPCs to be able to manually activate the door, then you could either remove the entire OnActivate block, or you could replace "Begin OnActivate" with "Begin OnActivate player" so that the OnActivate block only runs when the player activates the door. If you want the door to open automatically for any nearby Actor of a certain criteria, then you'd have to do something a little different. One way in which you could do it would be to use FOSE's looping and cell walking functions, but it's also possible to do this without the use of FOSE. Unfortunately I don't have time to finish this post at the moment, but I'll come back later and explain the method that I would use to make it work for any NPC in a few hours. Cipscis
  2. Alright, I've been through a couple more scripts now. Here's what I have to say. ==================== This Quest Script essentially uses "pseudo-stages". Another method would be to use SetStage/GetStage and store this information in the Quest's stage. Of course, this approach is only possible for quest scripts. For other scripts, this sort of structure could be used:scn CALFPeffectSCR ref Target short ActorValue1 short ActorValue2 short ActorValue3 short DamageValue Begin ScriptEffectStart set Target to GetSelf [b]; places initial explosion when hit, just in case there's no specified explosion effect in the weapons projectile ; also useful if you'd want a melee weapon or fist to cause explosions on hit[/b] Target.placeatme {ExplosionType} Player.PushActorAway Target 104 [b]; this is the part of the script in which you can specify a certain damage formula ; the formula can be added to the base damage amount set in the weapons tab ; -or you can set the damage in the tab to zero and fully rely on the formula to calculate damage ; it can be the target NPC/creature's actor value or the player's ; it's also fine not to make a certain damage formula[/b] set ActorValue1 to Player.GetAv {ActorValue1} set ActorValue2 to Player.GetAv {ActorValue2} set ActorValue3 to Player.GetAv {ActorValue3} set Damage to -1 * ( {formula incorporating actor values} ) Target.ModAv Health DamageValue [b]; make the effect play a certain sound-you could also use the sound of the explosion and not specify a specific sound[/b] Playsound3D Play a certain sound End Begin ScriptEffectUpdate [b]; this is the part where you place a smoke effect on every axis the NPC/Creature is located ; it makes as if the smoke is always coming out of the target while the effect is taking place[/b] Target.placeatme {smokeeffect} [b]; alternatively add a smoke actor effect on the character[/b] Target.CastImmediateOnSelf {smoke actor effect} End begin ScriptEffectFinish [b];if you used the above alternative, this segment removes the smok eeffect from the character[/b] Player.RemoveSpell {smoke actor effect} end This script looks fairly lengthy when full of comments like it is, but once they are removed there are less than 25 lines of code so don't let yourself be daunted when looking at it. If you find that it looks like a bit of a wall of text, it can help a lot to look at it with syntax highlighting. I personally use a custom-built syntax highlighter that I wrote for use with Notepad++. There's a link in my signature if anybody is interested in using it. I plan on releasing updates alongside every new version of FOSE. Now, let's look at this script... The first thing that I notice here is that explicit reference syntax has been used where it is not required. For reference scripts (e.g. Effect Scripts), implied reference syntax can and should be used when calling a reference function on the reference which the script is running on. In other words, this code is unnecessary: set Target to GetSelf Target.placeatme {ExplosionType}Instead, this code should be used: set Target to GetSelf PlaceAtMe {ExplosionType} The only variables that are actually required in this script are the "ref" variable called "Target" and the "short" variable called "DamageValue" (line 29 should use "DamageValue", not "Damage". Because GetActorValue can be called multiple times within a calculation, "ActorValue1", "ActorValue2" and "ActorValue3" aren't required at all. In fact, the calculation section of the script is also completely optional and usually won't be included. The same applies for the PlaySound3D line - it will not usually be used. PlaceAtMe should not be used in a situation like this, as it will lead to savegame bloat. Instead, CastImmediateOnSelf and RemoveSpell should be used. One important limitation of this script is that it will only work as intended when the weapon is used by the player. This is because the "player" reference is "hard coded" into the call to PushActorAway. It is relatively easy to overcome this, although making it so that multiple instances of the weapon can exist is much more complicated. I'm currently working on a method by which multiple instances of the same weapon can exist and the Attacker's RefID can be returned in the weapon's Effect Script, but it's not completed yet. To improve this so that any Actor can use the weapon, you'll first need to create what I like to call a VR Quest. A VR Quest is a Quest that has a Quest Script consisting only of variable declarations. These variables can be accessed remotely from any other script with the following format: QuestID.VariableNameThe only limitation worth mentioning with this method of accessing variables remotely is that remote "ref" variables cannot be used to call reference functions. Instead, a locally declared "ref" variable should be substituted for the remote one: ref rRefVar set rRefVar to QuestID.rRefVarName rRefVar.ReferenceFunction ===================== Once again, if anything I've said doesn't quite seem right, or doesn't make sense, just let me know. Cipscis
  3. I've had a look at the first three scripts posted in this thread (EDIT - Those that aren't on the GECK Wiki), and it seems to me that none of them will work as intended. I've worked through them and changed them so that they should work properly, and optimised them a little bit too. Please don't take offence at anything I say about your script, nothing I say is meant to offend anyone and my last intention is to personally attack any of you. If anything I've said is incorrect, please let me know. I have a tendency to ramble as well so please let me know as well if there's anything I've said that doesn't make sense and I'll try to explain it a little better. ===================================== Unless the level cap has already been raised (i.e. by increasing the "iMaxCharacterLevel" GMST), the "player.getlevel >= 21" condition will always return false, so the code will never run. You should also avoid using Global variables. They are unnecessary and messy, and Quest variables can be used in their place to make a data file much more organised without losing any functionality whatsoever. To access a variable remotely, i.e. in a script other than the one in which it was declared, you must use the syntax "FormID.VariableName". The FormID can be either in the form of a "ref" variable, or an EditorID. Because the Form which the script is attached to must be specified, only variables declared Quest or Object scripts can be accessed remotely - you cannot remotely access variables declared in Effect scripts. Because Quests don't have references, the Base Object should be specified when trying to remotely access a variable declared in a Quest script. However, with Object scripts, specifying the Base Object won't work, as each reference of the Base Object has its own instance of the script, each with its own variable declarations. Because of this, the RefID must be specified. This can be done either by using the EditorRefID, or by storing the RefID in a "ref" variable. In the script that you posted above, you've declared your variables in the wrong place. Variable declarations should be situated right after the "ScriptName" declaration, before the first block. Of the four variables that you have declared, only one is required. The only reason why a variable is required at all is because the return value of a function has to be stored in a variable before it can be used as a parameter for a function. Your calculations should be changed so that, instead of "hard-coding" the values used, the relevant GMSTs are used. This ensures that the calculations will still work correctly if the relevant GMSTs have been changed by another Mod. Unfortunately, in order to make the calculation completely compatible, FOSE's GetBaseHealth function is required. This is the code that I would use instead: ScriptName ResetLevel short sLevel Begin MenuMode 1027 ; "LevelUp" Menu if player.GetLevel == GetGameSetting iMaxCharacterLevel set sLevel to GetGameSetting iMaxCharacterLevel - 1 player.SetLevel sLevel ; determine and modify HP set sLevel to GetGameSetting fAVDHealthEnduranceMult * ( player.GetActorValue Endurance - GetGameSetting fAVDHealthEnduranceOffset ) + GetGameSetting fAVDHealthLevelMult * sLevel + player.GetBaseHealth player.SetActorValue health sLevel endif End Please ignore that I've separated that calculation onto two lines. I did this so that it wouldn't produce a horizontal scrollbar in the codebox, the calculation should be on a single line. ===================================== This script can be optimised a hell of a lot - most of the "if" logic is either broken or unnecessary, and the timer should be done using GetSecondsPassed, not GetCurrentTime. Because only part of the script has been posted, I can't optimise it fully as something I remove might be required earlier or later in the script, so I will maintain the basic function (basically that of a simple timer) and just optimise the logic and calculations. if ( Det < 0 ) if ( Det == 100 ) set attackWarning to 3 elseif ( Det <= 80 && Det > 100 ) set attackWarning to 2 elseif ( Det <= 60 && Det > 80 ) set attackWarning to 1 elseif ( Det <= 40 && Det > 60 ) set attackWarning to 0 elseif ( Det <= 20 && Det > 40 ) set attackWarning to -1 elseif ( Det <= 0 && Det > 20 ) set attackWarning to -2 endif endifWhen creating logical "if/elseif" structures like this, it is important to always keep in mind which conditions must be true, in addition to the condition that is currently being checked. It seems to me that you've been mixing up your ">" and "<" signs, as all of these conditions are paradoxical - if the conditions necessary for them to be checked are true, then they must evaluate to false. When using "if/elseif" statements like this one, it is usually possible to replace them with a single calculation. In this case, given that "Det" is an integer, this is possible. When division of a number results in greater precision than the type of variable that it is being stored in, the number is truncated. Because of this, your "if/elseif" structure can be combined with the previous line of code, and placed into a single line: if ( attackActive == 1 ) set attackStartTime to getCurrentTime ; 0.0166 = 1 minute / 0.02 = 1.2 minutes if ( attackWarning == 3 ) set attackWarnTime to attackStartTime elseif ( attackWarning == 2 ) set attackWarnTime to attackStartTime + 0.02 elseif ( attackWarning == 1 ) set attackWarnTime to attackStartTime + 0.04 elseif ( attackWarning == 0 ) set attackWarnTime to attackStartTime + 0.06 elseif ( attackWarning == -1 ) set attackWarnTime to attackStartTime + 0.08 elseif ( attackWarning == -2 ) set attackWarnTime to attackStartTime + 0.1 endif endifThe first thing I'm going to mention here is the condition. Given that it seems here that "attackActive" can only ever take on one of two values, 1 and 0, you only need to check if it is true as opposed to checking specifically if it is equal to 1. As you can see, this "if/elseif" structure is very similar to the previous one, in that it looks like it could be replaced with a single calculation. In this case, the calculation is: if ( attackActive == 1 ) if ( attackMsgShown != 1 ) if ( attackWarnTime == getCurrentTime ) if ( attackWarning < -3 ) ShowMessage MercMsgBaseAttack2 else ShowMessage MercMsgBaseAttack1 endif set attackMsgShown to 1; Only want to show message once per attack. endif endif endifOnce again, have a look at the condition. It is identical to the condition of the previous "if" statement! This can be a useful tool when the condition needs to be re-evaulated, but in this example this is not that case, so this code should be included in the same "if" statement as the previous section of code. If "attackMsgShown" is only ever set to one of two values, 0 or 1, then the first "if" statement's condition should check if "attackMsgShown" is equal to 0, instead of checking that it is not equal to one. Because variables are initialised automatically to 0, this condition will return true if the variable has been declared but not set. Because "attackWarnTime" and the return value of GetCurrentTime both have "float" precision, using " == " is highly inadvisable, as it will almost never return true. Instead, " >= " should be used. Now that the timer has been restructured to use GetSecondsPassed instead of GetCurrentTime, this block of code needs to be restructured to use GetSecondsPassed: elseif attackActive == 2 if attackMsgShown == 0 if attackWarnTime > 0 set attackWarnTime to attackWarnTime - GetSecondsPassed * TimeScale elseif attackWarning < 0 ShowMessage MercMsgBaseAttack2 set attackMsgShown to 1 else ShowMessage MercMsgBaseAttack1 set attackMsgShown to 1 endif endif ; Calculate warning time from detection modifier set attackWarning to ( Det + DetBoost ) / 20 - 5 ; Set the clock if attackActive == 1 set attackActive to 2 set attackWarnTime to 72 * attackWarning elseif attackActive == 2 ; Send the warning message to the player if attackMsgShown == 0 if attackWarnTime > 0 set attackWarnTime to attackWarnTime - GetSecondsPassed - TimeScale elseif attackWarning < -3 ShowMessage MercMsgBaseAttack2 set attackMsgShown to 1 else ShowMessage MercMsgBaseAttack1 set attackMsgShown to 1 endif endif elseif attackMsgShown set attackMsgShown to 0 endif ===================================== Once again, if any of what I've just said was incorrect or didn't make sense please let me know. Cipscis
  4. There isn't really any point in making an extra reference when one of the references that is already present can be used as an Enable Parent, although you're absolutely correct that Enable Parenting should be used. Cipscis
  5. In a situation like this, you'll want to use Enable Parenting so that you can Enable/Disable many references at once by calling Enable/Disable on a single parent reference. This would enable you to reduce the code to this: ScriptName AAAScrubShowerSingleValvefinal float Timer Begin OnActivate player.CastImmediateOnSelf AAAShowerEffect ; AAAShowerEffect is an actor effect with Restore Rads Effect set to duration 15, magnitude 20 set Timer to 15 End Begin GameMode if Timer > 0 if Timer == 15 ; The timer has just started DisablePlayerControls 1 0 0 0 0 0 0 Shower1.Enable 0 endif set Timer to Timer - GetSecondsPassed elseif Timer <= 1 EnablePlayerControls 1 0 0 0 0 0 0 0 Shower1.Disable endif End Cipscis
  6. I've had a look at the page illyism created on the GECK Wiki, and I've optimised some of the scripts (check the GECK Wiki page linked to by illyism above for the current versions). - For the "KarmaEffect" script, I've changed it so that the Karma modifier will be applied to whatever Actor equips the scripted item instead of modifying the player's Karma whenever any Actor equips or unequips the item. I've also noted that, for Amours, a scripted Object Effect should be used instead of an Object script. This will ensure that the effect won't break if the item is equipped via EquipItem - For the automatic door closing script, I've changed it to use GetOpenState and SetOpenState so that it will always work correctly. - For the "CALFPeffectSCR" script, I've changed it to use implied reference syntax instead of "ref" variables - basically removed unnecessary code. I haven't looked at it enough to try to optimise it in any other way though. - For the "LightSwitchScript" script, I've changed it to use implied reference syntax, just like with "CALFPeffectSCR", and I've also gotten rid of the "toggle" variable and used GetDisabled instead. I only just found this thread, so I haven't read it through it yet, but when I get the chance I'd like to have a look at the scripts that haven't been included on the Wiki page and see if they can't be shortened or optimised as well. Cipscis
  7. By "cheats" I assume you're referring to console commands? You can use either AddSPECIALPoints or ModActorValue to do this. For example - "player.ModActorValue Perception 5" will increase the player's Perception by 5 points, up to the currently defined maximum (10 by default). Cipscis
×
×
  • Create New...