Jump to content

Fallout New Vegas GECK & Scripting Help 101


Recommended Posts

Posted

 

What sort of "output" do you need from all this? I mean once the weapon is selected and the mods applied by the user, what's the end result?

 

 

Edit: I mean are you changing the effect.. changing out the item to change the model.. all the above? I'm just wondering what kind of info you actually need, there is probably an easier way to dynamically build it without all the conditionals.

 

 

The output would be the new modded weapon. So say if I choose to add a grip, then I should get a new weapon variant with a grip, be it a 10in, 15in or 20in version. The previous weapon would then be removed. But basically it is just a model change, though not like swapping the models for an object, each model has its own object within FNV, or to state that better, each model itself is a weapon. 

 

EDIT: The ideas with maps seems pretty good, I don't know about the performance issues or anything, I can imagine that using too many ifs, will make things slower because of the constant checking, and if I remember correctly, they say FNV's compiler isn't all that smart and checks all the conditions in an if even if the first one is false and its being used with an AND, or so I was told. Though my main issue is with repeat code, and having to fix something ten times if there's an error, I don't like to repeat code, not unless its necessary. 

 

Oh and thanks for the map idea DoctaSax, it sounds like a pretty nice idea. I've only ever used maps like once before, so I didn't really consider them.

Guest tomm434
Posted

A.J. I think ritualclarity is talking about Rape Key  :D  :D 

Posted

 

What sort of "output" do you need from all this? I mean once the weapon is selected and the mods applied by the user, what's the end result?

 

 

Edit: I mean are you changing the effect.. changing out the item to change the model.. all the above? I'm just wondering what kind of info you actually need, there is probably an easier way to dynamically build it without all the conditionals.

 

The output would be the new modded weapon. So say if I choose to add a grip, then I should get a new weapon variant with a grip, be it a 10in, 15in or 20in version. The previous weapon would then be removed. But basically it is just a model change, though not like swapping the models for an object, each model has its own object within FNV, or to state that better, each model itself is a weapon.

 

So all those different combinations of weapons+mods already exist in the mod, and you just want to come up with the formid so you can do an "additem"? This is where the mega if/elseif/elseif came from?

 

I would probably construct a stringmap for that and just combine the info and look them up. A list like..

smWeapons["assaultrifle_none_none"] = <refid 1>
smWeapons["assaultrifle_somescope_suppressor"] = <refid 2>
smWeapons["assaultrifle_otherscope_suppressor"] = <refid 3>
smWeapons["assaultrifle_none_suppressor"] = <refid 4>
; etc
You could go multidimensional with it instead of course. Prettier but more work to ocnstruct since you need to ar_construct many times.

smWeapons["assaultrifle"]["none"]["none"] = <refid 1>
smWeapons["assaultrifle"]["somescope"]["suppressor"] = <refid 2>
smWeapons["assaultrifle"]["otherscope"]["suppressor"] = <refid 3>
smWeapons["assaultrifle"]["none"]["suppressor"] = <refid 4>
Something like that. Then you just look up the ID based off what's currently selected.
Posted

I'd do it like this, you can use the local ref index of a base form as the key in a map, paired with its attached mods. You just need to buildref it back again after. Here's the gist:

array_var Weapons, entry
ref NewWeapon
 
; init
let Weapons := Ar_Construct "map"
let Weapons[(GetLocalRefIndex MyWeaponForm1)] := Ar_List "Long barrel", "Suppressor", "Tactical Scope"
let Weapons[(GetLocalRefIndex MyWeaponForm2)] := Ar_List "Short barrel", "Bayonet", "Laser Sight"
...
 
; When we want to find the right weapon form
let Desired_Mods := Ar_List "Long barrel", "Laser Sight"
foreach entry <- Weapons
    if entry["value"] matches Desired_Mods ; use Ar_Find or whatever
        let NewWeapon := BuildRef (GetModIndex "My-Mod-Name.esp"), entry["key"]
        PlayerREF.AddItem NewWeapon, 1
        break
Posted

That's not too bad, but the foreach gets pathological when the desired combination of stuff is towards the end of the array. A direct lookup is really the 'power' of a map/stringmap -- avoiding the foreach entirely.

Posted

Direct look up suffers the same problem when stuff is at the end of the array, skipping the foreach in the script does not avoid that, its intrinsic to map data structures.

 

Since string matching is slow, to optimize you could replace them with numbers, which you could typedef for readability.

 

In any case, I question that performance difference is significant here, especially given players won't change their mods very often.

Posted

I still think a regular map is best there. First you need to be able to tell what can be done with the selected weapon, so having to check it against strings will lead to a bunch of elseifs too. If things are organized according to number ranges, you can make the appearance of menu buttons ("remove silencer", "add grip") directly dependent on the map key value in the menu button's conditions, and have standard responses to each button.

 

if iButton == somevalue

  ; that button only appears if it's a silencer, and means "remove silencer"

  ; let iIndex -= 100

  ; let newweapon := map[iIndex]

endif

;done

 

Posted

Direct look up suffers the same problem when stuff is at the end of the array, skipping the foreach in the script does not avoid that, its intrinsic to map data structures.

Huh?

 

The implementation of the maps is done with a hashtable. It's no slower to look up any particular item in a map/stringmap than any other; it's always O(log(n)) with a little overhead for the hash function. The foreach is always O(n), with overhead for a case insensitive string compare.

 

Since string matching is slow, to optimize you could replace them with numbers, which you could typedef for readability.

There's no string comparison being performed in a map/stringmap lookup.

 

In any case, I question that performance difference is significant here, especially given players won't change their mods very often.

Probably not, but it's a "bad habit" to get into. You should only foreach() when you actually need to visit every item in the list to perform some operation on/with it -- or when you must store it as an array, because visiting all the entries is how you normally use it and the lookup is an exception.

Posted

These all look like good ideas, thanks. I will take a look at them all. I don't know about this game's engine but I do know that too much if/elseif can make a processor work slower due to constant checking of conditions. I don't know if that is the case here, I don't think so, maybe if someone over did it and a person wasn't using a strong CPU, it might slow down. I'm not sure how the whole engine thing works to be honest, I know the processor has to carry out the instructions eventually, but if a person pushes then engine too much it can cause issues regardless of how powerful their CPU is. Well, that's what I understood anyway. 

 

Thanks for the help. 

Posted

If you are worried some of your code may be causing a performance problem, one way to profile it is to put it in a loop and run it redundantly 10,000+ extra times, every frame and see if it stutters- in my experience it usually doesn't.

 

It doesn't prove you're innocent, since I don't think anyone truly understands the engine and you aren't testing every situation, but it gives some perspective- I think a lot of people massively overestimate the overhead of scripts (obviously one should still write efficiently as good practise).

 

Some functions are slow, but most are not. If too many if clauses become unreliable I question its directly performance related.

Posted

Well that statement wasn't so much about performance as it was about trying to understand how things work with the engine. As far as I'm aware, code is translated to machine language or whatever so that the CPU can execute those instructions. What I don't understand is why the engine would screw up in a case which the CPU would be perfectly capable of executing whatever it is that screws with the engine. 

 

I'm not really sure if this matters that much or not, to be honest, but I still like to know, even if I don't really make anything that would have to consider a person's CPU, well at least I don't think Fallout's language is really suited for that anyway. 

Posted

 

No definitive figures. For example, used to be Sexout Brutal Rapers main script begining was a very long elseif with multiple conditions on each test.

After realizing it was selecting the wrong condition a lot of the time, it was reversed to be a series of independent ifs, with only one condition per line.

It's possible the cure was overkill but it did stop the issue.

 

 

Mmmh... I guess I should plan that script a lil better then. Thanks for the tip!

Posted

Scripts are not compiled to machine language but to another intermediary language, which is basically a series of expression like :

 

result := function Parameter Parameter Parameter ...

 

where every parameter could be the result of another expression. In that sense, all the operators ( + - * / ), even the flow control operators like if and goto are handled as function call.

 

This means the process is highly recursive and computers never liked "recursivity" (are those actual English words ? :) )

 

 

Guest tomm434
Posted

jaam, maybe it's a bug!

 

When you add "PrintC"  to menumode in NPC' objects script (npc is NOT set as low level)

begin menumode
if GETGAMELoaded||GEtGameRestarted
let iIndex := GetModIndex "Lunette.esm"                                            
PrintC "Lunette v.%.0f.0%.0f loaded  at index (hex): %x2 or (dec): %g" Lunette322ModVersion  Lunette322ModVersionUpdate  iIndex iIndex
endif 
end

game CTDs on the main menu.

 

Game doesn't CTD if the same code ran by Quest script.

Game doesn't CTD if I delete "PrintC" line from object script.

Posted

I know there is a long standing issue with using PrintC in the Start part of the dialogs. Usually, unless there is a syntax error, those kind of CTD comes from running the code in another thread than the main thread.

 

Posted

Some functions are slow, but most are not. If too many if clauses become unreliable I question its directly performance related.

My experience is that there's no performance penalty at all for them (or the vast majority of things), they just "stop working." It's been so long that I can't remember if the script just stopped, or started getting the checks wrong, or what. I just know having an assload of them in a single long if/endif is "bad." It's easy enough to rewrite any "if/elseif/endif" as a series of "if/endif", it's just slower for it to run. If it starts getting beyond a page or so long now, I just split it in half.

 

As long as you aren't changing the value you're comparing, it works fine.

 

No problem on the unordered list thing. Hashtables were invented to address those (and other) concerns directly. ;)

 

Taking some lessons from them for things like this can make the code faster as well, which is why I like using a map/stringmap for this, with a calculated (rather than static) key. The map/stringmap bit isn't as important a distinction, the map will be somewhat faster. Dynamically creating the key to either one is the important part.

 

Then it's just two steps to get whatever you want: Generate the key, and look it up in the map.

Posted

Hey all... This isn't an adult-oriented mod but the tutorials on here have helped me start scripting and I was hoping someone might be able to point me in the right direction on something I'm stuck on. Ya'll seem to know more than most communities I can find :P

 

I'm making a melee-combat perks mod, and want a line of perks that add a dodge/evasion chance. But I can't figure out how to make an attack "miss" the perk owner. There must is there something like that or am I going to have to do something like adding +100 DR for the triggering attack?

Guest tomm434
Posted

Maybe you can set player as ghost for a moment.

You're using Events right? If so that can be the solution - make player ghost just before the hit and then after the hit in 0.3 secs or something make him not ghost again.(You can use short gamemode quest I guess)

Posted

Well the engine itself has no concept of dodging/evading attacks of any kind. That's left up to the player, since the game is as much FPS as it is RPG. Messing around with DR and DT is the "easy" way to go, but note that you can't reduce damage to zero that way. DR is applied first and is capped at 85% no matter what you set it to via equipment/perks/etc. DT is then applied which is simply a subtraction, but it also has an effective maximum value. It won't lower damage taken below 20% of the post-DR value.

 

So being it for 10 points with DR and DT both 100..

 

1. DR attempts to reduce it from 10 to 0, but the cap at 85 only reduces it to 1.5.

2. DT tries to reduce it to -98.5 but this is below 20% of 1.5 (0.3) so it's reduced to 0.3.

 

However, that is still a very high reduction ... 100 to 0.3. I think using the stats this way 'as intended' would be fine..?

Guest tomm434
Posted

Yea, I think that is not a bad way to go. Even 85% damage reduction - you rolled out of the direct hit and just made it a grazing blow basically. Thanks!

 

BOORING

 

:D

Posted

You can use the DT to make it a lot less than 85%. 85% is what you get with a DR of 85 and a DT of 0. I don't know what the upper bound on DT is, if any, but the overall lower limit is 20% of 15% == 3%.

Posted

I use that approach in AKH and it works quite well, although I only had NVSE 2 when I came up with it. I tried various other things like SetEssential and resurrect but they sometimes cause engine issues when used on the player. I don't think I tried ghost.

 

Some attacks ignore DR/DT, but it works most the time and is safe.

 

I believe OnHit events run just before the damage is applied and may be worth investigating, you could perhaps heal the player the damage they took a frame later without them noticing.. unless they died.

Posted

Is there something 'odd' about GetSelf and local vars (in particular) in a dialog begin/end script?

 

This works fine:

set somequest.someref to getself
This doesn't.

ref self
set self to 0
set self to getself
Seems to always be 0/null. The initial set to 0 is there to avoid the actor/quest var stomping issue with local vars in result scripts.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

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