Chat Room “Design”

I just posted this in the private Beta forums, but I figure folks not-yet-in-the-beta might have some thoughts.

In preparation for opening the beta, I’m about to do chat rooms in the lobby. I’m going to do the simplest thing possible at first, and hope it gets us through the surge. Here is the current “design”, let me know if you think it’s okay. Please don’t turn this into a giant thread of how the ultimate lobby should work given infinite time and resources, we can have that discussion later when time and resources are infinite. For now, I just want to make sure I’m not missing something stupid and obvious in this thing I plan to spend one (1) day on.

  • When you log in, you will be presented with a list of available rooms, including the room name, and how many people are in it. You will have to pick one. I don’t know what I’ll do if you let it sit on that screen, probably time you out and kick you after a reasonable period, or auto-join you to the default room.
  • There will be a default main chat room called This is the Main Lobby Default Chat Room or something like that. It can’t be changed, and won’t disappear even if nobody is in it.
  • /say will broadcast to everybody in the current room. Being in a room is basically like being in the lobby right now.
  • You will only be able to play people in the current room, because that’s the only time you see a name to click on.
  • /whisper will still complete nicks of everybody on the server, unless that becomes a performance problem.
  • I will probably add a /who command to list everybody (with regex) and what room they’re in. This way ZeroTKA won’t have to constantly /whisper to see who’s in the lobby.
  • /joinroom will complete with the room name, or you can back out to the room selection screen. When you go out to the room chooser you’re still considered in the current room until you choose a new one.
  • /makeroom will create a new room with the name you specify (yikes) and put you in it. If people are jerks I’ll have to add a way to report naughtiness. For now, it’ll just say “mail support at spyparty.com with problems” or something. There probably won’t be a visual UI for creating a room, only the chat cmd, which will act as a bit of a weeder.
  • Rooms are deleted when the last person leaves them. Room names cannot be changed once created. There are no owners of rooms, no hidden rooms, no moderators, etc.
  • I’m hoping people make rooms with descriptive names, like, “Newbs Get Mentoring Here” and “Players with Less than 20 Wins”, and the like. I’m sure that’s a total game designer fantasy, and they’re going to end up being things like “Your Grandma’s Bedroom Closet”, sadly.
  • There is going to be no limit on the number of people in a room at the start. We’ll see how bad that is.

I will also try to color code chat lines in a way that’s not terrible. 

Thoughts? Clear, simple, parsimonious thoughts?

Chris

Scalability and Asserts I’m Not Yet Fixing

I’ve been a bit quiet because I’ve been working on (hopefully) completely invisible stuff involving backend server scalability.  What does that mean, you ask?  In practical player-facing terms, it means I’m trying to get the lobby and registration system robust enough to first invite the rest of the waiting list in,1 and then to open the beta up completely, allowing people to hear about the game, visit the site, pay their money, and get into the beta instantly.  Between 10 and 500 people sign up a day, depending on how much press the game is getting at the time, and I’m very curious to find out how many of those will join the beta if it’s available immediately, and they aren’t told they have to wait.

I’m currently working on the text-mode robot client.  I’ve got it logging into the lobby and pretending it’s a full SpyParty client, and it can chat, and invite people to games.  I posted on facebook and twitter asking for suggestions of what the robots should say to each other as they’re hanging out in the lobby.

Next up, I’m going to get the robots playing fake matches and games and reporting the game results to the lobby, and logging in and out a bunch.  Then, I’m going to hack up the bees with machineguns load testing app to launch a bunch of EC2 micro instances with multiple robots running on each, and aim them all at my test server.  I expect chaos.

Once I’ve got the obvious stuff fixed, and have figured out how many clients my system can host at a given machine size, I’ll do some tests with beefier machines to make sure it scales linearly.  There are four basic machine resources: CPU, memory, network, and disk…I assume I’ll run out of memory first, but I don’t know for sure.  I was originally going to try to get the entire backend infrastructure running in the cloud, but I think for the near term I’m going to just make sure I get a machine that can accept way more clients signing up and playing than I think I’m going to get when I open up the beta, and hope it holds up.  If it dies due to too much traffic, well, I guess that’s a good problem to have, at least!  I did this same kind of thing with when I started taking beta signups, and I stress tested and optimized it well enough that it didn’t even break a sweat, so hopefully the beta launch itself will go as smoothly. If not, then if Blizzard can do it, so can I! Yikes.

Then, once I’ve got the backend scalable for the robots, I’ll start inviting much larger groups into the beta to loadtest with live players.  There are some features I’m going to need to add to make this work, like chat rooms and colored chat text, and it’s going to be pretty raw to start with, but it should work okay.  The beta testers have been very forgiving of my incredibly primitive lobby so far, so hopefully that attitude will continue!

After everybody’s in, I’ll shut off the signups and let it stew for a few days, and if it’s working, I’ll open it up!  I hesitate to give time estimates on this stuff, because I’ve never hit a date, but most of this will be happening over the next few weeks.

Asserts!

Programming is complicated, and handling errors makes it even more complicated.  Oftentimes, it’s good programming practice to not handle some types of errors gracefully, but simply assert that you aren’t in a state you shouldn’t be in, and if the assertion fails, you exit with an error.

assert(1 + 1 == 2);  // integer arithmetic better work or we're hosed

Of course, during development, the impossible is not only possible, but likely, and so your asserts fire.  And, often in complicated code, you’ll assert things that should be true, but aren’t catastrophic if they’re not true, and so you usually pop up a dialog that gives you the choice to break into the debugger, abort the program, or ignore the assert (just this once, or forever).  The problem is, once you let yourself have that ignore option, you can get lazy, and start using asserts as popup reminders of things to fix.  This tends to be really bad on a large team, because the game is asserting every 2 seconds, and you’re just hitting ignore all the time to other people’s asserts, and it becomes a habit.  However, on an indie sized team, which in my case means exactly one programmer, one can use asserts this way and they can still be useful.  I can get a feel for how often certain types of things are going wrong while I’m testing, and I can remember what I was doing when a specific assert fired most of the time…it goes from a purely quantitative tool to a qualitative tool.

I also can leave the asserts on in beta builds, but in a way that fires silently once and then auto-ignores-forever, and then have them shipped off over the network to the server, so I can see what kinds of things are going wrong on player machines, and how frequently they go wrong.

I tend to be pretty liberal with asserts in my code, and so they fire a lot, and in turn the server logs a lot of them.  About 30000 of them so far in the beta.  Here they are, sorted by frequency:

4918 internalMcostTest(sx, sy)
object_system\subsystems\pathing\pathing.cpp
2237 holding
object_system\subsystems\animation\animation.cpp
2237 ad
object_system\subsystems\animation\animation.cpp
2145 am && (right || left)
object_system\subsystems\animation\animation.cpp
1780 am->playing.blend_out == am->queue.front().blend_in
object_system\subsystems\animation\animation.cpp
1485 am && (boneId != -1) && ad
object_system\subsystems\animation\animation.cpp
1415 cd->object_of_interest
character.cpp
1350 d->NearBookcaseID
situations\bookcase\bookcase.cpp
1114 (err = glGetError()) == GL_NO_ERROR, “code: 0x00000501”
spyparty.cpp
1105 cd->object_of_interest && (cd->object_of_interest == BriefcaseID)
situations\briefcase\briefcase.cpp
994 StatueAD && (StatueAD->Object->Type == object_types::STATUE) && (object_system::GetObject(StatueAD->ParentID)->Type == object_types::PEDESTAL)
situations\pedestal\pedestal.cpp
952 am && (am->playing.type == &core_talks)
situations\conversation\conversation.cpp
813 BriefcaseAD && HoldingBriefcase && d->PlayingCycle
situations\briefcase\briefcase.cpp
801 !d->PlayingCycle
situations\drinks\drinks.cpp
628 verify( pathing::pathGetCharacterValue(x, y) == BriefcasePathValue )
situations\briefcase\briefcase.cpp
539 verify( animation::HandleDetach(am, am->Events[i].event->boneId, cd->holding_right, cd->holding_left, &OnRight) && OnRight )
situations\briefcase\briefcase.cpp
478 cd->object_of_interest == BriefcaseID
situations\briefcase\briefcase.cpp
469 !PoppedYesNoQuestioner
spyparty.cpp
413 d->GoalPedestalID == d->NearPedestalID
situations\pedestal\pedestal.cpp
278 HoldingBriefcase && BriefcaseAD
situations\briefcase\briefcase.cpp
252 verify( HandleDetach(am, am->Events[i].event->boneId, cd->holding_right, cd->holding_left, &OnRight) && OnRight )
situations\pedestal\pedestal.cpp
236 IK.Bone && IK.Target && IK.MeshHardpoint
object_system\subsystems\animation\animation.cpp
229 am->queue.empty()
object_system\subsystems\animation\animation.cpp
216 x == p->ex && y == p->ey
object_system\subsystems\pathing\pathing.cpp
193 0, “unknown packet type: 9”
spy_server.cpp
188 !”stuck!”
character.cpp
172 cd->disposable_left
situations\pedestal\pedestal.cpp
160 verify( pathing::pathGetCharacterValue(x, y) == pathing::PATH_VALUE_INFINITE )
situations\briefcase\briefcase.cpp
151 !cd->holding_right || (cd->holding_right == d->BookID)
situations\bookcase\bookcase.cpp
139 (cd->holding_left == d->BookID) && (!cd->holding_right || (cd->holding_right == d->BookID))
situations\bookcase\bookcase.cpp
110 e.MatchTimestamp > RoundTimeline[LastMarkSuspectIdx].MatchTimestamp
round_events.cpp
97 verify( GetConnectionNames(Us, sizeof(Us), Them, sizeof(Them)) )
spyparty.cpp
78 0, “unknown packet type: 15”
spy_server.cpp
77 0, “unknown packet type: 9”
sniper_client.cpp
74 Distance2(cd->Object->Position, ps_sci->Object->Position) <= MaxHandoffDistance2
situations\briefcase\briefcase.cpp
70 !gd->ForceGoToPedestalID
situations\steal_statue\steal_statue.cpp
66 am->playing.type == &core_briefcase_pickups
situations\briefcase\briefcase.cpp
59 !”spy stuck!”
spy_server.cpp
59 0, “unknown packet type: 9”
spyparty.cpp
55 pathTestOpen(sx, sy)
object_system\subsystems\pathing\pathing.cpp
50 0, “unknown packet type: 7”
spy_server.cpp
48 d->BookBookcaseID && cd->IsGoalOwner(this)
situations\bookcase\bookcase.cpp
43 !”what do to here?”
examples\lobby\lobbyclient.cpp
40 verify( animation::GetPlayingAnimationInfo(am, &time, &duration) )
situations\conversation\conversation.cpp
40 HoldingBriefcase
situations\briefcase\briefcase.cpp
40 am->playing.animid == -1
situations\pedestal\pedestal.cpp
35 n
spyparty.cpp
32 e.MatchTimestamp > RoundTimeline[LastMarkBookIdx].MatchTimestamp
round_events.cpp
26 verify( animation::HandleDetach(am, am->Events[i].event->boneId, cd->holding_right, cd->holding_left, &OnRight) && !OnRight )
situations\bookcase\bookcase.cpp
26 object_system::GetObject(cd->holding_left) && (object_system::GetObject(cd->holding_left)->Type == FNV1(“BOOK”))
situations\bookcase\bookcase.cpp
26 it != am->AnimHandleMap.end()
network.cpp
26 IsTypingString
spyparty.cpp
26 0, “unknown packet type: 8”
spyparty.cpp
25 DefaultCharacterStatePacket && (ndata == CharacterStatePacketSizeBytes)
sniper_client.cpp
25 cd->object_of_interest == d->BookID
situations\bookcase\bookcase.cpp
24 !p2pauth_con && (p2pauthn_state == WAITING_AUTHN)
examples\lobby\lobbyclient.cpp
24 !IsSpy && d->TargetBookcaseID
situations\bookcase\bookcase.cpp
24 HoldingDrink
situations\drinks\drinks.cpp
23 obj->Rotation.IsIdentity()
character.cpp
23 HoldingBook
situations\bookcase\bookcase.cpp
23 !err, “krb5_rd_priv err: -1765328342”
examples\lobby\lobbyclient.cpp
22 (w >= 0) && (w <= 1)
object_system\subsystems\animation\animation_cal3dutils.cpp
22 d_cust && (d_cust->State == drinks_data::INVALID)
situations\serving\serving.cpp
21 !”somehow in a valid state but !HoldingDrink?!”
situations\drinks\drinks.cpp
20 propid && ( (Spy->holding_right == propid) || (Spy->holding_left == propid))
situations\steal_statue\steal_statue.cpp
20 !HoldingDrink
situations\drinks\drinks.cpp
18 !”somehow didn’t get statue”
situations\pedestal\pedestal.cpp
18 ad && (ad->Object->Type == object_types::STATUE)
situations\pedestal\pedestal.cpp
16 o && !(o->Flags & object::UNMANAGED)
object_system\object_manager.cpp
16 am->queue.empty()
situations\drinks\drinks.cpp
13 !”something went wrong picking up briefcase!”
situations\briefcase\briefcase.cpp
12 !”should have detached”
situations\bookcase\bookcase.cpp
11 !”should not get here”
situations\conversation\conversation.cpp
11 0, “unknown packet type: 11”
spyparty.cpp
10 (w >= 0) && (w <= 1) && (u >= 0) && (u <= 1) && (v >= 0) && (v <= 1)
checkerlib\misc\geomutils.cpp
10 verify( network::SendPacket(&gs, sizeof(gs), true) )
spy_server.cpp
10 (rem >= 0.0f) && (rem < 1.0f)
spyparty.cpp
10 Pedestal
situations\pedestal\pedestal.cpp
10 (err = glGetError()) == GL_NO_ERROR, “code: 0x00000505”
spyparty.cpp
9 Statue && (Statue->Type == object_types::STATUE)
object_utils.cpp
9 Level
network.cpp
9 !err
examples\lobby\async_krb5_wrapper.cpp
9 0, “unknown packet type: 1”
sniper_client.cpp
8 SpyCheck == player_control::state::TESTING
situations\check_watch\check_watch.cpp
8 ObjectIDToByte.empty() || nettest_mode
network.cpp
8 !IsSpy && cd->object_of_interest
situations\pedestal\pedestal.cpp
8 ByteToObjectID.empty() || nettest_mode
network.cpp
8 0, “unknown packet type: 20”
sniper_client.cpp
8 0, “unknown packet type: 13”
spy_server.cpp
8 0, “unknown packet type: 13”
spyparty.cpp
7 ObjectIDToByte.empty() && ByteToObjectID.empty()
network.cpp
7 (CameraMode == SNIPER_CAMERA) && Level
round_events.cpp
7 0, “unknown packet type: 24”
spy_server.cpp
6 SwapStatueID && ( (Spy->holding_right == SwapStatueID) || (Spy->holding_left == SwapStatueID))
situations\steal_statue\steal_statue.cpp
6 RoundTimeline.size() < round_events_packet::MAX_NUM_EVENTS
round_events.cpp
6 o
network.cpp
6 mc && (mc->Object->Type == object_types::STATUE) && (StatueMeshIndex < mc->Meshes.size())
object_utils.cpp
6 IsChatAllowed()
spyparty.cpp
6 ad
object_utils.cpp
6 0, “unknown packet type: 21”
sniper_client.cpp
5 !”shouldn’t get here”
situations\steal_statue\steal_statue.cpp
5 mark_value <= 1.0f
spyparty.cpp
5 client && client->OtherClientID
spyparty_lobby.cpp
5 0, “unknown packet type: 25”
spy_server.cpp
4 !”shouldn’t get here, must have moved while holding book”
situations\book_transfer\book_transfer.cpp
4 object_system::GetObject(History.States[i].ID)
network.cpp
4 Level
sniper_client.cpp
4 Level->ActiveGameTypeIndex < Level->GameTypes.size()
sniper_client.cpp
4 !err
examples\lobby\lobbyclient.cpp
4 am
network.cpp
4 0, “unknown packet type: 10”
spyparty.cpp
3 SpyCheck == player_control::state::TESTING
situations\seduction\seduction.cpp
3 !”shouldn’t get here”
examples\lobby\lobbyclient.cpp
3 i_ind == i_dep+1
character.cpp
3 !Focus
player_control.cpp
3 Control->GetSpyTriggeredResult(this) == player_control::state::TESTING
situations\double_agent\double_agent.cpp
3 !(ChooserCurrentCharacter->Object->Flags & object_system::object::UNMANAGED)
ui.cpp
3 am->Events[i].animation->getCoreAnimation()
situations\drinks\drinks.cpp
3 0, “unknown packet type: 16”
spyparty.cpp
3 0, “unknown packet type: 15”
spyparty.cpp
2 verify( network::SendPacket(&p, sizeof(p), true) )
network.cpp
2 verify( network::SendPacket(&gs, sizeof(gs), true) )
spyparty.cpp
2 verify( ConfirmGameIDToLobby(CurrentPlayID, CurrentGameID) )
spy_server.cpp
2 verify( animation::GetPlayingAnimationInfo(am, &time, &duration) )
situations\book_transfer\book_transfer.cpp
2 t >= 0
round_events.cpp
2 State.CurrentNode->Parent->StringSoFar == State.CurrentLeaf->StringSoFar
spyparty.cpp
2 s
checkerlib\misc\glutils.cpp
2 mc
situations\steal_statue\steal_statue.cpp
2 glMultiTexCoord2f_ && glActiveTexture_
spy_server.cpp
2 GameIDs.find(PacketPlayID) == GameIDs.end()
examples\lobby\lobbyclient.cpp
2 !err && p2pauth_con && krbtgt.data && (krbtgt.length < KRBTGT_LIMIT)
examples\lobby\lobbyclient.cpp
2 (err = glGetError()) == GL_NO_ERROR, “code: 0x00000506”
spyparty.cpp
2 !decoder.underflowed() && decoder.on_last_byte()
examples\lobby\lobbyclient.cpp
2 CurrentNetworkObjectID
network.cpp
2 !ChooserScrollDrag
ui.cpp
2 (cd->object_of_interest == d->BookID) || (ps->status != pathing::PATH_success)
situations\bookcase\bookcase.cpp
2 cd->object_of_interest && (cd->object_of_interest == d->BookID)
situations\bookcase\bookcase.cpp
2 ATPCachedDescription
player_control.cpp
2 Array && Num
checkerlib\misc\utils.h
2 am->playing.type == core_book_hidefilm_okay
situations\book_transfer\book_transfer.cpp
2 0, “unknown packet type: 8”
sniper_client.cpp
2 0, “unknown packet type: 2”
spy_server.cpp
2 0, “unknown packet type: 22”
sniper_client.cpp
1 verify( network::SendPacket(DefaultCharacterStatePacket, CharacterStatePacketSizeBytes, true) )
spy_server.cpp
1 verify( network::SendPacket(CommandsPacketBuffer, SizeBytes, true) )
network.cpp
1 verify( Camera.Project(vector_3(0, 0,0), &origin) )
spyparty.cpp
1 StatueAD && (StatueAD->Object->Type == object_types::STATUE)
situations\pedestal\pedestal.cpp
1 !”somehow didn’t get book”
situations\bookcase\bookcase.cpp
1 ScreamSound
spy_server.cpp
1 network::IsConnected()
spy_server.cpp
1 it != NetTestByteMap.end()
network.cpp
1 fabs(1-Length2(Axis)) < 1e-5
checkerlib\misc\math4d.h
1 (err = glGetError()) == GL_NO_ERROR, “code: 0x00000502”
spyparty.cpp
1 DefaultCharacterStatePacket && (ndata == sizeof(*DefaultCharacterStatePacket) + DefaultCharacterStatePacket->NumCharacters*sizeof(DefaultCharacterStatePacket->States[0]))
sniper_client.cpp
1 !decoder.underflowed()
network.cpp
1 !decoder.underflowed() && decoder.on_last_byte()
network.cpp
1 (d >= 0) && (d <= 1)
c:\users\checker\dev\spyparty\project\spyparty\code\network.h
1 CharacterStatePacket && (CharacterStatePacket->NumCharacters <= MaxNumCharacters)
network.cpp
1 ByteToObjectID.find(CurrentNetworkObjectID) == ByteToObjectID.end()
network.cpp
1 0, “unknown packet type: 8”
spy_server.cpp
1 0, “unknown packet type: 6”
spy_server.cpp
1 0, “unknown packet type: 6”
sniper_client.cpp
1 0, “unknown packet type: 4”
sniper_client.cpp
1 0, “unknown packet type: 2”
sniper_client.cpp
1 0, “unknown packet type: 25”
sniper_client.cpp
1 0, “unknown packet type: 19”
spyparty.cpp
1 0, “unknown packet type: 18”
spyparty.cpp
1 0, “got new play id with existing: 0xca98 “
sniper_client.cpp
1 0, “got new play id with existing: 0x57a5 “
sniper_client.cpp
1 0, “got new play id with existing: 0x5267 “
sniper_client.cpp
1 0, “got new play id with existing: 0x3e15 “
sniper_client.cpp
1 0, “got new play id with existing: 0x33fc “
sniper_client.cpp
1 0, “got new play id (0xabeb) with existing (0xcdb6) “
sniper_client.cpp
1 0, “got new play id (0x6f9f) when already playing with existing (0xfb2e) “
sniper_client.cpp
  1. currently at 16681 people as of this post []

SpyParty at Evo 2012 pics!

I’m back from Evo, and finally mostly rested.  It was a lot of fun!  I had no idea what to expect and so I went in with no expectations, and that’s good, because it turned out to be quite different from previous playtests. The show floor was a small part of the overall event, and the main focus was obviously on the competition, so attendees were able to enjoy the Indie Showcase titles without the long lines that always happen at PAX. For SpyParty, this meant people could play for longer, come back more often, and explore more of the game’s depth. I had the same booth setup as PAX West 2011, with the three stations, and it worked out pretty well. People seemed to enjoy the game and the stations were almost always full, and as our voices gave out,1 we had attendees train other attendees, which I particularly like because it starts to get to some of my future goals for mentoring modes.

I have to admit, I was slightly worried we’d get beat up or something, but actually my first experience with the Fighting Game Community was really positive, and they really seemed to appreciate the depth of the game and what I’m going for.

I want to thank Tom and Tony Cannon, and Seth Killian for making this possible, it was a really great time.

Below are a ton of pics. Continue reading ‘SpyParty at Evo 2012 pics!’ »

  1. The show floor hours were 8am to 12am!!! Holy cow that’s exhausting…PAX hours are like 10am to 6pm! []

Competitive Gaming: Come Play SpyParty at Evo in Vegas, July 6th & 7th

I give a lot of lectures on games.  In these lectures, I often talk about games as an art form, and how games are different from the other popular and important art and entertainment forms.  Even though we often get compared to film, I think when games finally fulfill their potential, they will be as deeply different from film in the ways they affect people emotionally as film is from music or painting.1

I don’t think anyone knows exactly what these differences will feel like in 50 or 100 years, but I think we can get whiffs of them in current games, and for me, the scent is especially strong in competitive player-skill games.  There is something about a competitive player-skill game that gets to the essence of interactivity, the thing that makes games different from the other art and entertainment forms.  The players, interacting with the game systems and each other, using their physical and mental skills to achieve a clearly defined win-state…it can be magic.  Although there are many different-yet-amazing competitive player-skill games, like my old flame Counter-Strike, but also obviously Starcraft, and DOTA/League of Legends,2 I think the magic I’m talking about is most clearly captured in this famous clip of a Street Fighter tournament:

There have been articles written about these seconds of gameplay, it has become internet-famous, and I have used it in several of the aforementioned lectures.

This magical thing happened at a fighting game tournament called The Evolution Championship Series, or more commonly, Evo.

It’s always been one of my design goals for SpyParty to be regarded as a competition-worthy player-skill game, to try to attain the depth of a game like Counter-Strike or Street Fighter, but by emphasizing a very different set of player-skills from the current crop of competition games.  However, to be included in that pantheon your game has to be able to stand up to thousands of hours of play, and designing a game like that turns out to be very hard.  Luckily, I’m patient, but I’m expecting it to take years for SpyParty to get to that level, if it ever does. Eventually, once the game was balanced and tuned and deep enough, I hoped people would start running tournaments, and maybe those tournaments would grow, and then, who knows.  But I’m getting way ahead of myself…

The Email

This is all by way of saying I’ve been fascinated by Evo for a while, but hadn’t spent much time thinking about how it relates to me and my game right here and now.  And then my friend Seth Killian emailed me, “Would you be interested to bring SpyParty to Evo this summer?”

Whoa.

He went on, “Obviously your game isn’t a fighter, but what’s interesting about these guys is that they aren’t just good at fighters–they’re good at games, and breaking down systems, period. They like games that involve psychology, competition, or are just insanely difficult.”

Now you’re talking.

The Show

So, after I said “YES” as fast as I could, Seth introduced me to Tom Cannon, and it’s happening, and I’m nervous but very excited!  Did I mention the nervous part?  The fighting game community is well-known for their, um, honest feedback, and I’m hoping they like SpyParty, but if they don’t, I’m hoping they’ll tell me why, and not just beat me up or something.

In fact, to this end, I offered to invite a bunch of Evo attendees into the beta before the show, so they could read the manual, practice, and play some games online with the current beta testers to increase the level of play once we get there.  Tom put a signup form on the announcement and got over twice as many signups from registered Evo attendees as we planned, so he had to shut it down.  I was originally going to invite 50, but I may try to invite even more of them this week.  I expect some of them will be pretty good by the time the show starts if they practice.  I’m going to do a similar setup to last year’s PAX booth, so three 1v1 stations, but this time I’m also going to try to get two of them on the internet so people in the beta lobby can play people at Evo.  I hope some of the expert players will hang around the lobby to give hands-on demonstrations of SpyParty‘s current player-skill depth!

Even better, it turns out Tom and Seth wanted to have a bunch of player-skill competitive indie games at the show, so they’ve got a great lineup for their Indie Showcase, including AztezBaraBariBall, DIVEKICK, Nidhogg, Super Comboman, and Super Time Force!

But, the coolest thing of all is that I found out the Evo expo is open to the public and free on July 6th and 7th!  So, if you’re in the Las Vegas area, or within driving distance, come by and play SpyParty and all these indie games!  Oh, and I’m sure there’ll be a couple fighting games around somewhere to play if you look hard enough.

Postscript

I have my work cut out for me trying to make SpyParty deep enough to be a competitive gaming title, of course.  There have been a few different articles about the game’s upcoming appearance at Evo, with comments ranging from “awesome!” to “wtf?!”, but my favorite comment was on the Kotaku piece:

LuppyLuptonium Tue 26 Jun 2012 8:26 PM
This is like Bingo being played at an NBA basketball game…. Nothing necessarily wrong with it but strange.
  1. This is why I wish people would stop wasting their time making big movie-wannabe games with big movie-wannabe linear stories the player has to fight/shoot/drive/fly their way through, and instead play our unique strengths. []
  2. or non-digitally, Go and Poker, or even sports like basketball []

Release notes v0.1.2056.0 – THE MEGABUILD

A dispatch from the private beta forums regarding the “megabuild”

Author Message
checker
Post subject: Release notes v0.1.2056.0 – THE MEGABUILD
Posted: 2012/06/22 23:25 
Online
Joined: 2011/06/22 17:00
Posts: 724

I didn’t get to everything I wanted to before I start spectation, so there will be a Daughter of Megabuild at some point soon.

  • add crlf to whole chat log ctrl-c copy so notepad.exe works
  • strip x says: or x whispers: if it’s at the beginning of a single line ctrl-c copy, yay urls!
  • add gameid confirm message to avoid junking game bug due to canceled starts
  • swap statue bad test – wait until action test is over, flicker longer
  • swap statue good test – don’t timeout during pending, go to OT, closest available npc comes over, statue swaps instantly when they bring it to looking idle
  • swap statue preview modes for these changes
  • prevent npcs and spy from picking up ghost statues
  • in practice: ctrl-m resets missions, shift-m adds time, ctrl-shift-m does both
  • fix multiple characters picking up statues at the same time
  • stop keyboard beep on alt keys
  • change add time to 45 seconds
  • treat book holding as one handed when not reading
  • handle case where ambassador/scientist picks something up while handing briefcase over
  • retry briefcase putdown on path failure as well
  • don’t crash on non-text data in clipboard
  • try to get unstuck if we’re failing to path because our start point is blocked
  • don’t read a book you’re not holding if you’re failing to get to the bookshelf
  • don’t freeze if you can’t put the briefcase down, just move a bit to a new spot
  • wait before picking up the statue
  • add /win x y w h maximized borderless command
  • oh man, actually tick situations in scored order, yikes! will this change things?
  • display highlit and lowlit characters on results screen
  • display waiting text on waiting for results screen
  • separate y invert setting for mouse and gamepad
  • handle case where sniper shoots too late given the time sync
  • write game result to log
  • don’t show mouse cursor when waiting for results
  • don’t sample keyboard when chat is up in results screen
  • don’t let mouse down state affect spy camera movement
  • /ping command for testing peer-to-peer network connection – hopefully will help with “greeting” and “sniper accept” bugs
  • more handling of briefcase put down and then started moving
  • sample spy keys async instead of doing up/down processing to avoid losing ups on focus change
  • more explicit duplicate login prompt
  • allow briefcase pickup with just right hand
  • fixing spy walking away with briefcase while putting down
  • better phrasing of game results everywhere
  • disable attachment lerp on detach so statues don’t erroneously end up on the floor
  • prevent focus changing mouse presses from interacting with game
  • fix drinks.cpp crash, animation was getting freed out from under the events
  • more play id checking to avoid issues with old packets coming in when starting games
  • don’t pick up briefcase for a while if you’ve given up
  • initialize the statues before game start
  • use ConnectTime to register player since LoginTime not available yet, fixing beta homepage display
  • listen pauses
  • don’t prompt to escape from practice menu
  • fix problem with shift key not working after sniping somebody with gamepad
  • tells for bailing on banana bread okay and bad action test
  • fake banana bread available when mission selected but not enabled
  • add escape confirmation to results screen
  • remove escape confirmation from practice screen
  • don’t hide mouse cursor on sniper game start waiting screens so you can adjust your window
  • wait a bit after disposing of book as well
  • log some more connection info
  • pause at bookshelf before picking up book…enough time to seduce?
  • add CancelInteraction everwhere there’s an action test and it’s bailed on
  • be clear that the first whisper was delivered
  • better newline handling in lobbyclient assert logs
  • verify overtime on sniper machine before beeping
  • flash window when in background and beeps happen
  • fix spy missions in practice sniper
  • think I fixed the levitating characters by the briefcase bug
  • add SpyWatchID to holding_right to block animations (especially bug) while checking watch
  • expiring actions to avoid accidental banana bread triggers
  • ctrl/shoulder slam all the way to highlight/lowlight
  • single level highlight and lowlight
  • new inspect statues with inspecting neighboring statues
  • pedestal works with disposable holding, drinks, briefcase, like bookshelves
  • play clank sound when teleporting statue back
  • add layer attribute for hittest ignore and don’t hittest muntin bars with laser
  • hittest columns with laser on both spy and sniper view
  • wait a bit after putting book away
  • make briefcase non-infinite and fix spiral search to deal with max open value
  • make pathTestOpen take rectangle for anisotropic queries and fix bookcase pileup
  • don’t crash or delete krb5 files on duplicate login
  • gl and cpuid info to log and journal
  • sometimes interrupted people finish talking completely

I really should not have shoved so much stuff into a single build, but it feels pretty good. I’m sure there are some bugs, as well, but I think this build is a subtle but significant step forward in continuing to increase the depth of the game.

Chris