Okay. Finally getting somewhere with this... nowhere nice, but somewhere.
For some reason I can't figure out, when you test
if persistent.AchUnlocked is None:
during
init python:
, the result is TRUE... even if a value has be loaded into
persistent.AchUnlocked
during the previous playthrough. I can sort of explain it, but I'd rather stick with things I can prove at this stage...
If I use
if persistent.AchUnlocked is None:
or
default persistent.AchUnlocked = []
, the variable is ALWAYS initialized, regardless of any previous values.
I (eventually) tested this by running my test script, then removed my
default persistent.AchUnlocked = []
line from the code before running it again. With the line removed, the persistent values were retained. (The same is true for the
if persistent.AchUnlocked is None:
check too, before anyone jumps on that).
A quick look in the
log.txt
file and I found:
Code:
AttributeError: 'StoreModule' object has no attribute 'Achiev'
I came to the conclusion that because
AchUnlocked
is a list of
Achiev()
objects, there was somehow a problem during initialization referencing a class that doesn't actually exist yet.
I tried creating the class during
init -1 python:
. That didn't work.
I then found
python early:
... and that does work. If you create the
Achiev()
class during "python early", it lets you use any method you like to initialize the array/list in anyway you like. And yes,
default persistent.AchUnlocked = []
works just fine.
Now you start to run into more problems. As long as you don't quit out from the game, everything runs fine. But when you do exit the game completely, the next time it runs it effectively creates a new
AchievList
array. Well, not exactly new... but stored at a different location in memory. As a result, when you check to see if the achievement has already unlocked, even though the values are the same, the memory location is different and the test fails
(at least that is what I assume is happening)... which results in the same achievement being added to the unlocked list again... and again... and again... and again, depending on how many times you run that code.
Let me come back to that one...
Meanwhile, there's another issue. Because of the way you are initializing the
AchievList
, it changes the value of an already existing variable... which results in that variable being saved as part of the save game data. So currently there are two possible achievements. If you run the game and save, those two values are saved. Then, if later, you add a third and fourth achievement but load a saved game from when only two existed... those 3rd and 4th will be lost for that playthrough.
The answer is to NOT save
AchievList
by making it a constant. That is... use
define
and NEVER change the values while the game is running. Nice and simple...
Python:
define AchievList = [
Achiev("Achiev0name",Image("Achiev0.jpg"),"Achiev0 desc1","Achiev0 desc2"),
Achiev("Achiev1name",Image("Achiev0.jpg"),"Achiev1 desc1","Achiev1 desc2"),
]
If you do already have some saves, you may want to delete them - since they may already include
AchievList
.
Again, the problem here is that each time the game is run from scratch, the lists are stored in different locations in memory
(again, I assume) and python/RenPy cares about that when it does checks like
if not AchievList[n] in persistent.AchUnlocked:
.
The answer
(in my opinion) is to match the achievement name rather than doing a direct compare of the
Achiev()
objects.
This seems to work for me:
Python:
def achiecall(n,sex): # sex is ignored here, because I don't know how it is used.
foundmatch = False
for x in persistent.AchUnlocked:
if x.name == AchievList[n].name:
foundmatch = True
if not foundmatch:
persistent.AchUnlocked.append( AchievList[n] )
My concern is that the achievement numbers could change over time. So I think a better way would be to add achievements based on their name rather than their array number.
My final test script looks like this:
Python:
python early:
class Achiev:
def __init__(self, name, thumb, desc1, desc2):
self.name = name
self.thumb = thumb #thumbnail image of the gallery
self.desc1 = desc1
self.desc2 = desc2
def __eq__(self, other): #to compare Achievs, without it gives error
if not isinstance(other, Achiev):
return False
return self.name is other.name
init python:
def achiecall(achname,sex): # sex is ignored here, because I don't know how it is used.
for ach in AchievList:
if achname == ach.name:
foundmatch = False
for x in persistent.AchUnlocked:
if x.name == ach.name:
foundmatch = True
if not foundmatch:
persistent.AchUnlocked.append( ach )
gallery_page = 0
define AchievList = [
Achiev("Achiev0name",Image("Achiev0.jpg"),"Achiev0 desc1","Achiev0 desc2"),
Achiev("Achiev1name",Image("Achiev0.jpg"),"Achiev1 desc1","Achiev1 desc2"),
]
default persistent.AchUnlocked = []
label start:
scene black with fade
"Start:.... "
$ templen = len(persistent.AchUnlocked)
if templen > 0:
$ tempach = persistent.AchUnlocked[0].name
else:
$ tempach = "<not yet set>"
"AchUnlocked is [templen] objects. The first achievement is [tempach]."
label add_achievment:
menu:
"Unlocked Achievement ZERO":
$ achiecall("Achiev0name",3)
"Unlocked Achievement 'Achiev0name'."
jump add_achievment
"Unlocked Achievement ONE":
$ achiecall("Achiev1name",2)
"Unlocked Achievement 'Achiev1name'."
jump add_achievment
"I'm finished here":
pass
$ templen = len(persistent.AchUnlocked)
if templen > 0:
$ tempach = persistent.AchUnlocked[len(persistent.AchUnlocked)-1].name
else:
$ tempach = "<not yet set>"
"AchUnlocked is [templen] objects. The last achievement is [tempach]."
menu:
"Delete achievements before ending?"
"No":
pass
"Yes (reset)":
$ persistent.AchUnlocked = []
"Yes (full delete)":
$ del persistent.AchUnlocked
"NOTE: Game will now exit (otherwise errors happen)."
$ renpy.quit()
"*** THE END ***"
return
Part of me thinks that I too am missing something here. Probably something to do with
def __eq__(self, other):
and how classes are compared. But honestly, other than knowing that python create pointers to other variables when you do stuff like
$ x = y
, where
x
is just a pointer/reference to
y
, I'm so far out of my depth.
In addition, part of me wants to say that the achievement system should be some form of
You must be registered to see the links
rather than a list. But that is taking me even further out of my depth.
Perhaps
anne O'nymous or
Rich could correct my thinking.
Edit: The more I think about this, the more I'm doubting my "it's something to do with memory locations" things. Maybe someone could take a closer look at:
Python:
def __eq__(self, other): #to compare Achievs, without it gives error
if not isinstance(other, Achiev):
return False
return self.name is other.name
... when used in relation to something like...
Python:
def achiecall(n,sex):
if not AchievList[n] in persistent.AchUnlocked:
persistent.AchUnlocked.append(AchievList[n])