Fic: The Only Exception

Hey, look. It’s another fic! (See all of them here.)

There’s a lot of things, in terms of backstory with the other characters and things that you don’t see, that I have specced out in my mind. I thought it would be interesting to present them in text form to fill in some of the gaps, as it were, in the main YAGS storyline.

Needless to say, these pieces will be highly spoilerific, so I recommend you do not read them unless you have finished the game at least once. (Likewise, they may not make as much sense if you haven’t played the game at least once.)

Seriously. Spoilers ahead! Do not continue if you haven’t played the game!

(Additional warning: This spoils the secret route of the game. If you haven’t found it yet [you’ll know if you have], and want to, do not read.)

Continue reading “Fic: The Only Exception”

Codes: Game Trailer (in RenPy!)

As someone with no video editing experience, looking at the requirement of making a trailer for YAGS, I was rather daunted by the prospect of figuring something out.

Enter RenPy, and ATL! A WIP version of the trailer can be viewed here. (Edit: Here now.)

I was able to create the entire game trailer in RenPy. The best part is that, because most of the sprites aren’t done yet, but the trailer is just RenPy code, I can easily fill in the final sprites later and re-record the video, without having to do serious updates. (At least, if I was doing this with video editing software, I imagine it would be a larger pain to swap images out.)

The best way to share this is probably to literally just paste the entire code used to create the trailer below. There are some notes and things I want to point out, after this huge block of text.

define quickwipeclear = CropMove(0.2, "wipeleft")
define quickwipe = CropMove(0.5, "wipeleft")
define quickwiperight = CropMove(0.5, "wiperight")
define quickwipeup = CropMove(0.5, "wipeup")

image welcomecollege = Text("Welcome to your Freshman year of college...", size=70)

image makefriends = Text("Meet people...", size=60)
image makefriendstwo = Text("(...and make friends...)", size=40)
image navigatecollege = Text("...navigate college life...", size=60)
image comeout = Text("...and come out...", size=60)
image comeoutfriends = Text("(...with a bit of help.)", size=40)

image makedec = Text("Make life-changing decisions!", size=40)
image playgames = Text("Play board games!", size=40)
image findbf = Text("Maybe even find a boyfriend?", size=40)

image comingsoon = Text("{color=#ffffff}Coming soon{/color}", size=60)
image myurl = Text("{color=#ffffff}https://yags-game.tumblr.com{/color}", size=40)

image adam grin = "images/adamgrin.png"
image nikhil crossed = "images/nikhilcrossed.png"

image adamchoice = "images/adamchoices.png"
image adamchoice choiceone = "images/adamchoices1.png"
image adamchoice choicetwo = "images/adamchoices2.png"
image adamchoice choicethree = "images/adamchoices3.png"

image gameschoice = "images/gamechoices.png"
image gameschoice choiceone = "images/gamechoices1.png"

image questionmark = Text("?", size=300)


transform questionm:
    xalign 0.5
    yalign 0.5
    alpha 0.0
    linear 1.0 alpha 1.0
    parallel:
        linear 4.0 alpha 0.2
        pause 1.0
        linear 0.5 alpha 0.0
    parallel:
        linear 5.5 zoom 5.0
transform campustransp:
    alpha 0.0
    pause 0.5
    linear 0.5 alpha 0.2
transform logoshow:
    zoom 20.0 xalign 0.5 yalign 0.5 alpha 0.0
    parallel:
        linear 0.5 alpha 1.0
    parallel:
        linear 0.5 zoom 0.8
    parallel:
        linear 0.5 rotate 2.0
    linear 0.2 zoom 1.3
    parallel:
        linear 0.3 rotate 0.0
    parallel:
        linear 0.3 zoom 1.0
    linear 0.2 zoom 1.1
    linear 0.2 zoom 1.0
transform whitefadeout:
    xalign 0.5
    yalign 0.5
    alpha 0.0
    linear 0.5 alpha 0.8
    linear 0.5 alpha 1.0
transform whitefadequick:
    xalign 0.5
    yalign 0.5
    alpha 0.0
    linear 0.5 alpha 1.0
transform subtextfadein:
    xalign 0.5
    yalign 0.3
    alpha 0.0
    pause 1.0
    linear 0.3 alpha 1.0
transform textfadein:
    xalign 0.5
    yalign 0.2
    alpha 0.0
    linear 0.3 alpha 1.0
transform adamcursor1:
    xalign 0.5
    yanchor 0.0
    ypos -50
    linear 1.0 ypos 350
transform adamcursor2:
    ypos 350
    parallel:
        linear 0.5 ypos 187
    parallel:
        linear 0.5 xoffset 50
transform adamcursor3:
    ypos 187
    parallel:
        linear 0.25 ypos 270
    parallel:
        linear 0.25 xoffset -20
transform gamescursor1:
    xalign 0.5
    yanchor 0.0
    ypos -50
    parallel:
        linear 0.7 ypos 226
    parallel:
        linear 0.7 xoffset 100
        
label start:
$ quick_menu = False
scene black
$ renpy.pause(2.0)
hide window
play music "theme.wav" noloop
show logo at logoshow
$ renpy.pause(1.5)
show campus behind logo with quickwipe
show logo with move:
    yoffset -100
show welcomecollege with dissolve:
    yanchor 1.0
    xalign 0.5
    ypos 550
$ renpy.pause(1.5)
show whitefill at whitefadeout
$ renpy.pause(0.5)
show white at whitefadequick
$ renpy.pause(1.7)
scene white
show makefriends at textfadein
show makefriendstwo at subtextfadein
show adam with moveinleft:
    yalign 1.0
    xanchor 0.5
    xpos 0.2
$ renpy.pause(1.0)
show juan with moveinright:
    yalign 1.0
    xanchor 0.5
    xpos 0.8
$ renpy.pause(1.0)
show frat behind adam with quickwiperight
$ renpy.pause(1.0)
show otherfrat behind adam with quickwipeup
$ renpy.pause(1.3)
scene white with quickwipeclear
show navigatecollege at textfadein
show james with moveinleft:
    yalign 1.0
    xanchor 0.5
    xpos 0.2
$ renpy.pause(1.0)
show dan with moveinright:
    yalign 1.0
    xanchor 0.5
    xpos 0.8
$ renpy.pause(1.0)
show halls behind james with quickwiperight
$ renpy.pause(1.0)
show otherqa behind james with quickwipeup
$ renpy.pause(1.3)
scene white with quickwipeclear
show comeout at textfadein
show comeoutfriends at subtextfadein
show jake with moveinleft:
    yalign 1.0
    xanchor 0.5
    xpos 0.2
$ renpy.pause(1.0)
show nikhil with moveinright:
    yalign 1.0
    xanchor 0.5
    xpos 0.8
$ renpy.pause(1.0)
show gamer behind jake with quickwiperight
$ renpy.pause(1.0)
show othergames behind jake with quickwipeup
$ renpy.pause(1.5)
scene theyard
show adam
show white:
    xalign 0.5
    yalign 0.5
    alpha 0.3
show makedec:
    xalign 0.5
    yalign 0.3
with quickwipeup
$ renpy.pause(1.0)
hide white
hide makedec
with dissolve
"You wonder if it's just his friendly demeanor, but somehow, the prompt makes you actually consider telling him. After all, you {i}do{/i} have to live with this guy for the next 9 months.{w=3.25}{nw}"
window hide
show cursor at adamcursor1
show adamchoice behind cursor:
    xalign 0.5
    yalign 0.5
$ renpy.pause(0.45)
show adamchoice choiceone:
    xalign 0.5
    yalign 0.5
$ renpy.pause(0.2)
show adamchoice choicetwo:
    xalign 0.5
    yalign 0.5
$ renpy.pause(0.2)
show adamchoice choicethree:
    xalign 0.5
    yalign 0.5
$ renpy.pause(0.7)
show cursor at adamcursor2
$ renpy.pause(0.2)
show adamchoice choicetwo
$ renpy.pause(0.2)
show adamchoice choiceone
$ renpy.pause(0.7)
show cursor at adamcursor3
$ renpy.pause(0.2)
show adamchoice choicetwo
$ renpy.pause(0.7)
hide adamchoice
hide cursor
show adam grin
"You hit yourself internally, vowing to take the next good opportunity to come out to him.{w=1.75}{nw}"
scene atrium
show jake:
    yalign 1.0
    xanchor 0.5
    xpos 0.2
show nikhil crossed:
    yalign 1.0
    xanchor 0.5
    xpos 0.8
show white:
    xalign 0.5
    yalign 0.5
    alpha 0.3
show playgames:
    xalign 0.5
    yalign 0.3
with quickwipe
$ renpy.pause(1.0)
hide white
hide playgames
with dissolve
nick "Ooo. Make a new city!{w=0.75}{nw}"
jake "No way. Finish that city. Guaranteed points. And a meeple back.{w=1.0}{nw}"
window hide
show cursor at gamescursor1
show gameschoice behind cursor
$ renpy.pause(0.6)
show gameschoice choiceone
$ renpy.pause(1.0)
hide cursor
hide gameschoice
show nikhil
nick "Going with the safe play? Both of you play it safe too often.{w=1.5}{nw}"
scene qaroom
show dan:
    yalign 1.0
    xalign 0.5
show white:
    xalign 0.5
    yalign 0.5
    alpha 0.3
show findbf:
    xalign 0.5
    yalign 0.3
with quickwipe
$ renpy.pause(1.0)
hide white
hide findbf
with dissolve
"Dan is there by himself, sitting on a couch when you arrive.{w=1.25}{nw}"
dan "Christopher!{w=1.0}{nw}"
hide window
show questionmark at questionm
$ renpy.pause(0.5)
hide dan
show james behind questionmark
$ renpy.pause(1.0)
hide james
show jake behind questionmark
$ renpy.pause(1.0)
hide jake
show nikhil behind questionmark
$ renpy.pause(1.0)
hide nikhil
show juan behind questionmark
$ renpy.pause(1.0)
hide juan
show adam behind questionmark
$ renpy.pause(1.2)
scene black with quickwipeup
show logo with dissolve:
    zoom 0.8
    xalign 0.5
    yalign 0.2
$ renpy.pause(1.8)
show comingsoon with dissolve:
    xalign 0.5
    yalign 0.6
$ renpy.pause(1.0)
show myurl with dissolve:
    xalign 0.5
    yalign 0.7
$ renpy.pause()
return

The most important timing thing is to automatically advance text without clicking. The combination of {w=number} and {nw} does that quite nicely.

The second weird thing is how to handle choice menus. Because there’s otherwise no good way to handle that, and you really don’t want the timing to be at the whim of you clicking at the right time, I cheated…

I put up a real choice menu, then took screenshots of the screen and with the mouse hovered over each option, in turn. I then removed the choice menu and showed these screenshots instead (with a fake cursor image hovering over them and moving around).

Everything else is fairly straightforward. We rely heavily on RenPy automatically creating displayable for items in the /images folder with the right names. Everything else is just making sure we synchronize to the music.

To actually record the trailer, we take advantage of Windows 10′s game bar. Hit Win+G while the game is started, then start recording. The extra click and pause at the beginning is so you can move your cursor out of the way before the actual trailer starts. After it’s recorded, you can trim it to size using Windows 10′s Photos program (right click the video, Open With, Photos).

Fic: The Morning After

Hey, look. It’s another fic! (See all of them here.)

There’s a lot of things, in terms of backstory with the other characters and things that you don’t see, that I have specced out in my mind. I thought it would be interesting to present them in text form to fill in some of the gaps, as it were, in the main YAGS storyline.

Needless to say, these pieces will be highly spoilerific, so I recommend you do not read them unless you have finished the game at least once. (Likewise, they may not make as much sense if you haven’t played the game at least once.)

Seriously. Spoilers ahead! Do not continue if you haven’t played the game!

(Additional warning: This spoils the secret route of the game. If you haven’t found it yet [you’ll know if you have], and want to, do not read.)

Continue reading “Fic: The Morning After”

Status Update

I’ve been posting monthly status updates at @bobcgames, but I figured I owed some update over here as well.

There hasn’t been too much progress, in the past few months. My sprite artist has been crazed with his webcomic, and my background artist seems to have had some personal issues.

As noted in the last post, I found a new background artist, and the final two backgrounds in the game are now complete, thanks to Vui Huynh. The epilogue apartment is kind of a gorgeous way to end on backgrounds.

In the music department, I have almost all of the tracks as demo versions, so I’ll be able to start staging music into the game soon. With any luck, the next build in a couple of months will have fully-staged music as well as another sprite or two.

It’s worth noting that the game is already fully playable, despite the lack of assets, and you can download it today. Just click the “Get the Game” link above.

Progress!

Fic: Confrontation

Hey, look. It’s another fic! (See all of them here.)

There’s a lot of things, in terms of backstory with the other characters and things that you don’t see, that I have specced out in my mind. I thought it would be interesting to present them in text form to fill in some of the gaps, as it were, in the main YAGS storyline.

Needless to say, these pieces will be highly spoilerific, so I recommend you do not read them unless you have finished the game at least once. (Likewise, they may not make as much sense if you haven’t played the game at least once.)

Seriously. Spoilers ahead! Do not continue if you haven’t played the game!

Continue reading “Fic: Confrontation”

Release #9

Thanks to @stollcomics‘ epic toiling, we have a new game build with two new sprites!

Version: v0.20407
Release name: D is for Dan

As usual, you can get it on the download page

Changes in this release:

  • Replaced Jake and Dan sprites in game
  • Added 33 more collectibles
  • Added another achievement
  • Minor sprite cleanup

Stats for this release: 26,254 dialog blocks, 230,683 words, 1,211,435 characters 

Fic: First Impressions

Hey, look. It’s another fic! (See all of them here.)

There’s a lot of things, in terms of backstory with the other characters and things that you don’t see, that I have specced out in my mind. I thought it would be interesting to present them in text form to fill in some of the gaps, as it were, in the main YAGS storyline.

Needless to say, these pieces will be highly spoilerific, so I recommend you do not read them unless you have finished the game at least once. (Likewise, they may not make as much sense if you haven’t played the game at least once.)

Seriously. Spoilers ahead! Do not continue if you haven’t played the game!

Continue reading “Fic: First Impressions”

Codes: Changing Stubble (lol)

I’ve been having a few discussions with people lately about custom RenPy commands, and generally recommending them because you get the ability to lint your game scripts.

For example, the fact that I have lint checks on my commands to show sprites, and change their expressions, helped me catch a bunch of typos and misordered arguments during sprite staging, which would have been a huge pain to track down later (and that would have resulted in exceptions during gameplay).

Given the benefits of lint, I wanted to share an extremely small example for something that I could have easily done manually: Changing characters’ facial hair in the game.

First off, the code:

python early:
def parse_showstubble(lex):
who = lex.simple_expression()
level = lex.rest()
return (who, level)
def lint_showstubble(o):
who, level = o
if len(o) != 2 or who is None or level is None:
renpy.error("Invalid showstubble declaration")
return
if who != "adam" and who != "james":
renpy.error("Invalid person " + who)
return
try:
lvlint = int(level)
if lvlint < 0 or lvlint > 3:
renpy.error("Invalid level " + level)
except ValueError:
renpy.error("Invalid level " + level)
def execute_showstubble(o):
who, level = o
if not who in store.spposes_data:
store.spposes_data[who] = CharPoses()
lvlint = int(level)
if lvlint == 1:
store.spposes_data[who].stubble = "_sblight"
elif lvlint == 2:
store.spposes_data[who].stubble = "_sbheavy"
elif lvlint == 3:
store.spposes_data[who].stubble = "_sbbeard"
else:
store.spposes_data[who].stubble = ""
renpy.register_statement("showstubble", parse=parse_showstubble, execute=execute_showstubble, lint=lint_showstubble)

This then gets used in game code like

showstubble adam 1

It’s worth noting I could have easily not added this command, and simply written

$ sposes_data['adam'].stubble = "_sblight"

However. the benefit of doing it this way, again, is the lint checks. If you accidentally try to set stubble on someone else (say, Jake), or if you fat-finger the stubble value (23), you’ll get a pre-runtime error. Versus if you do that directly (say, “sblightr”), you’ll get an ugly runtime error only when you hit that line of code.

I’d argue there’s very little that isn’t worth making a custom command for. In addition to this, I have them for Instant Messaging in addition to Sprites and even the day transition code. In addition to linting, this gives you the ability to easily change something en masse (like when I wanted a day transition to also update the save identifier string).