Skip to content
Draft
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
34 changes: 34 additions & 0 deletions src/content-library/content-library.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type Activity from '../content-types/activity';
import Article from '../content-types/article';
import ContentLoader from './content-loader';

class ContentLibrary {
loader: ContentLoader;
articles: Map<string, Article> = new Map<string, Article>();
activities: Map<string, Activity> = new Map<string, Activity>();

getArticle(id: string) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are for testing purposes (although they might also be useful in general.) I imagine this interface will grow a bit larger, e.g. getAllActivities().

return this.articles.get(id);
}

getActivity(id: string) {
return this.activities.get(id);
}

async initialize() {
const articles = await this.loader.loadArticles();
articles.forEach((article) => {
this.articles.set(article.id, article);
});
const activities = await this.loader.loadActivities();
activities.forEach((activity) => {
this.activities.set(activity.id, activity);
});
}

constructor() {
this.loader = new ContentLoader();
}
}

export default ContentLibrary;
32 changes: 32 additions & 0 deletions src/content-library/content-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Activity from '../content-types/activity';
import Article from '../content-types/article';
const loadedActivities = import.meta.glob('../content/activities/*', { eager: true });
const loadedArticles = import.meta.glob('../content/articles/*', { eager: true });
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since even a small game can have 100+ assets, I didn't want to specify each one individually. I also didn't want a single file per content type, since some content types might be large.

This pattern is what I came up with to allow for dynamic discovery and loading of files, although as a result I had to cheat a bit on the types.

import.meta.glob('../scripts/*', { eager: true });

class ContentLoader {
async loadArticles(): Promise<Article[]> {
const articles: Article[] = [];
Object.entries(loadedArticles).forEach(([, definition]) => {
const article = (definition as Record<string, Article>).default;
articles.push(article);
});
return articles;
}

async loadActivities(): Promise<Activity[]> {
const activities: Activity[] = [];
for (let i = 0; i < Object.entries(loadedActivities).length; i++) {
const module: [string, unknown] = Object.entries(loadedActivities)[i];
const rawActivity = (module[1] as Record<string, Activity>).default;
const importedScript = await import(rawActivity.scriptPath as string);
rawActivity.apply = importedScript.default;
activities.push(rawActivity);
}
return activities;
}

constructor() {}
}

export default ContentLoader;
12 changes: 6 additions & 6 deletions src/content-types/activity.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import BaseContentType from './base-content';
import UIData from './ui-data';

// An activity is a way for players to spend time in between each month of gameplay.
// e.g. leisure, studying, working
class Activity extends BaseContentType {
// Run any state changing effects of the activity.
apply: (/* player */) => void;
apply: (/* player */) => void = () => {
console.log('warning: apply not set on activity with id ' + this.id);
return;
};

constructor(id: string, uiData: UIData, applyFunction: () => void) {
super(id, uiData);
this.apply = applyFunction;
}
// for deserialization purposes
scriptPath: string = '';
Copy link
Collaborator Author

@telrikk telrikk Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I'm thinking of to attach scripts. There are some pros and cons to this approach, but it lets us take advantage of TypeScript. It's a fairly common approach in engines, although we'll have fewer bells and whistles than say, Godot.

}

export default Activity;
8 changes: 8 additions & 0 deletions src/content/activities/test-activity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "test-activity",
"scriptPath": "../scripts/test-activity",
"uiData": {
"description": "an activity for testing",
"name": "test activity"
}
}
7 changes: 7 additions & 0 deletions src/content/articles/test-article.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"id": "test-article",
Copy link
Collaborator Author

@telrikk telrikk Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These files are under /src, which is probably not ideal.

I tried /content with no success. I could also see /static/content, though I'm not sure what ramifications that has.

This feels like an "I don't know which config to change, and haven't figured it out yet."

"uiData": {
"description": "an article for testing",
"name": "test article"
}
}
5 changes: 5 additions & 0 deletions src/scripts/test-activity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const testFunction = function () {
console.log('test successful');
};

export default testFunction;