Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support arbitrary doors #129

Merged
merged 1 commit into from
Dec 24, 2023
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- An item collection screen will now be shown, when the user starts with random items.
- When softlock prevention is active, then the first two crumble blocks in Super Missile Chamber will be shoot blocks instead.
- Clearer GUI symbols, when expansions have been collected, but not their corresponding launcher.
- Feature, that allows you to place Doors on transitions where there are none.

### Changed
- When softlock prevention is active, then in the EMP Escape route room, instead of the bottom row of speedbooster blocks being gone, now every pillar but the leftmost one is gone.
Expand Down
17 changes: 17 additions & 0 deletions YAMS-LIB/SeedObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
{
[JsonInclude]
[JsonPropertyName("configuration_identifier")]
public ConfigurationIdentifier Identifier;

Check warning on line 13 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'Identifier' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

[JsonInclude]
[JsonPropertyName("game_patches")]
public GamePatches Patches;

Check warning on line 17 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'Patches' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

[JsonInclude]
[JsonPropertyName("door_locks")]
Expand Down Expand Up @@ -53,11 +53,28 @@
public string CreditsSpoiler;
}

[JsonConverter(typeof(JsonStringEnumMemberConverter))]
public enum DoorFacingDirection
{
[EnumMember(Value = "left")]
Left,
[EnumMember(Value = "right")]
Right,
}

public class DoorLock
{
[JsonInclude]
[JsonPropertyName("lock")]
public DoorLockType Lock;

[JsonInclude]
[JsonPropertyName("is_dock")]
public bool isDock;

[JsonInclude]
[JsonPropertyName("facing_direction")]
public DoorFacingDirection FacingDirection;
}

[JsonConverter(typeof(JsonStringEnumMemberConverter))]
Expand Down Expand Up @@ -269,15 +286,15 @@

[JsonInclude]
[JsonPropertyName("locked_missile_text")]
public TextDetails LockedMissileText;

Check warning on line 289 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'LockedMissileText' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

[JsonInclude]
[JsonPropertyName("locked_super_text")]
public TextDetails LockedSuperText;

Check warning on line 293 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'LockedSuperText' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

[JsonInclude]
[JsonPropertyName("locked_pb_text")]
public TextDetails LockedPBombText;

Check warning on line 297 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'LockedPBombText' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
}

public class ConfigurationIdentifier
Expand Down Expand Up @@ -500,10 +517,10 @@
{
[JsonInclude]
[JsonPropertyName("item_id")]
public string ItemID;

Check warning on line 520 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'ItemID' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
[JsonInclude]
[JsonPropertyName("sprite_details")]
public SpriteDetails SpriteDetails;

Check warning on line 523 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'SpriteDetails' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
[JsonInclude]
[JsonPropertyName("item_effect")]
public ItemEnum ItemEffect;
Expand All @@ -513,7 +530,7 @@

[JsonInclude]
[JsonPropertyName("text")]
public TextDetails Text;

Check warning on line 533 in YAMS-LIB/SeedObject.cs

View workflow job for this annotation

GitHub Actions / Build YAMS-Lib and Python Wheel

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

}

Expand Down
70 changes: 44 additions & 26 deletions YAMS-LIB/patches/DoorLockRando.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static void Apply(UndertaleData gmData, GlobalDecompileContext decompileC
{
var characterVarsCode = gmData.Code.ByName("gml_Script_load_character_vars");

// Adjust global event array to be 700
// Adjust global event array to be 900
characterVarsCode.ReplaceGMLInCode( """
i = 350
repeat (350)
Expand All @@ -20,15 +20,15 @@ public static void Apply(UndertaleData gmData, GlobalDecompileContext decompileC
global.event[i] = 0
}
""", """
i = 700
repeat (700)
i = 900
repeat (900)
{
i -= 1
global.event[i] = 0
}
""");
gmData.Code.ByName("gml_Script_sv6_add_events").ReplaceGMLInCode( "350", "700");
gmData.Code.ByName("gml_Script_sv6_get_events").ReplaceGMLInCode( "350", "700");
gmData.Code.ByName("gml_Script_sv6_add_events").ReplaceGMLInCode( "350", "900");
gmData.Code.ByName("gml_Script_sv6_get_events").ReplaceGMLInCode( "350", "900");

// Replace every normal, a4 and a8 door with an a5 door for consistency
var a5Door = gmData.GameObjects.ByName("oDoorA5");
Expand All @@ -44,7 +44,7 @@ public static void Apply(UndertaleData gmData, GlobalDecompileContext decompileC


var doorEventIndex = 350;
foreach ((var id, var doorLock) in seedObject.DoorLocks)
foreach ((var id, var doorEntry) in seedObject.DoorLocks)
{
bool found = false;
foreach (var room in gmData.Rooms)
Expand All @@ -53,18 +53,36 @@ public static void Apply(UndertaleData gmData, GlobalDecompileContext decompileC
{
if (gameObject.InstanceID != id) continue;

if (!gameObject.ObjectDefinition.Name.Content.StartsWith("oDoor") && gameObject.ObjectDefinition.Name.Content != "oA2BigTurbine")
bool isGotoObject = gameObject.ObjectDefinition.Name.Content == "oGotoRoom";
if (isGotoObject && !doorEntry.isDock)
{
throw new NotSupportedException($"The instance id {id} is a GotoRoom object, but the setting whether this instance is a dock is set to false!");
}

if (!gameObject.ObjectDefinition.Name.Content.StartsWith("oDoor") && gameObject.ObjectDefinition.Name.Content != "oA2BigTurbine" && !isGotoObject)
{
throw new NotSupportedException($"The 'door' instance {id} is not actually a door!");
}

UndertaleRoom.GameObject door = gameObject;
if (isGotoObject)
{
// Place tiles
room.Tiles.Add(CreateRoomTile(gameObject.X - doorEntry.FacingDirection == DoorFacingDirection.Left ? 32 : 0, gameObject.Y-64, -100, gmData.Backgrounds.ByName("tlDoor"), doorEntry.FacingDirection == DoorFacingDirection.Left ? (uint)192 : 224, 64, 32, 64));
// Place door
door = CreateRoomObject(gameObject.X - ((doorEntry.FacingDirection == DoorFacingDirection.Left ? 1 : -1) * 24), gameObject.Y-64, gmData.GameObjects.ByName("oDoorA5"), null, doorEntry.FacingDirection == DoorFacingDirection.Left ? -1 : 1);
room.GameObjects.Add(door);
}

found = true;
if (gameObject.CreationCode is null)
if (door.CreationCode is null)
{
var code = new UndertaleCode() { Name = gmData.Strings.MakeString($"gml_RoomCC_{room.Name.Content}_{id}_Create") };
gmData.Code.Add(code);
gameObject.CreationCode = code;
door.CreationCode = code;
}

string codeText = doorLock.Lock switch
string codeText = doorEntry.Lock switch
{
DoorLockType.Normal => "lock = 0; event = -1;",
DoorLockType.Missile => $"lock = 1; originalLock = lock; event = {doorEventIndex};",
Expand Down Expand Up @@ -102,35 +120,35 @@ public static void Apply(UndertaleData gmData, GlobalDecompileContext decompileC
$"if (global.event[eventToSet] > 0)" +
$"{{ if (!wasAlreadyDestroyed) {{ with (wall) instance_destroy(); }} instance_destroy();}} " +
$"if (wasAlreadyDestroyed && global.event[eventToSet] < 1) global.event[eventToSet] = 1;",
_ => throw new NotSupportedException($"Door {id} has an unsupported door lock ({doorLock.Lock})!")
_ => throw new NotSupportedException($"Door {id} has an unsupported door lock ({doorEntry.Lock})!")
};

var waterTurbineObject = gmData.GameObjects.ByName("oA2BigTurbine");
if (gameObject.ObjectDefinition == waterTurbineObject && doorLock.Lock != DoorLockType.A2WaterTurbine)
if (door.ObjectDefinition == waterTurbineObject && doorEntry.Lock != DoorLockType.A2WaterTurbine)
{
gameObject.ObjectDefinition = gmData.GameObjects.ByName("oDoorA5");
gameObject.X += (24 * (int)gameObject.ScaleX);
gameObject.ScaleX *= -1;
bool leftFacing = gameObject.ScaleX < 0;
room.Tiles.Add(CreateRoomTile(gameObject.X - (leftFacing ? 8 : 24), gameObject.Y, -110, gmData.Backgrounds.ByName("tlDoor"), (leftFacing ? 0u : 32u), 0, 32, 64));
door.ObjectDefinition = gmData.GameObjects.ByName("oDoorA5");
door.X += (24 * (int)door.ScaleX);
door.ScaleX *= -1;
bool leftFacing = door.ScaleX < 0;
room.Tiles.Add(CreateRoomTile(door.X - (leftFacing ? 8 : 24), door.Y, -110, gmData.Backgrounds.ByName("tlDoor"), (leftFacing ? 0u : 32u), 0, 32, 64));
var tilesToDelete = room.Tiles.Where((t => (t is { X: 912, Y: 1584, SourceX: 48, SourceY: 304 } or { X: 928, Y: 1536, SourceX: 96, SourceY: 304 }))).ToList();
foreach (var tile in tilesToDelete)
room.Tiles.Remove(tile);
}

if (gameObject.ObjectDefinition != waterTurbineObject && doorLock.Lock == DoorLockType.A2WaterTurbine)
if (door.ObjectDefinition != waterTurbineObject && doorEntry.Lock == DoorLockType.A2WaterTurbine)
{
gameObject.ObjectDefinition = waterTurbineObject;
gameObject.X += (24 * (int)gameObject.ScaleX);
gameObject.ScaleX *= -1;
if ((gameObject.X - 48) == 0)
room.GameObjects.Add(CreateRoomObject(gameObject.X-72, gameObject.Y, gmData.GameObjects.ByName("oSolid1x4")));
else if ((gameObject.X + 48) == room.Width)
room.GameObjects.Add(CreateRoomObject(gameObject.X+72, gameObject.Y, gmData.GameObjects.ByName("oSolid1x4")));
door.ObjectDefinition = waterTurbineObject;
door.X += (24 * (int)door.ScaleX);
door.ScaleX *= -1;
if ((door.X - 48) == 0)
room.GameObjects.Add(CreateRoomObject(door.X-72, door.Y, gmData.GameObjects.ByName("oSolid1x4")));
else if ((door.X + 48) == room.Width)
room.GameObjects.Add(CreateRoomObject(door.X+72, door.Y, gmData.GameObjects.ByName("oSolid1x4")));

}

gameObject.CreationCode.AppendGMLInCode( codeText);
door.CreationCode.AppendGMLInCode( codeText);
doorEventIndex++;
break;
}
Expand Down
Loading