PDA

View Full Version : GM and Player Both Looking at Character Sheet?



PneumaPilot
March 6th, 2009, 17:33
I've been having a problem with a little code that I wrote for the New World of Darkness ruleset that checks to see whether a character sheet has a template selected when it draws. If there is no template selected (because it's a new character), then the code selects the 'Mortal' template by default. Once a player chooses another template, then, of course, the code doesn't fire. All of this works fine if a player is making a new character, of if the Storyteller (GM) works on someone's character sheet when they are not connected or have their sheet closed, but problems arise when both player and Storyteller open the sheet at the same time. When this happens, any template selection is erased. The mortal template is not reselected. Nothing is selected. Now, this is easily fixed by just clicking the template bead again, but this has to be done every time I as a Storyteller open the sheet while the players are online. That gets old fast, and I don't like bugs in my code.

Here's the code snippet. Maybe someone can suggest how to put in some king of check that would cancel out this freak behavior. This is the onInit function of the mortal bead on the character sheet. If it detects that no other beads are selected, it selects itself.


function onInit()

setIcon(stateicons[1].off[1]);



if not sourceless and window.getDatabaseNode() then

-- Get value from source node

if sourcename then

source = window.getDatabaseNode().createChild(sourcename[1], "number");

else

source = window.getDatabaseNode().createChild(getName(), "number");

end

if not (window.isvampire.getState() or window.iswerewolf.getState() or window.ismage.getState() or window.ispromethean.getState() or window.ischangeling.getState() or window.ishunter.getState() or window.isspirit.getState()) then

setState(true);

end

if source then

source.onUpdate = update;

update();

end

else

-- Use internal value, initialize to checked if <checked /> is specified

if checked then

sourcelessvalue = true;

update();

end

end

end

Moon Wizard
March 6th, 2009, 18:45
A couple thoughts that might help you:

* Any time that you are working with a window which is visible to both clients and hosts, you have to be very careful, especially in the onInit function. Each time the window is opened, the onInit function will execute, whether it's opened on the client or the host. This makes it very easy for subsequent onInit calls to stomp on changes made by previous onInit calls. I don't see anything that seems to be causing the issue, but the update function is called and not shown.

* Whenever I need to make a change to a field/control during initialization that depends on other fields, I put it in the parent window's onInit function, which is called after all the child controls/fields are initialized.

Cheers,
JPG

PneumaPilot
March 6th, 2009, 18:52
Thanks moon_wizard. I will indeed change the code around to the parent window's onInit function if I can't work an easy fix here.

Question: do you think that the first line of the code (setting the state icon to off) might have anything to do with this?

PneumaPilot
March 6th, 2009, 18:54
Nevermind, that was stupid...

I've been out of this for a while...

PneumaPilot
March 7th, 2009, 19:40
Here's my update function for the same control. I don't see anything that might cause the weirdness, but maybe I'm missing something.


function update()
if source then
if source.getValue() ~= 0 then
setIcon(stateicons[1].on[1]);
window.ishunter.setState(false);
window.ismage.setState(false);
window.isvampire.setState(false);
window.iswerewolf.setState(false);
window.ischangeling.setState(false);
window.ispromethean.setState(false);
window.isspirit.setState(false);
window.wodlogo.setIcon("WODText");
window.setFrame("charsheetmortal");
window.attributeslabel.setFont("sheetlabelwhite");
window.skillslabel.setFont("sheetlabelwhite");
window.advantageslabel.setFont("sheetlabelwhite");
window.faction.update();
window.profession.update();
window.clan.update();
window.auspice.update();
window.path.update();
window.seeming.update();
window.lineage.update();
window.covenant.update();
window.groupname.update();
window.tribe.update();
window.refinement.update();
window.order.update();
window.kith.update();
window.compactconspiracy.update();
window.morality.setValue("Morality");
else
setIcon(stateicons[1].off[1]);
end
else
if sourcelessvalue then
setIcon(stateicons[1].on[1]);
else
setIcon(stateicons[1].off[1]);
end
end

if self.onValueChanged then
self.onValueChanged();
end
end

Moon Wizard
March 8th, 2009, 00:58
I had a similar function that I had a problem with on a "reach" field:


function onInit()
if getValue() == 0 then
setValue(1);
end
super.onInit();
end


When I created a window by dropping an NPC on the combat tracker, the drop function would create the window, then update the fields to match the dropped NPC. If the client had the combat tracker window open also, then the reach field always got set to 1.

My belief is that the code executes in an interrupt fashion. (i.e. creating a window will trigger the onInit before the createWindow function returns and the rest of the code is executed.)

Also, I believe that the values written to and read from the database will not be synchronized until after both the host and client complete the onInit call. This means that you can easily run into a situation where the client and host are writing over each other's data.

In my case, I was able to limit this code to host only, since only the host could add.

However, in your case, it seems like you are running into something similar but can't limit to one side or the other, though I can't see the problem just from reviewing what you posted.

If I was working on it, the first thing I would do is to move the code for the all the updates into the parent window, then make it so that clicking on the creature type button made a parent window call. By centralizing the code into one location, you can prevent onInit calls for multiple child controls from stomping on each other. Also, you might look into storing the type in a hidden string field. See the combattracker_entry object in the 4E_JPG ruleset as an example.

Cheers,
JPG

PneumaPilot
March 8th, 2009, 01:46
Well what about something else? I mean, all I want to do is have the 'mortal' sheet selected by default. I put the code there in the first place because when a player created a new character, there would be no beads selected and so all of the controls from the various templates were being written over one another. If the player had no idea what to do, he might freak out. So, all I need is a default template to be selected upon character creation.

Moon Wizard
March 8th, 2009, 04:02
Another way to do it that I used for the NPC creature types in the 4E_JPG ruleset is that the default fields are visible by default. Then the onInit function can change the display based on what values it finds.

Cheers,
JPG

Brenn
March 8th, 2009, 07:18
I recently had a very similar problem, Pneuma. Mine wasn't occuring in the OnInit, it was when the hit location chart was changed when both the host and client had the window open. I had to force both windows to close if a change occured. This is fine in my situation (because it arises rarely), in yours however it is definitely not an acceptable solution. I think this might be related to there being database linked controls on the sheet more than anything else. That is just supposition on my part, however.

PneumaPilot
March 9th, 2009, 14:39
Brenn, I definitely think it has to do with the database. Here's what I would like to do. I would just like a simple 'if' statement to turn off that particular code if the GM is the one opening the sheet. Does anyone know how to do that? I've not ever messed with that kind of thing.

Tenian
March 9th, 2009, 14:59
The User object has methods that are useful for this type of thing. Below is an example that prints a different value for the Host (GM/Dm/StoryTeller) and the client (player/victim)



if User.isHost() then
print("foo");
else
print("bar");
end


You can find more detail in the library:
https://www.fantasygrounds.com/refdoc/User.xcp

PneumaPilot
March 9th, 2009, 15:35
Perfect, thanks. I'll implement it immediately and see if it fixes the problem.

PneumaPilot
March 9th, 2009, 17:06
Hmm, here's an interesting behavior that makes me think we're getting closer to solving the problem: I edited the script to not perform the template check if the user was the host. What I found was that if the player opens the character sheet first and then the GM opens it too, nothing destructive happens, because the code is being skipped. However, if the GM opens the sheet first and then the player, the bead gets deselected again.

So, I guess what I really need to do is to find a way to not engage this code if someone already has the sheet open. Is that possible?

Additionally, I would like to understand more fully why the program is doing this. What's going on in the database when one person is already connected to the source that causes that simple code to freak out?

PneumaPilot
March 9th, 2009, 17:10
Is it because I'm trying to access a property of the window in the check? Should I be trying to access the database instead? For those just joining us, here's the problem code snippet:


if not (window.isvampire.getState() or window.iswerewolf.getState() or window.ismage.getState() or window.ispromethean.getState() or window.ischangeling.getState() or window.ishunter.getState() or window.isspirit.getState()) then

setState(true);

end

PneumaPilot
March 9th, 2009, 22:23
Well, I fixed this problem. It's probably not the greatest solution in the world, but it works, and works perfectly, so that's all I care about. There are 8 template buttons at the top of the character sheet. I just moved the test to see if one was selected from the first one to the last one, since I figured that maybe the reason why it was messing up was because the first control was initializing before the last one. Whether this was the reason or not, it works now. I've tested every possibility. This is no longer a bug. Awesome! Thanks for all the help guys!

Foen
March 10th, 2009, 07:25
This is one of the main reasons why you should really try to perform the check in a window-level script. The onInit event for a window isn't called until after all the child objects have been initialised.

For controls, there is no guarantee of the order in which they fire, although it seems to be the order in which they appear in the window definition.

Foen

PneumaPilot
March 10th, 2009, 13:51
Well, it's only one line of code, so it would be easy to move to the window-level onInit. I wouldn't even have to change the wording at all.