@@ -8,7 +8,7 @@ The Mixtape Engine supports HScript-based Archipelago integration through the Cu
88
991 . ** Create the AP folder** : In your mod directory, create an ` ap/ ` folder
10102 . ** Add HScript files** : Create ` .hx ` files containing your Archipelago logic
11- 3 . ** Define your content** : Use the available functions to add items, locations, and customize songs
11+ 3 . ** Define your content** : Use the available functions to add items, locations, song requirements, and customize songs
12124 . ** Test and iterate** : The system provides automatic validation and helpful error messages
1313
1414### Basic Example
@@ -21,11 +21,15 @@ addItem("Song Unlock Key");
2121addItem("Difficulty Modifier");
2222addItem("Special Effect");
2323
24+ // Make certain songs require items to be accessible
25+ addSimpleSongRequirement("Boss Battle", null, ["Song Unlock Key"]);
26+ addSimpleSongRequirement("Final Boss", null, ["Song Unlock Key", "Difficulty Modifier"]);
27+
2428// Create locations based on your mod's songs
2529for (song in songList) {
2630 // Simple location requiring just the unlock key
2731 addSimpleLocation(song + " Clear", song, null, ["Song Unlock Key"], true);
28-
32+
2933 // Advanced location requiring multiple items
3034 addLocationWithCounts(song + " Perfect", song, null, [
3135 { name: "Song Unlock Key", count: 1 },
4650 ap/
4751 items.hx # Define your items
4852 locations.hx # Define your locations
53+ song_requirements.hx # Define song access requirements
4954 traps.hx # Define trap items
5055 special.hx # Any other AP logic
5156 data/
6570When your HScript runs, these variables are automatically available:
6671
6772- ` modName ` : String - Display name of your mod
68- - ` modFolderName ` : String - Folder name of your mod
73+ - ` modFolderName ` : String - Folder name of your mod
6974- ` songList ` : Array<String > - List of songs in your mod
7075- ` availableMods ` : Array<ModInfo > - Information about all available mods
7176- ` playerSettings ` : Dynamic - All player settings from the YAML generation (APEntryState.gameSettings.FNF)
@@ -82,15 +87,15 @@ Can also simply be used for people used to having a callback method to execute c
8287function onGenYAML() {
8388 // This runs after all mods have been processed and the final song list is generated
8489 trace("Final validation for " + modName);
85-
90+
8691 var finalSongs = getFinalSongList();
8792 trace("Final song count: " + finalSongs.length);
88-
93+
8994 // Perform any final adjustments or validation
9095 if (finalSongs.length == 0) {
9196 trace("Warning: No songs available for mod " + modName);
9297 }
93-
98+
9499 // Final opportunity to add conditional content
95100 if (finalSongs.length >= 10) {
96101 addItem("Song Collection Master");
@@ -106,12 +111,12 @@ Called after the complete generation process, including all processing and data
106111function onAfterGen() {
107112 // This runs after everything is complete, including all data processing
108113 trace("Generation complete for " + modName);
109-
114+
110115 // Log final statistics or perform cleanup
111116 if (hasDataValue("debug_mode")) {
112117 trace("Debug info: " + getDataValue("debug_mode"));
113118 }
114-
119+
115120 // Final summary
116121 trace("Total items added: " + getDataValue("item_count", 0));
117122 trace("Total locations added: " + getDataValue("location_count", 0));
@@ -231,6 +236,89 @@ excludeSong("Local Song", ""); // Removes from base game
231236- Songs from other mods that this mod references
232237- Custom challenge tracks
233238
239+ ## Song Requirements
240+
241+ The system supports making songs require specific items to be accessible. This allows you to gate certain songs behind progression requirements, creating a more structured experience where players must earn access to different content.
242+
243+ ### Basic Song Requirements
244+
245+ ``` haxe
246+ // Make a song require a single item to access
247+ addSimpleSongRequirement("Boss Battle", null, ["Power Boost"]);
248+
249+ // Make a song require multiple items
250+ addSimpleSongRequirement("Final Boss", null, ["Magic Key", "Super Shield"]);
251+
252+ // Make a base game song require items from your mod
253+ addSimpleSongRequirement("Roses", "", ["Thorns Protection"]);
254+
255+ // Make a song from another mod require your items
256+ if (isModEnabled("Other Mod")) {
257+ addSimpleSongRequirement("Cross Mod Song", "Other Mod", ["Special Item"]);
258+ }
259+ ```
260+
261+ ### Advanced Song Requirements
262+
263+ ``` haxe
264+ // Require specific quantities of items
265+ var ultimateRequirements = [
266+ { name: "Energy Crystal", count: 3 },
267+ { name: "Master Key", count: 1 }
268+ ];
269+ addSongRequirementWithCounts("Ultimate Challenge", null, ultimateRequirements);
270+
271+ // Create progressive song unlocking chains
272+ addSimpleSongRequirement("Tutorial Plus", null, ["Basic Training"]);
273+ addSimpleSongRequirement("Beginner Challenge", null, ["Basic Training", "Tutorial Plus Access"]);
274+ addSimpleSongRequirement("Advanced Test", null, ["Basic Training", "Advanced Techniques"]);
275+ ```
276+
277+ ### Song Requirement Utilities
278+
279+ ``` haxe
280+ // Check if a song has requirements
281+ if (hasSongRequirement("Boss Battle", null)) {
282+ trace("Boss Battle is locked behind items");
283+ }
284+
285+ // Get the full requirement details
286+ var requirement = getSongRequirement("Boss Battle", null);
287+ if (requirement != null) {
288+ trace("Boss Battle requires " + requirement.accessRule.requiredItems.length + " items");
289+ }
290+ ```
291+
292+ ### Integration with Game Logic
293+
294+ Song requirements integrate with the randomizer and game client in several ways:
295+
296+ 1 . ** Randomizer Logic** : The AP world generator uses song requirements to ensure proper item distribution
297+ 2 . ** Client Integration** : The game client can lock/unlock songs based on received items
298+ 3 . ** Progression Tracking** : Requirements help create logical progression paths through content
299+
300+ ### Use Cases
301+
302+ ** Progressive Difficulty** : Lock harder songs behind easier ones plus skill items
303+ ``` haxe
304+ addSimpleSongRequirement("Expert Mode", null, ["Rhythm Master", "Perfect Timing"]);
305+ ```
306+
307+ ** Story Progression** : Gate story songs behind narrative items
308+ ``` haxe
309+ addSimpleSongRequirement("Chapter 2", null, ["Chapter 1 Complete", "Story Key"]);
310+ ```
311+
312+ ** Cross-Mod Integration** : Make collaboration songs require items from multiple mods
313+ ``` haxe
314+ addSimpleSongRequirement("Crossover Battle", null, ["Mod A Token", "Mod B Token"]);
315+ ```
316+
317+ ** Challenge Modes** : Lock special modes behind achievement items
318+ ``` haxe
319+ addSimpleSongRequirement("Nightmare Mode", null, ["Courage", "Determination", "Skill"]);
320+ ```
321+
234322## Available Functions
235323
236324### Item Management
@@ -255,6 +343,36 @@ addTrapItem("Universal Freeze", ""); // Empty string = affects base game
255343addTrapItem("Mod-Specific Chaos", "Psych Engine"); // Only affects Psych Engine
256344```
257345
346+ ### Song Requirement Management
347+ ``` haxe
348+ // Make a song require specific items to be accessible
349+ addSimpleSongRequirement("Boss Battle", null, ["Power Boost"]);
350+ addSimpleSongRequirement("Final Boss", null, ["Magic Key", "Super Shield"]);
351+
352+ // Song requirements with specific item counts
353+ addSongRequirementWithCounts("Ultimate Challenge", null, [
354+ { name: "Energy Crystal", count: 3 },
355+ { name: "Master Key", count: 1 }
356+ ]);
357+
358+ // Cross-mod song requirements
359+ addSimpleSongRequirement("Cross Mod Song", "Other Mod", ["Special Item"]);
360+
361+ // Base game song requirements
362+ addSimpleSongRequirement("Roses", "", ["Thorns Protection"]);
363+
364+ // Check if a song has requirements
365+ if (hasSongRequirement("Boss Battle", null)) {
366+ trace("Boss Battle is locked");
367+ }
368+
369+ // Get requirement details
370+ var requirement = getSongRequirement("Boss Battle", null);
371+ if (requirement != null) {
372+ trace("Requirements: " + requirement.accessRule.requiredItems.length + " items");
373+ }
374+ ```
375+
258376### Location Management
259377``` haxe
260378// Basic location with custom access rule
@@ -327,7 +445,7 @@ defineCustomWeek("AP Special Week", ["song1", "song2", "song3"]);
327445defineCustomWeek("Cross-Mod Week", ["base-song", "mod-song"], "Cross Mod");
328446
329447// Enhanced custom week with metadata
330- defineCustomWeek("Boss Week", ["Boss1", "Boss2"], "MyMod",
448+ defineCustomWeek("Boss Week", ["Boss1", "Boss2"], "MyMod",
331449 ["hard", "expert"], // Custom difficulties
332450 "boss", // Default icon for songs
333451 [255, 0, 0] // Default color for songs
@@ -385,7 +503,7 @@ addSong("Expert Song", "MyMod", "boss", [255, 0, 0], ["hard", "expert"]);
385503
386504// Results in optimized weeks:
387505// - ap_custom_MyMod_easy (contains "Easy Song")
388- // - ap_custom_MyMod_easy_normal (contains "Normal Song")
506+ // - ap_custom_MyMod_easy_normal (contains "Normal Song")
389507// - ap_custom_MyMod_hard (contains "Hard Song")
390508// - ap_custom_MyMod_hard_expert (contains "Expert Song")
391509```
@@ -468,7 +586,7 @@ addSongsWithMetadata([
468586// Strategy 3: Conditional content based on other mods
469587if (isModEnabled("Expansion Mod")) {
470588 addSongs(["expansion-collab-1", "expansion-collab-2"], null, "collab", [100, 200, 255]);
471- addSimpleLocation("Expansion Crossover", "expansion-collab-1", "Expansion Mod",
589+ addSimpleLocation("Expansion Crossover", "expansion-collab-1", "Expansion Mod",
472590 ["Crossover Item"], true);
473591}
474592
@@ -488,29 +606,29 @@ function setupEnhancedContent() {
488606 // Individual songs with full metadata
489607 addSong("Tutorial Song", "MyMod", "tutorial", [0, 255, 0], ["easy"]);
490608 addSong("Boss Fight", "MyMod", "boss", [255, 0, 0], ["hard", "expert"]);
491-
609+
492610 // Batch songs with shared metadata
493611 addSongs(["Chapter1", "Chapter2"], "MyMod", "story", [0, 100, 200], ["normal"]);
494-
612+
495613 // Songs with individual metadata (auto-optimized by difficulty)
496614 addSongsWithMetadata([
497615 {name: "Calm Intro", icon: "calm", color: [100, 200, 255], difficulties: ["easy"]},
498616 {name: "Epic Finale", icon: "epic", color: [255, 100, 0], difficulties: ["hard"]},
499617 {name: "Secret Track", icon: "secret", color: [128, 0, 128], difficulties: ["secret"]}
500618 ], "MyMod");
501-
619+
502620 // Basic custom week with defaults
503- defineCustomWeek("Standard Week", ["Normal1", "Normal2"], "MyMod",
621+ defineCustomWeek("Standard Week", ["Normal1", "Normal2"], "MyMod",
504622 ["easy", "normal"], "face", [146, 113, 253]);
505-
623+
506624 // Advanced custom week with per-song metadata
507625 defineCustomWeekWithSongMetadata("Story Mode", [
508626 {name: "Prologue", icon: "start", color: [0, 255, 0]},
509627 {name: "Rising Action", icon: "action", color: [255, 165, 0]},
510628 {name: "Climax", icon: "boss", color: [255, 0, 0]},
511629 {name: "Resolution", icon: "end", color: [0, 0, 255]}
512630 ], "MyMod", ["story"]);
513-
631+
514632 // Add corresponding locations with proper access rules
515633 addSimpleLocation("Tutorial Complete", "Tutorial Song", "MyMod", ["Basic Training"], true);
516634 addSimpleLocation("Boss Defeated", "Boss Fight", "MyMod", ["Boss Key", "Power Upgrade"], true);
@@ -659,6 +777,10 @@ addItem("BF Skin Unlock");
659777addItem("GF Costume");
660778addItem("Special Background");
661779
780+ // Create song requirements for progression
781+ addSimpleSongRequirement("Hard Song", null, ["BF Skin Unlock"]);
782+ addSimpleSongRequirement("Final Boss", null, ["BF Skin Unlock", "GF Costume"]);
783+
662784// Create locations for each song in the mod
663785for (song in songList) {
664786 addSimpleLocation(song + " Completion", song, null, ["BF Skin Unlock"], true);
@@ -683,9 +805,9 @@ for (mod in compatibleMods) {
683805 if (isModEnabled(mod)) {
684806 addItem("Collaboration with " + mod);
685807 addTrapItem(mod + " Style Chaos", mod);
686-
808+
687809 // Add locations that reference the other mod's content
688- addSimpleLocation(mod + " Crossover Achievement", "crossover-song", mod,
810+ addSimpleLocation(mod + " Crossover Achievement", "crossover-song", mod,
689811 ["Collaboration with " + mod], false);
690812 }
691813}
@@ -809,7 +931,7 @@ addItem("Cross Mod Item", "Missing Mod");
809931Split your AP logic into multiple files for better organization:
810932
811933- ` items.hx ` - All item definitions
812- - ` locations.hx ` - All location definitions
934+ - ` locations.hx ` - All location definitions
813935- ` cross-mod.hx ` - Content that depends on other mods
814936- ` special.hx ` - Advanced or conditional logic
815937
@@ -848,4 +970,8 @@ if (otherModCount > 5) {
848970
849971## Example Files
850972
851- See ` docs/HScript_AP_Example.hx ` for a complete example showing all available features.
973+ See the following files for complete examples:
974+ - ` docs/HScript_AP_Example.hx ` - Basic AP integration features
975+ - ` docs/SongRequirements_AP_Example.hx ` - Song requirements system usage
976+ - ` docs/Song_Requirements_Documentation.md ` - Complete song requirements guide
977+ - ` TestSongRequirements_customFNFData.py ` - Python implementation example
0 commit comments