#SWB Traits in Actor sheet
1 messages ยท Page 1 of 1 (latest)
okay so, lemme get everything that relates to traits, as I have it
_onItemControl(event, variable="item") {
event.preventDefault();
// Obtain event data
const button = event.currentTarget;
const li = button.closest(".item");
const item = this.actor.items.get(li?.dataset.itemId);
// Handle different actions
switch ( button.dataset.action ) {
case "create":
const cls = getDocumentClass("Item");
return cls.create({name: game.i18n.localize("SIMPLE.ItemNew"), type: variable}, {parent: this.actor});
case "edit":
return item.sheet.render(true);
case "delete":
return item.delete();
}
}
this should be the script that create/edit/deletes it
So the first thing is to change your HTML to do #each over data.items
so like
<div class="tab traits" data-group="primary" data-tab="traits">
<ol class="trait-list">
{{#each data.items as |trait id|}}
<li class="trait flexrow" data-trait-id="{{trait._id}}">
<img src="{{trait.img}}" title="{{trait.name}}" width="24" height="24" />
<h4 class="trait-name">{{trait.name}}</h4>
{{!-- Iterate through all attributes on the trait and output buttons for any that are formula. --}}
<div class="trait-buttons">
{{#each trait.system.attributes as |traitAttr key|}}
{{#if traitAttr.dtype}}
{{#if traitAttr.isFormula}}
Yes - you should see ALL the items in your actor sheet.
i'll take a look
The second step is to add an #if to only process items that have a type of "trait" (if that is how you are marking things as traits instead of other types of items).
currently not showing
wait, forgot to save
yeah currently not showing
I wonder if i should just copy/paste the item script exactly
then modify it
hmm
That might be best.
You can keep the "|item id|" in your copied section too.
Once you see a list of items in your new tab, you can add an "if" statement to show all the traits in your Traits tab, and NOT show them in the Items tab.
okay
its currently showing all items, as per the items tab
and we can make new items in either tab
so i should add a filter somewhere
So second step is to add an #if into the template.
After {{#each data.items as |item id|}} you need to add a new line.
{{#if (eq item.type "trait") }}
and just before the matching {{/each}} you need to add {{/if}}
done
That should reduce the list of items to only those items with a type of "trait"
now it should show nothing i think
okay yep
its showing nothing but clicking "'create" will add anothing object to Items
so i just need to make the Create Item button on traits make a new object with the "trait" type
Yes.
You will need to add a similar #if to the Items tab to ignore items with a type of trait.
I'm just looking ๐
nws
Almost, you need to wrap not inside more parentheses.
{{#if (not (eq item.type "trait")) }}
(Edited, to put ( in front of not
okay and i think if i want to make a new item with type trait
then
html.find(".traits .item-control").click(this._onItemControl.bind(this, "trait"));
should work for this function
_onItemControl(event, variable="item") {
event.preventDefault();
// Obtain event data
const button = event.currentTarget;
const li = button.closest(".item");
const item = this.actor.items.get(li?.dataset.itemId);
// Handle different actions
switch ( button.dataset.action ) {
case "create":
const cls = getDocumentClass("Item");
return cls.create({name: game.i18n.localize("SIMPLE.ItemNew"), type: variable}, {parent: this.actor});
case "edit":
return item.sheet.render(true);
case "delete":
return item.delete();
}
}
uh oh seem to have screwed something in the html, sheet won't open at all
Have you changed the classes in your copied HTML to use "traits" and "trait" instead of "items" and "item"?
not yet
Only the class names need to change.
Your html.find is looking for an element with a class called "traits"
i think it doesn't like this
For your html.find, only the top <div class="tab items"... needs to be changed to have "tab traits" instead
<div class="tab traits" data-group="primary" data-tab="traits">
Yes, that will also get the "sheet tab navigation" working.
hmm
<ol class="item-list">
{{#each data.items as |item id|}}
{{#if (eq item.type "trait") }}
<li class="item flexrow" data-item-id="{{item._id}}">
...
<div class="item-controls">
<a class="item-control" title="{{ localize "SIMPLE.ItemEdit" }}" data-action="edit"><i class="fas fa-edit"></i></a>
<a class="item-control" title="{{ localize "SIMPLE.ItemDelete" }}" data-action="delete"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/if}}
{{/each}}
</ol>
this works fine
<ol class="item-list">
{{#each data.items as |item id|}}
{{#if (not(eq item.type "trait")) }}
<li class="item flexrow" data-item-id="{{item._id}}">
....
<div class="item-controls">
<a class="item-control" title="{{ localize "SIMPLE.ItemEdit" }}" data-action="edit"><i class="fas fa-edit"></i></a>
<a class="item-control" title="{{ localize "SIMPLE.ItemDelete" }}" data-action="delete"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/if}}
{{/each}}
</ol>
this causes an error
You might need a space after not, so #if (not (eq
ah yes that does it
so change the class of everything from item-X to trait-X?
with just the navigation change and this
html.find(".traits .item-control").click(this._onItemControl.bind(this, "trait"));
it doesn't work yet
If you change the <div> I mentioned above, then clicking your buttons should trigger your bind function.
.
this is what i currently have in the topmost div
Ok.
maybe the bind function isn't firing
Does your _onItemContrll trigger?
or maybe because the other onItemControl exists
in the basic SWB
it fires that instead
as SWB has
html.find(".item-control").click(this._onItemControl.bind(this));
Ah, I see SWB is hooking it onto all those buttons.
lemme log it
to see whats getting output
we should get "trait" as variable, it might be giving "item" though
I suppose changing the 'item-control' to 'trait-control' will prevent SWB from finding those buttons.
yeah
yep it just logs item
so that's not firing
so i'd need to change item-control here
<div class="item-controls">
<a class="item-control" title="{{ localize "SIMPLE.ItemEdit" }}" data-action="edit"><i class="fas fa-edit"></i></a>
<a class="item-control" title="{{ localize "SIMPLE.ItemDelete" }}" data-action="delete"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/if}}
{{/each}}
</ol>
<p>
<a class="item-control" title="{{ localize "SIMPLE.ItemCreate" }}" data-action="create"><i class="fas fa-plus"></i> {{ localize "SIMPLE.ItemCreate" }}</a>
</p>
</div>
So change both your HTML and your html.find to use "trait-control" as the class name.
to trait-control
did you also change it in your html.find call?
yes
html.find(".trait-control").click(this._onItemControl.bind(this, "trait"));
hmm
Oh, I would also change the name of the routine to _onTraitControl, so that your class isn't overriding the base function of the underlying SWB actor sheet class.
Is your html.find in the activateListeners(html) function of your actor sheet class?
yep
okay
now it is calling the function
but not making an item with the type "trait"
its still adding it to the "items" tab but not the traits tab
wait
i know what the problem is
It looks like the "Item" definition in template.json doesn't add a field called "type".
Can you add it to the attributes{} of the item?
probably
so you would check item.attributes.type in the {{#if statements too.
or maybe the "Groups" field?
where would i add it to attributes{} in here
_onTraitControl(event) {
console.log("here")
event.preventDefault();
// Obtain event data
const button = event.currentTarget;
const li = button.closest(".item");
const item = this.actor.items.get(li?.dataset.itemId);
// Handle different actions
switch ( button.dataset.action ) {
case "create":
const cls = getDocumentClass("Item");
return cls.create({name: game.i18n.localize("SIMPLE.ItemNew"), type: "item"}, {parent: this.actor});
case "edit":
return item.sheet.render(true);
case "delete":
return item.delete();
}
}
in the cls.create call.
return cls.create({
name: game.i18n.localize("SIMPLE.ItemNew"),
{attributes: { type: "item"}},
{parent: this.actor});
Ah true.
return cls.create({
name: game.i18n.localize("SIMPLE.ItemNew"),
type: "Item".
{attributes: { type: "trait"}},
{parent: this.actor});
in this case i should overwrite the normal one
because i'll need to add {attributes: { type: "item"}}, to the other one right?
otherwise {{#if (not (eq item.attributes.type "trait")) }} will cause a crash?
No need to add it to the other one.
If the attribute is missing, then it isn't a Trait ๐
huh okay i thought it would just crash because it can't perform the operation
I think the value "item.attributes.type" will be null or undefined, rather than crashing.
lets give
return cls.create({name: game.i18n.localize("SIMPLE.ItemNew"), type: "item"}, {attributes: { type: "trait"}}, {parent: this.actor});
a try
hmm
it makes an item but it doesn't show up in either Traits or Items
seems to be a difference between firing it in Traits and firing it in Items
You have } in wrong place:
return cls.create({name: game.i18n.localize("SIMPLE.ItemNew"), type: "item", attributes: { type: "trait"}}, {parent: this.actor});
oh
attributes wasn't being set.
I think the attributes part of the actor might be more complicated than just a string.
Ok. maybe best to set a FLAG on the item.
In your getData, loop over all the items, and set a new flag on each item called "isTrait" - and set value to item.getFlag('yourmodulename'. 'isTrait')
Then the HTML can simply check {{#if item.isTrait}} for traits, and
{{#if (not (item.isTrait)) }} for your items list.
async getData(options) {
const context = await super.getData(options);
EntitySheetHelper.getAttributeData(context.data);
context.shorthand = !!game.settings.get("worldbuilding", "macroShorthand");
context.systemData = context.data.system;
context.dtypes = ATTRIBUTE_TYPES;
context.biographyHTML = await TextEditor.enrichHTML(context.systemData.biography, {
secrets: this.document.isOwner,
async: true
});
return context;
}
context.items = ?
So before return context you can do:
for (const item of context.data.
items) {
item.isTrait = item.getFlag('yourmodulename', 'isTrait') || false
}
It is either context.items or context.data.items
Probably context.data.items
In your create routine, you can set the flag during creation:
return cls.create({
name: game.i18n.localize("SIMPLE.ItemNew"),
type: "item",
flags: { ['yourmodulename'] : { isTrait: true }} },
{parent: this.actor});
It needs to be stored in the right place for getFlag to find it ๐
No, I think the "Item" object has been cleansed of functions.
You could try accessing the flag field directly:
item.isTrait : item.flags?.['yourmodulename']?.isTrait
i think its failing here
async getData(options) {
const context = await super.getData(options);
for (const item of context.items) {
item.isTrait = item.getFlag('icon-145-sheet', 'isTrait') || false
}
return context;
}
hmm
maybe its better to edit the item object
to allow an attribute?
Replace the getFlag call with the direct access to item.flags?.['yourmodulename']?.isTrait
async getData(options) {
const context = await super.getData(options);
for (const item of context.items) {
item.isTrait : item.flags?.['icon-145-sheet']?.isTrait || false
}
return context;
}
?
Sorry, using = not :
async getData(options) {
const context = await super.getData(options);
for (const item of context.items) {
item.isTrait = item.flags?.['icon-145-sheet']?.isTrait || false
}
return context;
}
okay
Or should it be item of context.data.items ?
the sheet will open
but
if i try to make an item
i don't think SWB is set up to allow this
so will probably need to make a new item.js
that inherits the old + adds a bit for attributes or flags
That error indicates that the HTML template is not quite right.
With the change to getData, the HTML #if statements can be simplified
{{#if item.isTrait}} for your trait list
{{#if (not (item.isTrait)) }} for your item list
yes i have these
<ol class="item-list">
{{#each data.items as |item id|}}
{{#if (not (item.isTrait)) }}
<li class="item flexrow" data-item-id="{{item._id}}">
<ol class="item-list">
{{#each data.items as |item id|}}
{{#if item.isTrait }}
<li class="item flexrow" data-item-id="{{item._id}}">
unless the second one is space sensitive
no the space is supposed to be there
In the getData() function, can you use a breakpoint to check that the context.data.items has an isTrait field on every item?
In your browser/app, open the console with F12.
ye
Select the Sources tab, and on the left you see a directory tree of all the files.
There should be one that says "modules" or "modules/yourmodulename"
If you open that, you should find your .js source file there.
Click on that file, and the right pane changes to show your source file.
Find the correct line in the source file, then click on the line number of that line.
Put the breakpoint on the return statement, not the line you showed above.
The controls are on the left at the top of your picture - the right-arrow causes the debugger to resume control.
Ok, in the bottom right panel, open up the "items" array, and see if each entry has a flags
As well as the flags, does each item have an "isTrait" property
yes
all are set to true
a bit weird because i thought this script default set to false
oh well
its only the one that i added items to before that is broken hmm
new actor works fine
but once i add an item
close
and try to reopen
it breaks
yeah okay
i made one in the items tab (shows) has isTrait: false
made one in the traits tab (doesn't show), in the console it says its there with isTrait: true
closed the actor
try to open the actor, error
interesting
i can add items to Items and close without it erroring when i reopen
but if i try to add items to Traits, not only will it not show, but it causes an error when i reopen
Does the breakpoint trigger immediately on opening the actor sheet?
yes
well
opening one of these broken actor sheets at least
with a fresh one it also triggers immediately
Ah, maybe one of it {{#if is wrong:
It probably should be
{{#if (not item.isTrait) }}
(without the extra parantheses after not
that one isn't the broken one though
i can add any number of non-traits and it won't break
but if i add a trait it will
hmm
i'll try
oh
that was it
there's a couple issues like the css being off for some random reason
but otherwise appears to work
The simple.css has a section:
.worldbuilding .item-list .item-controls {
flex: 0 0 36px;
}
which you will have to duplicate to set the same for .trait-list
ah right
You won't be able to create Items as Traits in the world without some more code to set the Traits flag on a created item.
should probably get that done
Maybe a macro is the simplest way, just set that flag on the currently open Item.
But that is a separate task - the first task is getting your Actor sheet working ๐
yeah i fixed the css
all my other things broke but i can fix those later
wait
no
they didn't break
lol
yeah just forgot how i set up my own things lmao
oki thanks very much
I don't really need to make new traits outside the sheet anyway
(at least at this point in time)
(I'll probably come back to regret this later)
Write a basic macro now that sets the flag, just for future reference ๐
Neither do I. but just use a macro to remember the setFlag call that you need to make.
Just as a Note, so you can come back later and it will remind you what you need to do.
ok, thanks a lot Farling!