Phoenix goes Ren'Py

A place to discuss things that aren't specific to any one creator or game.
Forum rules
Ren'Py specific questions should be posted in the Ren'Py Questions and Annoucements forum, not here.
Message
Author
User avatar
monele
Lemma-Class Veteran
Posts: 4101
Joined: Sat Oct 08, 2005 7:57 am
Location: France
Contact:

Phoenix goes Ren'Py

#1 Post by monele » Sun Feb 18, 2007 6:45 pm

Okay... I've been working on this for quite a few days now, and while it's not totally done, I'm going to post it (if at least to get a few bugs squashed).

NOTE : do not go fullscreen! I used a rather weird resolution because of the source I used for images. It shouldn't make your computer explode but either you won't be able to switch, or it won't display properly anyway, so... avoid it :).

Windows : http://www.megaupload.com/?d=75YFVNIY (~7mb)
Linux : http://www.megaupload.com/?d=P8SWEULB (~9mb)
Mac : http://www.megaupload.com/?d=22DLUJDZ (~8.5mb)

http://monele.eviscerate.net/phoenix0.2.zip
http://monele.eviscerate.net/phoenix02to03.zip

== DISCLAIMER ==
Ok, first things first : I didn't do any of these graphics, sounds, music, story or ideas. They're from Gyakuten Saiban 3, also known as Phoenix Wright outside Japan. No, I'm not trying to steal anything :), I'm trying to teach something.

Image Image Image Image Image Image Image


My primary goal was to see if Ren'Py could make what the Phoenix Wright games do : shakes, bipping while talking, animations all around, etc... The conclusion is : hail Ren'Py. Not only it does it all, but it can be setup to make the scenario scripting quite easy (I couldn't make it easier but I'm open for any suggestion).

My second goal is to show the results, open source, so everyone can learn how to do the same. Hey, if someone can use any of the "new" features, it makes it all worth it. So go ahead, read the code, and ask questions if something isn't clear. I tried to put comments in english for the most part but some might have slipped through ^^;.
Basically, script.rpy has the whole story (and two animation functions I wanted to reuse often).
options.rpy is the regular Ren'Py file (there were a few modifications though).
effects.rpy contains my Shaker effect which was published in the Cookbook already.
defs.rpy contains character animations, Character definitions and a lot of callback functions to combine both. You can also find all image statements in there.
interface.rpy deals with the Court Record interface. I did that a bit in a hurry though ^^;.

My third goal... is maybe to convert a few people to Phoenix Wright ^^. Maybe you never heard of the game or didn't have a chance to try it out. In its current state, this "demo" doesn't have enough to show the real core of the gameplay though, so this third goal might not be reached easily ^^;...


== BUGS ==

As I said, there are a few bugs roaming around. PyTom, I hope you can help with this... and sorry in advance ^^;...

- I get a random crash about something "clipped".

Code: Select all

-- Full Traceback ------------------------------------------------------------

  File "D:\Jeux\renpy-5.6.6\renpy\bootstrap.py", line 166, in bootstrap
  File "D:\Jeux\renpy-5.6.6\renpy\main.py", line 275, in main
  File "D:\Jeux\renpy-5.6.6\renpy\main.py", line 92, in run
  File "D:\Jeux\renpy-5.6.6\renpy\execution.py", line 97, in run
  File "D:\Jeux\renpy-5.6.6\renpy\ast.py", line 250, in execute
  File "D:\Jeux\renpy-5.6.6\renpy\exports.py", line 466, in say
  File "D:\Jeux\renpy-game\renpy\character.py", line 525, in __call__
  File "D:\Jeux\renpy-game\renpy\character.py", line 334, in display_say
  File "D:\Jeux\renpy-5.6.6\renpy\ui.py", line 66, in interact
  File "D:\Jeux\renpy-5.6.6\renpy\display\core.py", line 1101, in interact
  File "D:\Jeux\renpy-5.6.6\renpy\display\core.py", line 1507, in interact_core
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 251, in event
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 251, in event
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 251, in event
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 130, in event
  File "D:\Jeux\renpy-5.6.6\renpy\display\behavior.py", line 359, in event
  File "D:\Jeux\renpy-game\phoenix/game/interface.rpy", line 12, in enterrecord
  File "D:\Jeux\renpy-5.6.6\renpy\game.py", line 235, in call_in_new_context
  File "D:\Jeux\renpy-5.6.6\renpy\execution.py", line 97, in run
  File "D:\Jeux\renpy-5.6.6\renpy\ast.py", line 373, in execute
  File "D:\Jeux\renpy-5.6.6\renpy\python.py", line 838, in py_exec_bytecode
  File "D:\Jeux\renpy-game\phoenix/game/interface.rpy", line 190, in <module>
  File "D:\Jeux\renpy-5.6.6\renpy\ui.py", line 66, in interact
  File "D:\Jeux\renpy-5.6.6\renpy\display\core.py", line 1101, in interact
  File "D:\Jeux\renpy-5.6.6\renpy\display\core.py", line 1345, in interact_core
  File "D:\Jeux\renpy-5.6.6\renpy\display\core.py", line 782, in show
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 177, in render_screen
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 102, in render
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 228, in render
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 102, in render
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 228, in render
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 102, in render
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 228, in render
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 102, in render
  File "D:\Jeux\renpy-5.6.6\renpy\display\image.py", line 134, in render
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 102, in render
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 228, in render
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 102, in render
  File "D:\Jeux\renpy-game\renpy\display\layout.py", line 323, in render
  File "D:\Jeux\renpy-5.6.6\renpy\display\render.py", line 103, in render
AttributeError: 'NoneType' object has no attribute 'clipped'

While executing game script on line 170 of D:\Jeux\renpy-game\phoenix/game/interface.rpy.
While executing python code on line 190 of D:\Jeux\renpy-game\phoenix/game/interface.rpy.

Ren'Py Version: Ren'Py 5.6.7c
I couldn't reproduce it right now... Maybe it's because I didn't have "from" added to calls at the time. I also fathom it might happen only if you access the Court Record (upper right corner button) at some point.

- the itemlist and peoplelist variables are not saved properly (save, quit game, load). Strange thing, since I think I did the same I did with Magical Boutique to ensure they were saved... uh... oops... I remember now... they had to be initialized *during* the game, not in an init block >.>.... *fixes*. We might want to add this to the wiki at some point ^^;;;...

- Returning from the Court Record interface is the same as clicking. This means you jump to the next say statement or... worse... it validates the current menu. Obviously this had to do with ui.interact being called and cancelling the previous one, but... isn't there a way to prevent this?

- I noticed a strange "light up" of the sprites during the dissolves (dissolve to black of Phoenix looking down, after the intro). It's clearly linked to its eyes animation. Whenever the eyes blink, it lights up the whole sprite.

- As I said before, removing the ctc of the "talk" Character (base for all characters) prevents the "*NI" characters from automatically going to the next say statement.

- I can't click through the dissolve in of the "5 years ago..." text after the intro. It's a transition followed by a ui.interact... might have to do with this? Can't skip through it either I just noticed.

- Wasn't there a configuration variable to limit the max framerate? I just find the pans from Mia to Payne a bit... dizzying @_@... and they're not on the source game, I think, because it's at a lower framerate.

Ooookay... ish all. Moni, out!
Last edited by monele on Tue Mar 27, 2007 3:26 am, edited 1 time in total.

musical74
Eileen-Class Veteran
Posts: 1021
Joined: Sat Dec 18, 2004 6:13 pm
Location: Oregon
Contact:

#2 Post by musical74 » Sun Feb 18, 2007 10:59 pm

Downloading now...

I'll give you my thought on it once I've had a chance to play it properly...and if I run across any error messages of my own.

I rather like the *lawyer sim* (this is my punishment for watching who knows how much Perry Mason and Matlock) so you may have a convert =)
A friend is one that walks in when the world walks out.

User avatar
monele
Lemma-Class Veteran
Posts: 4101
Joined: Sat Oct 08, 2005 7:57 am
Location: France
Contact:

#3 Post by monele » Mon Feb 19, 2007 4:21 am

Phoenix Wright is a lot less serious though... be warned. It's no accurate simulation... more like Arcade Lawyer :)

pheriannath
Regular
Posts: 38
Joined: Fri Feb 16, 2007 5:08 am
Location: Singapore
Contact:

#4 Post by pheriannath » Mon Feb 19, 2007 5:38 am

I got those random crashes too, and I didn't check the 'court record'...it happened pretty early on, when Mia was asked about the cause of death >_>

I'll take a glance at the source, but I'm no Ren'Py guru...let's see what PyTom has to say.

User avatar
mikey
Lemma-Class Veteran
Posts: 3245
Joined: Sat Jan 10, 2004 6:03 am
itch: atpprojects
Contact:

#5 Post by mikey » Mon Feb 19, 2007 10:44 am

monele wrote:It's no accurate simulation... more like Arcade Lawyer :)
^_^ That's a great way to put it.

I was actually quite disappointed by the way the evidence is interpreted in PW, it just seems to have so many other options of what it could mean - and a lot of the time the resulting constructions as to what happened aren't easily digestible. The general impression I got was that the game's reasoning behind the evidence must have been done by the writers with the question: "How can this item prove that this or that happened?" And they ended up with a lot of hard-to-believe explanations.

User avatar
PyTom
Ren'Py Creator
Posts: 15423
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#6 Post by PyTom » Mon Feb 19, 2007 10:56 am

Right now, I'm debugging the dissolve-blinking problem. The clipping problem is almost certainly a None managing to sneak in somewhere... when I get a chance, I'll add better debugging for that.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
"Silly and fun things are important." - Elon Musk
Software > Drama • https://www.patreon.com/renpytom

User avatar
monele
Lemma-Class Veteran
Posts: 4101
Joined: Sat Oct 08, 2005 7:57 am
Location: France
Contact:

#7 Post by monele » Mon Feb 19, 2007 11:29 am

and a lot of the time the resulting constructions as to what happened aren't easily digestible.
Which episodes have you played? (I suppose 1 or 2 unless you read japanese). I'll agree that it's not always extremely clear and that some theories are made a bit out of the blue. There are even elements totally left out once you've proven something with them... while they could prove something wrong later. But overall, I'm rather pleased that you usually know how to prove what you want.

I've played the first case of #3 (the content of this very demo) and, according to the translator, it's the best game of the series so far. So maybe I'm biased already. But I'm also playing #1 on my newly acquired DS and, while a bit more... clumsy... it's still challenging and not totally unlogical.

what I really don't like so far (in #1 and #3) :

- You don't always know what you're supposed to do. Either you think too fast and try an evidence which is only supposed to get used later (I loathe Runaway, the adventure game, for that kind of thinking)... or you didn't follow quick enough and, as usual with adventure games, you are forced to go at random.
Fortunately, this hasn't happened too much to me so far. When I messed up, I realized it was all my fault for either not remembering something or just not thinking enough before giving an answer.

- Some clues are so insultingly obvious that you feel like everyone is so dumb. There's a good example in this demo : the picture of the crime scene. Even without knowing a single thing about the case, you could guess what happened ô_o...

- The "adventure" phases are not so thrilling in #1. It's a bit DiviDead like, roaming around to trigger something new you'll discover when you go back. You can't miss anything either since it's all linear... Which is both a blessing and a shame for the investigation part of the game. I'm not asking for a Maupiti Island or Mortevielle Manor (for those who know these games), but still! ^^;...

But really, overall, the thrill of understanding things and finding out you can slam the truth in people's faces is just too much fun... emphasized by the over-the-top presentation. It's like a fighting game, except you're throwing clues at each other instead of fireballs.


PyTom : Thanks ^^. I guess it's easier to debug now that you have it in front of you ^^;

User avatar
mikey
Lemma-Class Veteran
Posts: 3245
Joined: Sat Jan 10, 2004 6:03 am
itch: atpprojects
Contact:

#8 Post by mikey » Mon Feb 19, 2007 12:21 pm

I played the translated episode and one of PW a while ago (though I didn't solve it, I played it at a friend's).

But I guess heavy crime stories (with murder) are for me something that's just not very well suited for a "light-hearted" approach. It's something different if the game investigates the a lost book or something, kind of like the kids' detective agency type of games do http://www.mobygames.com/game/eagle-eye ... -in-london . But Phoenix is a bit grrrr.

I agree with the over-the-top presentation comment - plus in this case it would be even more funny to have some choleric chibis screaming and going all emotional. ^_^

So it's not a BAD game, by no means, actually it's really refreshing and in general it doesn't matter how the clues are, because it's got a nice atmosphere and all...

User avatar
monele
Lemma-Class Veteran
Posts: 4101
Joined: Sat Oct 08, 2005 7:57 am
Location: France
Contact:

#9 Post by monele » Mon Feb 19, 2007 12:56 pm

I played the translated episode
So... this very demo, right? ^^;...
it would be even more funny to have some choleric chibis screaming and going all emotional. ^_^
I'm tempted to follow mrsulu's suggestion of a hentai/anime/Phoenix parody. Could have lots of clichés from the genre. Well, at least, the system will be there. At that point, we could imagine either making a Phoenix spoof, or even use the "Court Record" as a regular Inventory and have some sort of VN / adventure game. The big different between Phoenix and an adventure game is that you don't click around and use every item on every other item, but you're asked what to use at specific points.
(well, unless you consider testimony sentences as "items"... and people during the adventure phases)

User avatar
monele
Lemma-Class Veteran
Posts: 4101
Joined: Sat Oct 08, 2005 7:57 am
Location: France
Contact:

#10 Post by monele » Tue Feb 20, 2007 8:56 am

PyTom : when skipping, which character callbacks are called? start and end I suppose?

EDIT : also, is there a renpy function to simulate "with" or do we have to use renpy.transition() ? (what I'm not sure is *when* is renpy.transition() triggered... I mean, the actual transition)

User avatar
PyTom
Ren'Py Creator
Posts: 15423
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#11 Post by PyTom » Tue Feb 20, 2007 9:23 am

IIRC, it's start, show, show_done, end... That's everything as of now. I'm thinking that it might make sense to have slow_done called fairly immediately when slow isn't enabled.

Spent yesterday dealing with:

Code: Select all

- I noticed a strange "light up" of the sprites during the dissolves (dissolve to black of Phoenix looking down, after the intro). It's clearly linked to its eyes animation. Whenever the eyes blink, it lights up the whole sprite.
It winds up that it was a fundamental problem with the routine that did optimized dissolves. Basically, the routine wasn't color-accurate, so when we switched from the optimized path (which was used when the images on the screen haven't changed for a frame) to the unoptimized path (used when a blink occurs), the colors changed, leading to problems.

For various reasons, this bug hasn't manifested for the years this code has been around (IIRC, it was added early in the 4 series). As usual, Monele is the lucky one. :-)

Now, there is a workaround. One can set config.enable_fast_dissolve to false, and the problem goes away. Unfortunately, so does 25% of the framerate. Not cool.

I spent some time yesterday trying to solve this. To do it, I implemented in C a new blend operator... and then I rewrote it to use MMX assembly, where supported. MMX is nice for this, since it lets you work on 4 16-bit words at once, which is enough to do interpolation in parallel.

So, the result is that I now have code that is more correct, and just as fast, at least on Intel boxes. (Unfortunately, the MMX code doesn't macs, even intel macs, since I only do a PPC build for macs. I could use altivec, but my ancient mac doesn't support that, and I don't like the idea of releasing code I can't test. So there will probably be a slowdown on macs.)
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
"Silly and fun things are important." - Elon Musk
Software > Drama • https://www.patreon.com/renpytom

User avatar
PyTom
Ren'Py Creator
Posts: 15423
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#12 Post by PyTom » Tue Feb 20, 2007 9:28 am

renpy.with is the programmatic equivalent of a with statement.

A transition specified with renpy.transition occurs during the next interaction. The way to thinking of it is that renpy.with is equvalent to a call to renpy.transition followed by a call to renpy.pause, with the pause time determined by querying the transition. (Internally, it works a little differently, since there isn't actually a way to query the transition like this.)
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
"Silly and fun things are important." - Elon Musk
Software > Drama • https://www.patreon.com/renpytom

User avatar
monele
Lemma-Class Veteran
Posts: 4101
Joined: Sat Oct 08, 2005 7:57 am
Location: France
Contact:

#13 Post by monele » Tue Feb 20, 2007 11:31 am

Well I could also test end just to stop animations once you stop skipping but yeah, in a sense, slow_done should be called too.

About the graphical glitch : so uhhh... in conclusion, it's fixed and just as fast as before... but only on PCs?
And the "workaround" you talk about is not necessary then, I take it.

Wanted to add renpy.with to the wiki but it seems the function index is different from the local documentation ô_o. Thanks though. Wasn't sure it existed ^^;. I do understand the difference kwowing this, now :)

Well, thanks for all that ^^;... I kinda came in with my load of trouble, didn't I ^^;



New question : is there any way to queue up animations without any interaction between them?

Code: Select all

p "I think I'm..."
show phoenix cough
$ renpy.no_interaction_pause(0.4)
show phoenix cough2
$ renpy.no_interaction_pause(0.4)
p "... sick."
Again, trying to keep the text + window on screen during the animation. I suppose I could use SMAnimation for this (one edge per other smaller animation), but I'm not sure it's the most efficient way of doing this :/.


Some other way of seeing this would be to ask for "animation_end" events/callbacks... but hm... Not sure this is that great in the end.

User avatar
PyTom
Ren'Py Creator
Posts: 15423
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#14 Post by PyTom » Tue Feb 20, 2007 12:09 pm

monele wrote:About the graphical glitch : so uhhh... in conclusion, it's fixed and just as fast as before... but only on PCs?
Right. It's fixed on the mac, but not as fast as before.
And the "workaround" you talk about is not necessary then, I take it.
Well, it's necessary in 6.0, but won't be necessary in 6.1.
New question : is there any way to queue up animations without any interaction between them?
No, or at least, not anytime soon.

Well, you could try making an Animation out of two other animations, but I'm not sure the times would work out on that.

Oh, I think the reason the evidence record terminates the current dialogue is that you return True from that function. Try returning None instead, and you should stay on the same screen.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
"Silly and fun things are important." - Elon Musk
Software > Drama • https://www.patreon.com/renpytom

User avatar
monele
Lemma-Class Veteran
Posts: 4101
Joined: Sat Oct 08, 2005 7:57 am
Location: France
Contact:

#15 Post by monele » Tue Feb 20, 2007 12:52 pm

No, or at least, not anytime soon.

Well, you could try making an Animation out of two other animations, but I'm not sure the times would work out on that.
Yeah... I'll be trying something with SMAnimations but it's tough o_O...

Code: Select all

show white with Pause(0.2)
hide white
hide phoenix
show phoenix cough
with Pause(0.25)
hide phoenix
show phoenix cough2
with Pause(0.25)
# and *now* we go on with the text @_@
I noticed I had to hide a tagname for animations to start from the beginning. I'm pretty sure it's a known problem but I'm not sure if it's the right workaround anymore ^^;
Oh, I think the reason the evidence record terminates the current dialogue is that you return True from that function. Try returning None instead, and you should stay on the same screen.
I'm so dumb!! @_@... Thanks for spotting that one... (I don't know *why* I returned True there...)


EDIT : While trying for more animations, I tested the SMAnimation solution. Took me some time to realize the first state lasted.... nothing (is it me not doing it properly or is the initial state supposed to be transitioned from right away?)

Anyway, what really bothered me is that if you do this :

Code: Select all

$ someanim = Animation("1.png", 0.5, "2.png")
image final = anim.SMAnimation("s",
anim.State("s", None),
anim.State("a", "marilyn.png"),
anim.State("b", someanim),
anim.Edge("s", 1.0, "a"),
anim.Edge("a", 2.0, "b"),
anim.Edge("b", 1.0, "a")
)
You will first see "marilyn.png" for 1 second, and then see "2.png" for 2 seconds, and then back to marylin, etc... You'll *never* see "1.png".
I understand why (someanim follows its own timeline and has reached 2.png when it's shown by the SMAnimation... and won't move from that spot for eternity), but I expected to see someanim restart everytime, of course :).

While I *think* SMAnimation could be useful the way it is, there is still a need for a way to queue animations... One possibility could be some merging of two Animation objects, concatenating both. But in a way, this means I could also write this animation myself (except it won't reflect any change I make to the original animations automatically). Also, if the first animation has an everlasting last frame... we'll never see the second animation ^^;...

I could use SMAnimation along with *looping* animations timed properly :

Code: Select all

$ anim1 = Animation("something.png", 1.0, "something2.png, 5.0")
$ anim2 = Animation("this.png", 3.0, "that.png", 3.0)

image final = anim.SMAnimation("s",
anim.State("s", None),
anim.State("a", anim1),
anim.State("b", anim2),
anim.Edge("s", 2.0, "a"),
anim.Edge("a", 4.0, "b"),
anim.Edge("b", 2.0, "a")
)
(the last frame of each animation lasts for its usual time plus the total time of all subsequent animations).
Problem : I can't use anim1 and anim2 independantly. They're meant to go with that particular SMAnimation and I'll be redefining the same animations multiple times if I want to combine them.

Something nice could be some "MultipleAnimations" object that goes like this :

Code: Select all

$ anim1 = Animation("something.png", 1.0, "something2.png, 1.0")
$ anim2 = Animation("this.png", 3.0, "that.png", 2.0)

image final_loop = MultipleAnimations(anim1, 2.0, anim2, 5.0)
# loops forever between these two looping animations
image final_once = MultipleAnimations(anim1, 2.0, anim2)
# goes once through two looping animations and ends with the last animation looping forever.
Of course, it needs to restart each animation whenever it starts showing them, otherwise it's just a simplified SMAnimation. Does this seem possible?

Post Reply

Who is online

Users browsing this forum: No registered users