Skip to content

Commit 9c8b614

Browse files
committed
e
1 parent fce087b commit 9c8b614

File tree

6 files changed

+862
-197
lines changed

6 files changed

+862
-197
lines changed

docs/HSCRIPT_AP_INTEGRATION.md

Lines changed: 147 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The Mixtape Engine supports HScript-based Archipelago integration through the Cu
88

99
1. **Create the AP folder**: In your mod directory, create an `ap/` folder
1010
2. **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
1212
4. **Test and iterate**: The system provides automatic validation and helpful error messages
1313

1414
### Basic Example
@@ -21,11 +21,15 @@ addItem("Song Unlock Key");
2121
addItem("Difficulty Modifier");
2222
addItem("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
2529
for (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 },
@@ -46,6 +50,7 @@ mods/
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/
@@ -65,7 +70,7 @@ mods/
6570
When 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
8287
function 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
106111
function 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
255343
addTrapItem("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"]);
327445
defineCustomWeek("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
469587
if (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");
659777
addItem("GF Costume");
660778
addItem("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
663785
for (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");
809931
Split 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

Comments
 (0)