Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,10 @@ type CharacterProperties = {
_defaulttoken: string;
};

declare type Roll20Character = Prettify<Roll20Object<CharacterProperties>>;
declare type Roll20Character = Prettify<Roll20Object<CharacterProperties> & {
/** Experimental: legacy Roll20 attributes vs Beacon sheet */
sheetEnvironment?: "legacy" | "beacon";
}>;

// Attribute type with proper properties
type AttributeProperties = {
Expand Down Expand Up @@ -554,7 +557,11 @@ declare function getAllObjs(): Roll20Object<AnyRoll20Object>[];
*/
declare function getAttrByName(character_id: string, attribute_name: string, value_type?: "current" | "max"): string;

type SheetItemOptions = { allowThrow?: boolean };
type SheetItemOptions = {
allowThrow?: boolean,
createAttr?: boolean,
withWorker?: boolean
};
type SheetItemValueTYpe = "current" | "max";

/**
Expand Down
Binary file not shown.
95 changes: 95 additions & 0 deletions libSmartAttributes/0.0.4/libSmartAttributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// libSmartAttributes v0.0.4 by GUD Team | libSmartAttributes provides an interface for managing beacon attributes in a slightly smarter way.
var libSmartAttributes = (function () {
'use strict';

async function getAttribute(characterId, name, type = "current") {
// Try for a legacy attribute or beacon computed
const attr = await getSheetItem(characterId, name, type);
if (attr !== null && attr !== undefined) {
return attr;
}
// Then try for the user attribute
const userAttr = await getSheetItem(characterId, `user.${name}`, type);
if (userAttr !== null && userAttr !== undefined) {
return userAttr;
}
log(`Attribute ${name} not found on character ${characterId}`);
return undefined;
}
async function setAttribute(characterId, name, value, type = "current", options) {
try {
await setSheetItem(characterId, name, value, type, {
allowThrow: true,
createAttr: options?.noCreate === undefined ? true : !options.noCreate,
withWorker: options?.setWithWorker === undefined ? true : options.setWithWorker
});
return true;
}
catch (e) {
// throw will happen on beacon sheets if the computed doesn't exist or is read-only
switch (e.type) {
// for read only computeds, we don't want to make a shadow "user." version.
case "COMPUTED_READONLY":
return false;
}
}
// Then default to a user attribute
try {
await setSheetItem(characterId, `user.${name}`, value, type, {
allowThrow: true,
createAttr: options?.noCreate === undefined ? true : !options.noCreate,
withWorker: options?.setWithWorker === undefined ? true : options.setWithWorker
});
return true;
}
catch {
return false;
}
}
async function deleteAttribute(characterId, name, type = "current") {
const character = getObj("character", characterId);
if (!character) {
return false;
}
if (character?.sheetEnvironment === "legacy" || character?.sheetEnvironment === undefined) {
const legacyAttr = findObjs({
_type: "attribute",
_characterid: characterId,
name: name,
})[0];
if (legacyAttr) {
legacyAttr.remove();
return true;
}
return false;
}
// Beacon computeds cannot be deleted (no change to the computed value).
const beaconAttr = await getSheetItem(characterId, name, type);
if (beaconAttr !== null && beaconAttr !== undefined) {
return false;
}
// Then try for the user attribute
const userAttr = await getSheetItem(characterId, `user.${name}`, type);
if (userAttr !== null && userAttr !== undefined) {
try {
await setSheetItem(characterId, `user.${name}`, undefined, type, {
allowThrow: true,
createAttr: false
});
return true;
}
catch {
return false;
}
}
return false;
}
var index = {
getAttribute,
setAttribute,
deleteAttribute,
};

return index;

})();
3 changes: 2 additions & 1 deletion libSmartAttributes/script.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "libSmartAttributes",
"version": "0.0.3",
"version": "0.0.4",
"description": "libSmartAttributes provides an interface for managing beacon attributes in a slightly smarter way.",
"authors": "GUD Team",
"roll20userid": "8705027",
Expand All @@ -10,6 +10,7 @@
"script": "libSmartAttributes.js",
"useroptions": [],
"previousversions": [
"0.0.3",
"0.0.2",
"0.0.1"
]
Expand Down
Binary file added libSmartAttributes/src/.index.ts.swo
Binary file not shown.
88 changes: 58 additions & 30 deletions libSmartAttributes/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@ async function getAttribute(
};

type SetOptions = {
setWithWorker?: boolean;
noCreate?: boolean;
};

type SheetItemError = Error & {
type: string;
details?: Record<string, unknown>;
};


async function setAttribute(
characterId: string,
name: string,
Expand All @@ -34,54 +41,75 @@ async function setAttribute(
) {

try {
await setSheetItem(characterId, name, value, type, {allowThrow: true});
return;
} catch {
await setSheetItem(characterId, name, value, type, {
allowThrow: true,
createAttr: options?.noCreate === undefined ? true : !options.noCreate,
withWorker: options?.setWithWorker === undefined ? true : options.setWithWorker
});
return true;
} catch (e) {
// throw will happen on beacon sheets if the computed doesn't exist or is read-only
}

// Guard against creating user attributes if noCreate is set
if (options?.noCreate) {
log(`Attribute ${name} not found on character ${characterId}, and noCreate option is set. Skipping creation.`);
return;
switch((e as SheetItemError).type){
// for read only computeds, we don't want to make a shadow "user." version.
case "COMPUTED_READONLY":
return false;
}
}

// Then default to a user attribute
setSheetItem(characterId, `user.${name}`, value, type);
return;
try {
await setSheetItem(characterId, `user.${name}`, value, type, {
allowThrow: true,
createAttr: options?.noCreate === undefined ? true : !options.noCreate,
withWorker: options?.setWithWorker === undefined ? true : options.setWithWorker
});
return true;
} catch {
return false;
}
};

async function deleteAttribute(characterId: string, name: string, type: AttributeType = "current") {
// Try for legacy attribute first
const legacyAttr = findObjs({
_type: "attribute",
_characterid: characterId,
name: name,
})[0];

if (legacyAttr) {
legacyAttr.remove();
return;
const character = getObj("character",characterId);
if(!character) {
return false;
}

if (character?.sheetEnvironment === "legacy" || character?.sheetEnvironment === undefined) {
const legacyAttr = findObjs({
_type: "attribute",
_characterid: characterId,
name: name,
})[0];

if (legacyAttr) {
legacyAttr.remove();
return true;
}
return false;
}

// Then try for the beacon computed
// Beacon computeds cannot be deleted (no change to the computed value).
const beaconAttr = await getSheetItem(characterId, name, type);
if (beaconAttr !== null && beaconAttr !== undefined) {
log(`Cannot delete beacon computed attribute ${name} on character ${characterId}. Setting to undefined instead`);
setSheetItem(characterId, name, undefined, type);
return;
return false;
}

// Then try for the user attribute
const userAttr = await getSheetItem(characterId, `user.${name}`, type);
if (userAttr !== null && userAttr !== undefined) {
log(`Deleting user attribute ${name} on character ${characterId}`);
setSheetItem(characterId, `user.${name}`, undefined, type);
return;
try {
await setSheetItem(characterId, `user.${name}`, undefined, type, {
allowThrow: true,
createAttr: false
});
return true;
} catch {
return false;
}
}

log(`Attribute ${type} not found on character ${characterId}, nothing to delete`);
return;
return false;
};

export default {
Expand Down
4 changes: 2 additions & 2 deletions libSmartAttributes/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
declare namespace SmartAttributes {
function getAttribute(characterId: string, name: string, type?: "current" | "max"): Promise<string | number | undefined>;
function setAttribute(characterId: string, name: string, value: unknown, type?: "current" | "max", options?: { setWithWorker?: boolean, noCreate?: boolean }): Promise<void>;
function deleteAttribute(characterId: string, name: string, type?: "current" | "max", options?: { setWithWorker?: boolean }): Promise<void>;
function setAttribute(characterId: string, name: string, value: unknown, type?: "current" | "max", options?: { setWithWorker?: boolean, noCreate?: boolean }): Promise<boolean>;
function deleteAttribute(characterId: string, name: string, type?: "current" | "max"): Promise<boolean>;
}
Loading
Loading