Tuesday, August 28, 2012

Portal 2 - Thaumaturgy Post Mortem Part 2

I forgot some things.

Hiding player-facing walls:


Okay, my original idea had the cube sliding back and forth in its room. When a weighted cube was lifted off the button, the circuits would move away and when placed, the circuits would move back. I had a little physbox on the back of each circuit that, when passing through a trigger, would hide and disable their respective circuits. However, the trigger wouldn't constantly fire it's outputs like it did in Here a Second ago. Instead, it'd fire once and not work until I disabled it, moved the circuits away, and re-enabled it. I still haven't figured out why that's the case.

I considered making the physboxes cover the entire surface of the circuit backings, but decided against it for some...reason... *shrugs*

Anyways, in the current build, if the weighted cube is not properly placed into its button, the wall hiding trigger will be enabled prematurely and some walls will not disappear making it hard to either a) complete the circuit or b) get into the interior. The trigger has a half-second delay so if you unpress the button too soon, it won't enable, but that's the best I've come up with so far.

Sliding:


As mentioned above, the cube initially moved back and forth. But as my control model moved more toward a realtime control, it became harder to make sure the cube would be flush against the wall. Since I could have arbitrary angles, the cube would clip into the walls, not something I wanted.

With my main method of getting into the cube gone, I thought about using linked_portal_doors, portalable surfaces, and light bridges as alternatives. As usual, the linked_portal_doors had a bug. In this case, once I stepped through into the cube, everything on the other side would disappear except world geometry and shadows. Portalable surfaces weren't very appealing as I'd have to cut more holes into the walls, thus making the solution either impossible or too easy. So, I settled on a light bridge.

Portal 2 - Thaumaturgy Post Mortem



So many things just didn't work in this map...oh where to begin.

Cube Rotation:

First, I wanted the circuit cube to move to the same position as the cube only when placed in the button. This led to a set of 20 triggers, a func_tank, 6 info_targets, and several momentary_rot_buttons. It was promising until I couldn't get it to match exactly due to how func_tanks don't have a continuous motion when going past -90 (Up). They spin in place and this threw the momentary_rot_button that controlled the face spin off.

The next iteration was a bunch of momentary_rot_buttons for each axis parented to each other. First run didn't work out. I needed a way to dynamically set the parents to prevent orientation distortion. I put a func_rotating in, but they aren't reliable for exact rotations. Even when set to 90deg/sec and spun for 1 second, they slowly become more and more inaccurate because the "stop" command doesn't happen exactly 1 second later.

So, I decided to try scripting it all. After spending 4 hours trying to get a basic "hello world" working (you would not believe how little there is for vscripting tutorials. It's all "here's the reference and what I've done, have at it!"), I managed to get a button to execute a function (Using the console is unreliable. It seems to work more often when you actually have parameters to pass. Not having parameters results in errors because Source doesn't want to search for your PerfectlyCapitalizedFunction() and searches instead for PerfectlycapitalizedFunction() thus throwing an "It doesn't exist!" error).

Anyways, I setup a basic "AddDegreesToX(x)" function for each axis and put the script on a single func_brush. Once the brush was flipped on any two axes (180), adding or subtracting past 90 on the third axis would result in jittering. This jittering made no sense either. Why? My debug output was telling me -90 - 1 = -89 AND -89 -1 = -90. Same went for 90 + 1 = 89 and 89 + 1 = 90. Source refused to do proper math.

This led me to believe I had to do 3d programming in Squirrel. Let's just say that didn't go well. All the code I found was immediately broken because my initial angles were zero and 0 * cos(angle) - 0 * sin(angle) =, you guessed it, zero. The tutorials I found were all for manipulating the actual vertices of a cube with each face represented in matrices, not rotating a 3d model in a game engine. I never took 3d programming and, frankly, my head was spinning at that point.

However, that got me thinking I should use multiple rotators and just parent the circuit cube to the one I needed at the time. Well...it kind of worked. It appears logic_measure_movement copies and pastes angles, so every time I changed parents, the circuit cube was given an entirely different set of angles instead of just adding to its own.

I took a break from all that programming to go with a more visceral method of manipulating the cube: Walking into a projector that shows arrows that, when pressed, rotate the cube in that direction. The code was already there, I just had to build the button framework and implement player view/collision separation. Piece of cake, right? Well, I ran into that little issue of "when you rotate enough on two axes, you flip the third". This effectively reversed the function of the buttons on that third axis, not good.

So, I tried again, but instead of keeping the buttons stationary, I made them move with the cube's angles through another logic_measure_movement. It worked to a point, but after enough rotations, the problem came up again. Solution? Use the "rotating axes" idea to parent the buttons only to the axes they didn't rotate. So, X would rotate along YZ, Y along XZ, and Z along XY. While it looked kind of cool, it only made the problem occur sooner instead of not at all. Also, I was getting motion sickness from being a ghost inside a rotating cube. I abandoned the button idea at this point.

Next, I tried using a logic_measure_movement on a weighted cube, but I couldn't separate the translational data from the desired angular data. So, I went back to scripting and made a small function that copied the angles of a SourceCube and applied them to a TargetCube; no translational data transferred. That worked perfectly, until I changed the TargetCube from the model to a brush. Model-to-model worked fine, but model-to-brush rotated the angles by 90 on the Z (or is it Y? Whichever is up in Source) axis.

There was also this annoying bug where instead of setting the degrees exactly, 359.1 degrees would become -.9. so everything was a little off. I resolved this by going back to a model-to-model...uh..model, using a logic_measure_movement on the TargetCube and having it influence the CircuitCube. And that worked perfectly, again. Each rotation of the SourceCube was piped through the entities and correctly applied to the CircuitCube. So, the pipeline was SourceCube - > script -> TargetCube -> logic_measure_movement -> CircuitCube. Awesome.

The circuits:

I had circuits from my first "Circuits" map. They were alright, but I didn't like the fact I had the triggers outside of the instance and didn't really have a way to track the "flow" without explicitly setting one up. For a 3D cube where you can set many paths, this wasn't going to cut it. So, I dove in and rearranged the internals of the instances and added triggers and movelinears to function as flow control. If a trigger is hit, its corresponding movelinear moves up to hit the next trigger in sequence. This was doubled up so the circuits could be bidirectional if desired.

The new circuits worked like a charm except when it came to arranging them into a cube.

First, there was an annoying bug back when I had the cube parented to a momentary_rot_button. Each circuit's backing was a func_brush that was then parented to the center of the cube, a momentary_rot_button. It turns out "using" a func_brush parented to a momentary_rot_button will cause the button to rotate partially with respect to the axis selection. I turned on Developer 3 and ent_messages_draw 1 just to see where the erroneous i/o was coming from and there was nothing. No links whatsoever. The bug even persisted when both the brush and the button were set to ignore +use! This was eventually solved when I settled on making the CircuitCube's center a func_brush driven by the scripted movement measure system.
*Note: Parenting the brush to a physbox parented to the button did not produce the error at all*

Next, it turns out moving triggers will trigger with no apparent provocation when moving and they'll stay triggered until they hit a nice 90 degree angle (and sometimes, not even then). Needless to say, this was a big problem as it meant if you spun the cube right, you could glitch your way through the map. Sadly, this wasn't fixed. Filters don't even combat this. Sure, I disable them before motion, but if you enable them and they aren't at an angle they like, they'll fire their outputs.

I still didn't change the method of circuit rotation. I have a nice script to handle it and, since they only rotate on a single axis, it *should* work. Since I haven't released the map, I may still try it so you get a better experience.

And then I forgot to turn off the Afterburner OSD and had to record the run-through video twice.

Saturday, August 25, 2012

Getting -hijack working

While working on my latest Portal 2 map, I noticed I chose the wrong compile settings (Expert -> default) versus what I normally choose (basic -> no vrad). Both have extra command line parameters, but have different resolutions and it was the resolution that tipped me off (I usually hit F9 immediately followed by Enter and don't see the compile window). This wasn't a big issue since the map is relatively small and vrad doesn't take that long (5 seconds or so). However, when I went for a recompile, I changed back to the basic compile window and found -hijack working again. Subsequent recompiles using either compile window worked fine as well.

The annoyance since DLC 1


For those who have been following this blog or my YouTube videos, you'll know that I've been having problems getting -hijack working after Valve released Portal 2 dlc. So, I was ecstatic for a bit...then I closed the game and in the next instance -hijack stopped working. This prompted me to look at the options I had to see if they matched up.








The expert window had "+sv_lan 1" in it, but that was really it. That change didn't have an effect on whether -hijack worked, so I decided to look at how the game started up by making the compile window wait for keypress before closing.

For the working -hijack (expert compile), this is what it showed:

"g:\valve\steam\steamapps\common\portal 2\portal2.exe" -hijack -game "g:\valve\steam\steamapps\common\portal 2\portal2" +map "temp" +sv_lan 1 -novid -sw -w 1152 -h 864


The broken -hijack compile showed this:

g:\valve\steam\steam.exe -applaunch 620 -game "g:\valve\steam\steamapps\common\portal 2\portal2" -hijack -novid -sw -w 1440 -h 900 +map "temp"


So, a basic compile runs the game through Steam instead of the Portal2 executable directly. After seeing this, I opened up Process Explorer to see what each runline turned into.

Working:

"g:\valve\steam\steamapps\common\portal 2\portal2.exe" -hijack -game "g:\valve\steam\steamapps\common\portal 2\portal2" +map "temp" +sv_lan 1 -novid -sw -w 1152 -h 864 


Not Working:

"G:\Valve\Steam\steamapps\common\Portal 2\portal2.exe" -game portal2 -steam  -game "g:\valve\steam\steamapps\common\portal 2\portal2" -hijack -novid -sw -w 1440 -h 900 +map temp  -novid
(I don't know why -novid has two spaces infront of it instead of one.)


Running the game with or without the -steam parameter didn't change anything. So I tried messing with the order, specifically where -hijack was placed. No change. Using either runline didn't make a difference. They both worked perfectly. This lead me to believe it was the initial start that made the difference. Maybe Steam wasn't passing -hijack through and was continually blocking it as part of some "is the game running" check that hasn't taken -hijack into account.

And...that was it (perhaps not the speculation bit). If Portal 2 is started via Steam, -hijack doesn't work. Starting portal2.exe directly eliminates this problem. Huh.

I don't know if this applies to other Source games as I currently only develop for Portal 2, so if you're having this same problem, try it. Stick your parameters into an expert compile and use it instead of the basic compile window...or batch it all.

I tried batch compiling Test Map Pack 3 or 4 a few times...vrad threw a fit and botched the lighting in all of the maps. I had to execute one after the other in separate commands by hand. Placing line after line in a batch file was not to vrad's liking.

Saturday, August 18, 2012

Restoring The Witcher 2 backup saves

I finished The Witcher 2 on Iorveth's path today. So, I decided to restore the Chapter 1 big decision save I made so I could go through Roche's. After backing up and clearing my Iorveth saves, I extracted the big decision save and started the game. And...the Steam Cloud restored my saves and The Witcher 2 completely ignored my save.

So, I deleted all the saves again, restored the one I wanted, and turned off the cloud. Still nothing. So, I tried restoring every single save I ever made. This resulted in the game botching their playdates and presenting them out of order with some missing their thumbnails. Quit out, delete, and restore the single save with cloud storage enabled. My save was ignored, but the cloud saves were present. This prompted me to delete each save through the game's interface to delete them from the cloud.

This made sense in theory, but only resulted in an undeletable thumbnail due to its corresponding savegame being deleted without it (I have no idea how Steam could miss deleting the thumbnail). This meant that I couldn't reduce the cloud storage for The Witcher 2 to 0 bytes for some forum post's magic condition that makes the game read from the My Documents savegame folder again.

This was followed by another 45 minutes of screwing around with changing CloudStorage=false in User.ini, enabling/disabling cloud storage both ingame and out, and trying to force the cloud to load my manual save by getting the file sizes and names, tweaking times, and generating Sha-1 hashes for my save and its thumbnail. I even cleared the gamedata registry value to see if that would help. No dice.

I came across a post about the patch that broke savegames due to compression and suggested that version mismatch between the game and the save was the problem. So, I verified The Witcher 2's game cache integrity. About 35% through, I decided it was taking too long and cancelled the operation. I tried to start The Witcher 2 again, but I guess the verification purged all the registry entries and Steam tried to do the first time setup again. This meant I had to manually set the registry values again since even after running the first time setup, steam will fail to set the "I'm done" registry entries for each step except DirectX. For reference, I've copied the posts I used to fix this here:

--------------------
Quote:
Originally Posted by Who Cares View Post
Another solution involves a registry hack.

open regedit and go to (on 32 bit XP that is):

HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam\Apps\20920

Should show:
default REG_SZ (value not set)
DirectX Reg_DWORD 0x00000001 (1)

Need to add
dotNetFX40 Reg_DWORD 0x00000001 (1)
vcredist Reg_DWORD 0x00000001 (1)

Adding a value:
right click, select new, select DWORD
fill in the name of the value, double click and fill in 1
For 64-bit win7 it's:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Valve\Stea m\Apps\20920--------------------

Once those keys were set, I put copies of my save in both the My Documents savegame folder AND the steam 20920 userdata savegame folder, turned on cloud saving, and started the game. IT WORKED!

I think something about verifying the game cache purging registry keys fixed it. To make certain it wasn't a fluke, I loaded the game, make a quicksave, quit the game, restarted it, and both saves were there. I still have that annoying thumbnail sitting there, so I need to screw with its filesize and try to force the Steam Cloud "FILE MISMATCH!" prompt to appear.

----------------------

I'm debugging a Portal 2 map idea right now. I am starting to loathe linked_portal_door. It can't move, flickers, and crashes (The ent, not the game) if a func_brush passes through it. This is crippling my idea something fierce. The initial idea was great and I managed to reduce its implementation complexity by substituting existing entities in place of reinventing the wheel, but I'm hitting that "Source can't do that" wall again.

Thursday, August 16, 2012

What is Steam doing?

Lately, Steam has been acting up for me. It refuses to launch games until the 3rd or 4th attempt claiming it is "unavailable", spontaneously starts and finishes 20-400MB downloads (as in, it doesn't tell me it is downloading until it is done), starts integrity checks for games or tools only when I start an unrelated game, and still persists with pausing all downloads with .1MB left until I start the game and it goes "J/K! I'm done!".

The most recent example (about 2 minutes ago):
I started up The Witcher 2 and suddenly a 28MB Portal 2 Authoring Tools update, 12MB Source SDK update, and 77MB GarrysMod update completed instantly and simultaneously with Portal 2 labelled "Update paused" while TF2 performed an integrity check after sending up the "You should quit all games before verifying integrity" message box and chime before promptly closing it and doing the check anyways. What?

There's probably file compression going on for the downloads as I know for a fact I don't have a 5.1MB/s download speed (and even then updates wouldn't start and complete instantly).

I also have a couple outstanding issues with Steam with only one being confirmed by another user. First, if you close the main steam window it'll fade away and close, BUT clicking on the region it occupied makes it magically fade in and out and absorb the click (so whatever you meant to click on doesn't have focus). Second, closing the friends window right before putting my machine to sleep makes it popup again when my machine wakes most of the time (it used to be all the time).

I'm also not a fan of screenshot uploads failing because Steam is synchronizing ("Some error occurred").

Wednesday, August 15, 2012

Trends of the people

I released the Vigilance Greatsword on the Skyrim Workshop because I didn't think keeping it to myself was right. Not exactly in those terms, though. It was more like something I felt I should do because, well, why not? If I kept it to myself, it'd be something I'd hold onto for a bit and no one would be the wiser. While that has a certain appeal, I released the sword anyways.

Upon release, I had several requests to do other swords and figured it would be a good idea since I had tried for so long to get into 3d Modeling and forgetting all I had learned would be both frustrating and a waste. So, off I went to create more weapons. Sure, I haven't grabbed a huge audience, but I smile every time someone thanks me.

Every subsequent release garnered more requests to do weapons. Sometimes I'd do them (either because I was going to do them anyway/already doing them or I liked the weapon), other times, I wouldn't. Now, though, I've been watching the comments shift to "make armor X". Everywhere I turn it's an armor request.

I just have an aversion to making armor. If you've read or even used my leather duster mod for Skyrim, you'd know what I'm talking about. Random vertices not showing, polys "wobbling" when bones move (makes it hard to gauge where problem spots are ingame), the mesh not showing at all, etc. It's some setting I didn't check/uncheck upon export, a default setting that screws everything up, some obscure work around I'd have never guessed, or I haven't done the same thing a dozen times for it to magically work yet (mostly this).

Before Moving Bones 
After Moving Bones. Note the size difference, both legs have the same weights.
Setting the skin weights is easy, if tedious. I just hate all the little things that go wrong whose only solution has been to try the same exact thing over and over until it works and, well, that's almost always been the case. Meshes just magically work for no apparent reason. The only thing that's changed is my frustration level (is that some sort of requirement for making armor?).

Now, the solution should be simple then, make the mesh and have someone else rig it! Eehhhh, I really like to do stuff on my own. I also figure that the more I know how to do, the more likely I'll a) do it and b) be able to legitimately claim it in a job interview. As Jim Rivers, the Hiring Manager at Obsidian Entertainment, said, (some paraphrasing here) "Don't put anything on your resume that you aren't comfortable with because it is my job to put your skills to the test and make you break. And I'm good at my job."

Wednesday, August 8, 2012

Youtube Monetization

A while ago, I decided to try monetizing my videos to see what kind of money I'd make. Conclusion: Enough to possibly buy a hotdog and soda after 14 years. Why am I writing about this now? Well, I got an email saying my Cube Freezing tutorial for Portal 2 needed more information in order to be monetized.

When I started monetizing my videos, YouTube had a text box you'd have to fill in to explain why they should monetize your video. A few months ago, they removed that box. While I was glad not to have to keep typing out the same spiel over and over again, the looseness concerned me. And now, I've set off their alarms or was randomly chosen to prove myself worthy.

In either case, I looked at how much money I "made" since I started and came to the conclusion of "Not worth it." So, I logged into YouTube to uncheck the monetization checkbox and was greeted by a "Changes cannot be saved" error. This also prevented me from changing anything else about the video even when I rechecked the box. I had to go to the viewing page of the video and click on the description to change anything.

So, in an attempt to remove the uncheck the monetization checkbox, I filled out the form YouTube sent me for "Proving I have ownership" and basically said "It was a fun experiment, but as you can clearly see, not worth it." This probably means my account has a strike against it now, so, joy.

For whomever it was that blew up when I started monetizing and said "You're getting greedy man! I'm not going to pay for this! What's next? Paying to read the titles of your maps?! What is wrong with you!?" Look, no money gain. Just like I said would most likely be the case.