Utilizador:Wrye/Breeze Bash

A UESPWiki – Sua fonte de The Elder Scrolls desde 1995

Recent Experiments

Stats Morphing

A couple of times after doing stats morphing, I went to the stats menu and found them quite off. After a bit of checking, it seemed that it was only the display that was incorrect. The actual values (use console, click on player and do a "getav statName" seemed to do the right thing. Also, internal checks on my monitor code seemed fine. :shrug: Some sort of timing issue -- maybe related to that "You can't load while paused" message that I keep seeing when shape changing.

The other issue with stats morphing, is that the game difficulty scales with the PC level. Now does this happen continuosly, when an area is loaded, when the PC levels up? If one form is level 10 and another is level 40, will the difficulty levels toggle when forms toggle? If not, the player is going to have to cycle forms, making sure they remain at similar levels... unless automatic class-based levelling is applied in the background... dunno... seems rather complicated to me.
Alternately, perhaps class-based buffs could be applied for a form instead. So the overall level stays the same, but in certain forms, the PC may have buffs to strength, intelligence, etc. These could be stored as Abilities (that can be edited in OBSE) and placed on the NPC cloned forms (I imagine they would be saved in the save game). OR the cloned NPC can be of the chosen class and autolevel to the player level, increases in stats between the PC and NPC are applied to the player, decreases can either be ignored or applied as well. OR examine the major attributes and skills of a class (think OBSE can do this now, haven't tried) and devise some sort of buff/penalty based on these values... All of these could lead to over powered characters though. --Breeze 04:52, 1 November 2007 (EDT)
It works two ways: 1) Creatures and npcs that are leveled relative to the npc will be releveled when you shape shift. 2) However, spawned creatures, npcs and items won't respawn (at the appropriate level) until after the cell they're in resets. (The default reset time is 3 days in which the cell has not been visited.
So, depending on your mod setup, you can manipulate the world somewhat by shifting between characters with different levels. E.g., you could walk into a cave as a low level character (spawning enemies at a low level), shift to a high level character, then whoop up on them -- but if you do this, you get lower level loot. I think that this isn't so much of a problem. Of course there's also a danger of shifting from a high level character to a low level character and suddenly finding yourself badly outclassed. There is something of a gameplay plus in this -- e.g., it's bad idea to shift into a low level form when out in the wilderness.
However, you're right that it risks being uber. I was thinking of just capping the doppel ability (e.g., you can't dopplegang someone who's higher level than you are), but using pcoffset might be better. This will probably be a story dependent feature though. E.g., some stories might allow doppelganging high level npcs. Anyway, the stats shifting is not required -- it's controllable by a flag argument that can turn it on/off when shape shifting. The variations on stat tweaks can be handled by manipulating the cloneForm stats before making the request to store the character. --Wrye 14:38, 1 November 2007 (EDT)

Class Morphing

With appropriate changes to monitor and to bosh.py, I was able to morph the players class. However, this doesn't work with custom classes. Problems with custom classes:

  • I don't fully understand how custom classes are stored. We understand some of it, but not all of it. I could probably figure it out given time, but there's another problem...
  • There's no easy way to store a custom class in the savefile. There might be some non-easy ways to do it, but I won't have time to check it out for a while.

So for now the solution is to not use (in-game) custom classes.

  • There are complaints that default classes are poorly defined. That's resolvable by using a mod which adds new classes. There are some mods out like this already. And this may be something for Cobl. And of course, it's easy to create one's own custom classes in TESCS. (Mod custom classes are fine -- it's only in-game custom classes that are stored oddly.)
  • Users who are already using a custom class can do a "showClassMenu" to switch to a pre-defined class before doing the morph. Thus (with my mod), one can morph to a character, then use the class menu, then morph to a different character (which will store the current character along with changes to his/her class).

Faction Morphing

I've given a little thought to automatically shifting factions when changing forms. (E.g., if you shape change to an bandit, you automatically become a bandit, and thus immune from bandit attacks.) This is difficult to do safely -- many factions are used for obscure signals. I was thinking that this should only be done for playable factions -- but then edit a lot more factions to make them playable. However, major snag is that the factions of cloned npcs are NOT saved in the savefile. There are probably workarounds, but I won't have time to pursue them for a while, and may not be fully satisfactory.

Faction editing would be useful, but like you said I believe that many factions are used for other purposes... I'm also guilty of using factions to store animation IDs and have used it in the past to act as variables attached to NPCs, for example. This was one of the original reasons I was only going to allow **specific** NPCs to be impersonated/doppelganged, since I can then control in the quest the faction membership and remove it as needed. --Breeze 04:37, 1 November 2007 (EDT)

File Format Oddness

Formids used in cloned objects are handled differently than for objects created in game. I haven't researched this thoroughly, but I'm a little concerned as to whether they'll shift correctly if your mod list changes. This would affect for instance the class morphing. I'll have to do some experiments.

Technical

Some technical suggestions for obse <<>> monitor development.

Bash Integration

The brzReqest <<>> bosh monitor is designed in a way that potentially it could be more generally useful. Because of speed issues, I don't think that one would ever want to use it frequently, but some additional infrequent uses might come up.

I would like to integrate the monitor into wrye bash at some point -- that should make it easier to autolaunch (like bash allows autolaunching of OBSE). Also, it makes sense to keep all bash code together. (But there will be a possibility to have an alternate command line monitor.) Full integration into the GUI will probably take a little while, but integration into bosh code would be easy, but something simpler would be possible now.

Heartbeat

One simple request addition would be support for an "echo" request which the monitor would just echo back with a completed -- this would be a good simple test to see if the monitor is running (e.g., useful to do when the system is starting up).

Good idea. I was going to include some code to scan the executing applications and determine if the monitor script was running... The idea was then to start the monitor from within Oblivion. I was going to use WinExec, but for some reason, Oblivion does not like it very much and does some strange things. So ATM it's a manual start up process, unless you do the launching thing from the GUI --Breeze 04:16, 1 November 2007 (EDT)

Command Suggestions

It would be cleaner if the plugin commands were all in the same namespace. E.g. brzFileExists, brzFileDelete, etc.

Easy enough to sort out --Breeze 04:13, 1 November 2007 (EDT)

The fileExists and FileDelete commands should be limited so that they only work in the saves directory. (As a security measure -- don't want people writing mods that allow deletion of arbitrary files in the users file system!) You might want to rename them accordingly:

  • brzSaveFileExists fileName
  • brzSaveFileDelete fileName
ATM fileExists, fileRename and fileDelete have 3 versions, determined by the number after the string, 0, 1 or 2. 0 means absolute (the path you type is the one used), 1 is relative to the Oblivion installation folder and 2 is relative to the save game profile. It probably makes sense to only allow renames/deletes in the save folder. The fileExists probably makes sense to allow any file to be detected --Breeze 04:13, 1 November 2007 (EDT)
Personally, I'd be paranoid and disallow 0 for fileExists as well. Also, I presume that you're disallowing tricks like "..\..\Program Files". --Wrye 15:07, 1 November 2007 (EDT)

If the system is used more generally, then it's possible that two different mods might try to use the system simultaneously. That can be resolved by associating an id with each request. Also, there might be a problem where a request might be started by one mod during a section of gameplay that will be chopped off (e.g., the period between quicksaving and quickloading a shapeshift request). That can be resolved by having a final clearance state. Specifically, I would suggest that the commands be like this:

Also, it might be useful if it were possible to write multiple times to the same request before posting it.

brzRequestNew
* Example: set rid to brzRequestNew
* Returns a new request id or an error code.
  * rid > 0: new id (success use the new id in other commands)
  * rid == 0: try again later (system is busy with another request)
  * rid < 0: system failure (monitor is not running, etc.)

brzRequestPrint
* Writes string (with arguments like current postRequest) to request buffer
* Example: brzRequestPrint rid string [arguments]

brzRequestPost
* Posts request. If string/arguments are present, then will first add them to request buffer.
* Example: brzRequestPost rid ["string" [arguments]]

brzRequestState
* Example: set rstate to brzRequestState rid
* Returns current state of request
  * -1 no such request (or has been cleared)
  *  0 Initialized
  *  1 has had one or more prints
  *  2 Posted, pending response
  *  3 Posted, failed
  *  4 Posted, completed

brzRequestClear
* Example: brzRequestClear rid
* Clears request, allows new requests to be made. Once a request is in progress, 
  then no new requests will be allowed (brzRequestNew will return 0) until the current 
  request has been cleared.

Shape shifting Example:
* Beginning:
  * set rid to brzRequestNew
  * if rid < 0 print error message and give up
  * if rid == 0 wait and try again
  * if rid > 0 continue
* Post shapeshift request
  * brzRequestPost rid "ripAppearance current.ess quicksave.ess 0x%x 0x%x" copyNpc tempRef
* Wait for success/failure
  * set rstate to brzRequestState rid
  * if rstate == 2 (wait some more)
  * if rstate == 4 (quickload)
  * else (handle failure)
* Recover from quickload
  * brzRequestClear rid
The above function calls seem reasonable, the requests can then be named in the format <rid>.request, <rid>.completed, <rid>.echo, etc. for easy identification. The signalling should probably also take place in a sub-folder of the save folder used to prevent cluttering of the save folder. It also makes it easier to perform a "flush" of the signal folder. Ideally I would like to associate a signalling folder with a save game to make game copying/sharing possible and to allow different save games to have different signalling going on. --Breeze 04:13, 1 November 2007 (EDT)
<rid>.completed syntax makes sense (though I thought that the compiler would only allow that if the leading variable was a ref variable?)
Re subfolders and state -- I think that's not necessary. The signalling is always ephemeral -- i.e. there's not state info in it (instead any state info is stored in the savegame itself). Since it's ephemeral, you don't really need a subfolder, nor savegame specific signals. (BTW, the current version of my monitor does flush all signal files on startup.) --Wrye 15:07, 1 November 2007 (EDT)
Re: naming. I see that you've talked about including the shape shifting and plugins code into Cobl. If this is the case (and it's no problem with me), then perhaps the naming of the plugin functions should reflect the normal naming scheme as well. Something like cobFileExists, etc. then will rename the plugin to something like CoblUtils.dll to be more appropriate. Form/quests follow normal naming with cobShp* as the project prefix... BTW when I continue work on the BodyShapes mod, it can fall under this naming convention as well and be made compatible with the shape shifting code too. --41.206.160.2 03:55, 2 November 2007 (EDT)
I think that "brz" is best. Using "cob" would mean that the code and distribution are part of the cobl distribution and is controlled by the Cobl team, which your plugin isn't. Besides, you gotta stick your name on the thing! (Note that I tend to keep a clear line between stuff which is mine and stuff which is Cobl. If I invent something cool, I keep it in my namespace unless it needs to go into Cobl. :) --Wrye 16:49, 2 November 2007 (EDT)

For writing the request, I would suggest

  • Don't open the file for writing until you get the brzPostRequest. If there are brzPrintRequest's before that, save their text in a buffer.
  • When writing, write to a temp file and then rename the file to the actual file name. This should avoid some file read/write collissions. (I'm still trying to figure out file locking in python, so an atomic rename operation is safer.)
Will do. At one stage I did experience some strange behaviour with this kind of thing. --Breeze 04:13, 1 November 2007 (EDT)

Have changed the commands and they are similar to what you proposed, but not identical. The main difference is that only one request at a time is allowed, regardless of how many mods want to make requests. Not doing so could cause issues with request state dependant scripts interfering with one another. Will probably add lock and unlock functionality for scripts that behave like this (e.g. the shape shifting) which will not allow other scripts to post requests until this one is completed.

Additionally have added a heartbeat call, called brzPingPong which returns true if the Monitor is playing ping pong with it, otherwise false. Will be used in a quest to monitor the monitor so to speak.

brzPingPong
* returns true if neither a .ping or .pong file exists. Creates a .ping file in this case.
* returns true if a .pong file exists. Renames it as a .ping file.
* returns false otherwise.
* assumes that the Monitor renames the .ping file as .pong as soon as detected.

Breeze 06:39, 6 December 2007 (EST)

Sounds good. I've just updated Wrye Bash so that the included bashmon supports pingpong as described. Keep in mind that the monitor sleeps for 0.25 sec, between file checks. So it will take at least that long for it to respond to a ping. --Wrye 14:40, 6 December 2007 (EST)

Player Identification

Yeah, the ShapeIDs in the original SS quest were reserved for that kind of usage (special quests/conversations, etc. and talking to an NPC, then going back and asking them something while you're wearing a different appearance and they tell you things like "who the hell are you? get away from me..."). --Breeze

Hmm.. I think you're right -- they're necessary. One of the difficulties is that you need to get the same id when you doppelgang someone for the second time as you did the first. I'm thinking that you should give them a token which stores that info. (I need to see how you were doing your impersonation code in your mod.) --Wrye 04:24, 29 October 2007 (EDT)
The ID for a player chosen shape is necessary, since the refs are all the same and don't change on a shape shift and you need to distinguish between appearance 1 (in slot 1) and a later appearance 20 (also in slot 1). Impersonating/doppelganging an NPC does not require an ID since you store the ref of the NPC being impersonated. So your code for doppelganging will be something like "if SSQst.NPCRef == somePersistantNPCref then ...", while the new appearance will be in the form "if SSQst.CurrentID == someNumber then ...". The assinging of -1 for the current ID in the case of doppelganging or the assigning of NULL to the ref in the case of a new appearance will probably be sufficient. --Breeze 04:23, 1 November 2007 (EDT)
Well even for doppelganging, you might need it. E.g., if you doppelgang a spawned npc, I think that eventually the original goes away, at which point the ref that you had stored might become set to 0. Also, it terms of testing code, its simplest if there's always a unique id available. (E.g., you have a conversation with npc x -- he may not care who you're doppelganging, rather just what your id is. --Wrye 15:26, 1 November 2007 (EDT)

Standarization

I'm wondering if we shouldn't simply package the SS code into an ESM (with Book of Shadows and Seducer specific stuff being toggled with a variable) to prevent version clashes/naming issues etc. later. So quests made for Identity X, work correctly regardless of whether your Book of Shadows mod is used or the Seducer race mod and that other mods can access the quest variables?? Of conversely, instead of toggle variables, we could set watcher quests in our mods to set settings or add animation, sfx, etc.? Anyway, something to consider for extension later. --Breeze

Yeah, I've thought about that, and have some rough plans. Cobl (forum, Projects) is really the place to put that sort of thing. (With some intensive work, it is finally starting to take off.) The pieces that should go in are:
  • Common variables: current id ref (if present), current id number, etc.
  • Code to actually do the transformation. As you say, this should be independent of the story of the transformation.
  • Some sort of archiving system (pages or a book, or something else) that can store the details of additional identities. (True + three alternates is fine for a menu, but presumably player might want twenty or much more identities.) I'm thinking that these could be installed to/from the slots. Or something else could be done.
--Wrye 04:24, 29 October 2007 (EDT)
I have some script implemented data structures, such as a vector, that can be used for this kind of thing. Seriously though, who really needs 5+ identities? Perhaps I should think of creating resizable vectors in OBSE? e.g. "set NPCRefsID to createRefVector 3", "setVectoredRef NPCRefsID 0 MyNPC", "set tempRef to getVectoredRef NPCRefsID 0". Of course saving is an issue, but this could be resolved by allocating dummy objects and using their space in the save game to store data, e.g. the stats/skills of an NPC could store a fair amount of data. What do you think?? --41.206.160.2 04:28, 2 November 2007 (EDT)
Number of identities: Well, you might have an identity for each major faction (Mages, Fighters, Thieves, Dark Brotherhood, KOTN, MQ, Sheo, Arena, ...) then you might have additional ids for special use (Amazonian to infiltrate Amazonian tribes, Marauder, etc.) So, yeah, it could pile up!
Vectors would be difficult to implment, and I think that objects would be more flexible. If you're storing in objects, then you can have any number of objects (so you as the modder, you don't have to worry ahead of time about how many identities to allow). Also, different stories might want to store different types of information -- e.g. a story that does stats/faction morphing has to store more info than one that just does appearance. Or one story might want to recognize something like "experience in this form" variable, while others don't. So, I think that it's best to leave this info storage up to the story mods. --Wrye 16:41, 2 November 2007 (EDT)

Misc. Notes

  • Don't forget to handle spaces in profile directory names.
Got that sorted out. --Breeze 04:31, 1 November 2007 (EDT)