This is a placeholder to test use as an all-media loader for ease of prototyping. Seems gradio HTML doesnt support loading local assets or my file path is wrong
"""
#-------------------------#-------------------------#-------------------------#-------------------------
playcanvasstorymanager = """
// storyManager.js
var StoryManager = pc.createScript('storyManager');
StoryManager.attributes.add('storyConfig', {
type: 'asset',
assetType: 'json',
title: 'Story Config'
});
// Initialize function
StoryManager.prototype.initialize = function() {
this.currentState = null;
this.storyData = null;
console.log('Initializing StoryManager');
if (this.storyConfig) {
// Primary Path: Asset assigned via script attributes
console.log('Loading storyConfig asset via script attributes:', this.storyConfig);
this.app.assets.load(this.storyConfig);
this.storyConfig.ready(function () {
if (this.storyConfig.resource) {
console.log('storyConfig.json loaded successfully via script attributes:', this.storyConfig.resource);
this.storyData = this.storyConfig.resource;
this.startStory('arrival'); // Starting state
} else {
console.error('Failed to load storyConfig.json via script attributes.');
}
}.bind(this));
} else {
// Fallback Path: Programmatically find and load 'storyConfig.json'
console.warn('Story Config asset is not assigned via script attributes. Attempting programmatic loading.');
var assetName = 'storyConfig.json'; // Ensure this matches exactly with the asset name in PlayCanvas
var asset = this.app.assets.find(assetName);
if (asset) {
console.log('Found asset programmatically:', assetName);
if (!asset.loaded) {
console.log('Asset not loaded. Initiating load...');
this.app.assets.load(asset);
} else {
console.log('Asset already loaded.');
}
asset.ready(function () {
if (asset.resource) {
console.log('storyConfig.json loaded successfully programmatically:', asset.resource);
this.storyData = asset.resource;
this.startStory('arrival'); // Starting state
} else {
console.error('storyConfig.json resource is undefined after loading.');
}
}.bind(this));
} else {
console.error('storyConfig.json not found in assets programmatically.');
// Optionally, you can implement further fallback mechanisms or alert the user
}
}
// Bind events regardless of asset loading method
this.bindEvents();
};
// Function to start or transition to a new state
StoryManager.prototype.startStory = function(stateKey) {
console.log('Starting story state:', stateKey);
if (!this.storyData) {
console.error('Story data is not loaded yet.');
return;
}
var state = this.storyData['singingCompetition'][stateKey];
if (!state) {
console.error('State not found in storyData:', stateKey);
return;
}
this.currentState = stateKey;
this.displayState(state);
};
// Function to display the current state
StoryManager.prototype.displayState = function(state) {
console.log('Displaying state:', this.currentState);
// Emit an event to update the UI
this.app.fire('story:display', {
description: state.description,
choices: state.choices
});
};
// Function to handle choices
StoryManager.prototype.makeChoice = function(choice) {
console.log('Making choice:', choice);
var current = this.storyData['singingCompetition'][this.currentState];
var nextStateKey = current.transitions[choice];
if (nextStateKey) {
this.startStory(nextStateKey);
} else {
console.error('Transition not found for choice:', choice);
}
};
// Listen for choice selections from UI
StoryManager.prototype.onChoiceSelected = function(choice) {
this.makeChoice(choice);
};
// Bind the choice selection event
StoryManager.prototype.bindEvents = function() {
console.log('Binding UI choice selection event');
this.app.on('ui:choiceSelected', this.onChoiceSelected, this);
};
"""
playcanvasuimanager = """
var UIManager = pc.createScript('uiManager');
UIManager.attributes.add('descriptionElement', { type: 'entity' });
UIManager.attributes.add('choicesContainer', { type: 'entity' });
UIManager.attributes.add('choiceButtonPrefab', { type: 'asset', assetType: 'template' });
UIManager.attributes.add('buttonSpacing', { type: 'number', default: 10 });
UIManager.attributes.add('buttonWidth', { type: 'number', default: 200 });
UIManager.attributes.add('buttonHeight', { type: 'number', default: 50 });
UIManager.prototype.initialize = function() {
console.log('Initializing UIManager');
this.app.on('story:display', this.onStoryDisplay, this);
this.validateSetup();
testsituation = {
description: 'Test description',
choices: ['Choice 1', 'Choice 2', 'Choice 3']
}
configstart = {
"singingCompetition": {
"arrival": {
"description": "You arrive at the grand auditorium, the stage illuminated and buzzing with excitement. Contestants and spectators alike fill the hall, eager for the night's performances.",
"events": [],
"choices": ["register for the competition", "explore the backstage area", "sit in the audience"],
"transitions": {
"register for the competition": "registration",
"explore the backstage area": "backstage",
"sit in the audience": "audienceSeat"
},
"media": [],
"developernotes": []
},
},
}
// Test event (remove in production)
setTimeout(() => {
this.app.fire('story:display', configstart["singingCompetition"]["arrival"]); //, testsituation);
}, 2000);
};
UIManager.prototype.validateSetup = function() {
if (!this.descriptionElement) {
console.error('Description Element is not assigned');
}
if (!this.choicesContainer) {
console.error('Choices Container is not assigned');
}
if (!this.choiceButtonPrefab) {
console.error('Choice Button Prefab is not assigned');
} else if (this.choiceButtonPrefab.type !== 'template') {
console.error('Choice Button Prefab must be a template asset');
}
};
UIManager.prototype.onStoryDisplay = function(data) {
console.log('UIManager received story:display event:', data);
// if (!data || !data.description || !Array.isArray(data.choices)) {
// console.error('Invalid data received in story:display event');
// return;
// }
this.updateDescription(data.description);
this.updateChoices(data.choices);
};
UIManager.prototype.updateDescription = function(description) {
if (this.descriptionElement && this.descriptionElement.element) {
this.descriptionElement.element.text = description;
console.log('Description updated');
} else {
console.warn('Unable to update description: Invalid Description Element');
}
};
UIManager.prototype.updateChoices = function(choices) {
if (!this.choicesContainer || !this.choiceButtonPrefab) {
console.error('Unable to update choices: Missing Choices Container or Choice Button Prefab');
return;
}
// Clear previous choices
while (this.choicesContainer.children.length > 0) {
this.choicesContainer.children[0].destroy();
}
if (choices.length === 0) {
console.log('No choices available for this state.');
// Optionally, display a "Restart" or "Exit" button
return;
}
// Calculate total width of all buttons plus spacing
var totalWidth = choices.length * this.buttonWidth + (choices.length - 1) * this.buttonSpacing;
// Create new choice buttons
choices.forEach((choice, index) => {
var button = this.choiceButtonPrefab.resource.instantiate();
this.choicesContainer.addChild(button);
// Set button size
if (button.element) {
button.element.width = this.buttonWidth;
button.element.height = this.buttonHeight;
}
// Position the button
var xPosition = (index * (this.buttonWidth + this.buttonSpacing)) - (totalWidth / 2) + (this.buttonWidth / 2);
button.setLocalPosition(xPosition, 0, 0);
var buttonText = button.findByName('ButtonText');
if (buttonText && buttonText.element) {
buttonText.element.text = choice;
}
var buttonComponent = button.button;
if (buttonComponent) {
buttonComponent.on('click', () => {
console.log('Choice selected:', choice);
this.app.fire('ui:choiceSelected', choice);
});
} else {
console.warn(`Button component missing for choice: ${choice}`);
}
});
console.log('Choices updated');
};
"""
ExampleTwineFileStructureasInitialPrompt = """:: StoryData
{
"ifid": "12345678-9abc-def0-1234-56789abcdef0",
"format": "SugarCube",
"format-version": "2.35.0",
"start": "Startup"
}
:: Startup [startup]
<>
<>
<>
<>
<>
<>
<>
<>
<>
<>
Welcome to the story!
This time, your character’s **stats** will matter. Your decisions will affect these metrics, which will shape the final outcome.
[[Choose Your Role|Choose Role]]
:: Choose Role
You can experience the upcoming events as one of three roles:
* [[Be a Guard|SetGuard]]
* [[Be a Merchant|SetMerchant]]
* [[Be a Villager|SetVillager]]
:: SetGuard
<>
<>
<>
You don the Guard’s mantle. Your starting stats:
**Reputation:** 5
**Authority:** 5
<>
:: SetMerchant
<>
<>
<>
You take the role of a Merchant. Your starting stats:
**Wealth:** 10
**Security:** 3
<>
:: SetVillager
<>
<>
<>
You become a Villager. Your starting stats:
**Knowledge:** 2
**Trust:** 5
<>
:: Market Riot
The marketplace is in chaos. Shouts and screams echo between the stalls.
<>
You see your fellow guards struggling to hold the line. This is your moment to either prove your dedication or risk your standing.
**Current Stats:** Reputation: $guardReputation, Authority: $guardAuthority
<>
<>
Overturned carts and scattered goods mean losses. How you respond could preserve or squander your wealth and security.
**Current Stats:** Wealth: $merchantWealth, Security: $merchantSecurity
<>
<>
Your neighbors panic. How you handle this crisis could increase your knowledge of the town’s issues or build trust among the people.
**Current Stats:** Knowledge: $villagerKnowledge, Trust: $villagerTrust
<>
[[Observe more details|Market Details]]
[[View Stats|Stats]]
:: Market Details
The riot intensifies. Guards form a barrier, vendors cry for help, and villagers seek shelter.
<>
A suspicious figure darts behind a stall. You can:
* [[Pursue the figure|GuardAction_Pursue]]
* [[Hold the line|GuardAction_Hold]]
<>
<>
Your ledger is underfoot.
* [[Retrieve the ledger (risking harm)|MerchantAction_Retrieve]]
* [[Seek safety and abandon the ledger|MerchantAction_Safety]]
<>
<>
Rumors swirl about corrupt officials.
* [[Listen and gather info|VillagerAction_Listen]]
* [[Calm the crowd and dispel rumors|VillagerAction_Calm]]
<>
[[View Stats|Stats]]
:: GuardAction_Pursue
<>
<>
<>
You chase the suspicious figure, boosting your reputation but weakening your authority for leaving your post.
[[Next|Market Aftermath]]
:: GuardAction_Hold
<>
<>
<>
You hold the line, reinforcing your authority but losing a bit of daring reputation.
[[Next|Market Aftermath]]
:: MerchantAction_Retrieve
<>
<>
<>
You dive into danger, salvaging your ledger (+Wealth) but feeling less secure.
[[Next|Market Aftermath]]
:: MerchantAction_Safety
<>
<>
<>
You choose personal safety over profit, losing some wealth but feeling more secure.
[[Next|Market Aftermath]]
:: VillagerAction_Listen
<>
<>
<>
You gather information quietly, increasing knowledge but losing a bit of trust.
[[Next|Market Aftermath]]
:: VillagerAction_Calm
<>
<>
<>
You try to calm everyone, increasing trust at the expense of missing out on details.
[[Next|Market Aftermath]]
:: Market Aftermath
As the riot disperses, the final scene reflects both your role and your stats:
<>
**Final Stats:** Reputation: $guardReputation, Authority: $guardAuthority
<>
You found clues about instigators. Your higher reputation means the captain respects your initiative, even if your authority slipped.
<>
You maintained order. Solid authority assures your superiors you’re reliable, though less bold than some.
<>
<>
<>
**Final Stats:** Wealth: $merchantWealth, Security: $merchantSecurity
<>
Your ledger ensures long-term financial potential despite feeling more vulnerable now.
<>
You’re poorer but safer, alive to rebuild another day.
<>
<>
<>
**Final Stats:** Knowledge: $villagerKnowledge, Trust: $villagerTrust
<>
With greater knowledge, you’re poised to influence future events, though some neighbors mistrust your quiet gathering of intel.
<>
Higher trust ensures your opinions hold weight in the community, even if you know fewer specifics.
<>
<>
**Try playing again to see how different choices shape your stats and outcomes.**
[[Restart|Startup]]
[[View Stats|Stats]]
:: Stats
**Your Stats:**
<>>
- Reputation: $guardReputation
- Authority: $guardAuthority
<>
<>>
- Wealth: $merchantWealth
- Security: $merchantSecurity
<>
<>>
- Knowledge: $villagerKnowledge
- Trust: $villagerTrust
<>
[[Return|Market Aftermath]]
"""
#-------------------------#-------------------------#-------------------------#-------------------------