Skip to content

Commit 505f11b

Browse files
authored
Merge pull request #106 from EverestAPI/custom_bird_tutorial
Bird tutorial with custom text and controls
2 parents 283e011 + d113d0f commit 505f11b

File tree

5 files changed

+185
-0
lines changed

5 files changed

+185
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module SpringCollab2020CustomBirdTutorial
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Entity "SpringCollab2020/CustomBirdTutorial" CustomBirdTutorial(x::Integer, y::Integer,
6+
birdId::String="birdId", onlyOnce::Bool=false, caw::Bool=true, faceLeft::Bool=true, info::String="tutorial_dreamjump", controls::String="DownRight,+,Dash,tinyarrow,Jump")
7+
8+
const placements = Ahorn.PlacementDict(
9+
"Custom Bird Tutorial (Spring Collab 2020)" => Ahorn.EntityPlacement(
10+
CustomBirdTutorial
11+
)
12+
)
13+
14+
sprite = "characters/bird/crow00"
15+
16+
function Ahorn.selection(entity::CustomBirdTutorial)
17+
x, y = Ahorn.position(entity)
18+
scaleX = get(entity.data, "faceLeft", true) ? -1 : 1
19+
20+
return Ahorn.Rectangle[Ahorn.getSpriteRectangle(sprite, x, y, sx=scaleX, jx=0.5, jy=1.0)]
21+
end
22+
23+
function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::CustomBirdTutorial, room::Maple.Room)
24+
scaleX = get(entity.data, "faceLeft", true) ? -1 : 1
25+
26+
Ahorn.drawSprite(ctx, sprite, 0, 0, sx=scaleX, jx=0.5, jy=1.0)
27+
end
28+
29+
end

Ahorn/lang/en_gb.lang

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,15 @@ placements.entities.SpringCollab2020/NoDashRefillSpring.tooltips.playerCanUse=De
115115

116116
# Underwater Switch Controller
117117
placements.entities.SpringCollab2020/UnderwaterSwitchController.tooltips.flag=The session flag the controller is listening to. Activating this flag will flood the room with water, deactivating it will remove that water.
118+
119+
# Custom Bird Tutorial
120+
placements.entities.SpringCollab2020/CustomBirdTutorial.tooltips.birdId=An identifier for the bird. Used to tie the bird to its trigger if there are multiple birds in a same room.
121+
placements.entities.SpringCollab2020/CustomBirdTutorial.tooltips.info=The bubble "title". Can either be a dialog ID, or a path to a texture in the Gui atlas.
122+
placements.entities.SpringCollab2020/CustomBirdTutorial.tooltips.caw=Whether the bird should caw upon displaying the bubble.
123+
placements.entities.SpringCollab2020/CustomBirdTutorial.tooltips.faceLeft=Whether the bird should face left or right.
124+
placements.entities.SpringCollab2020/CustomBirdTutorial.tooltips.onlyOnce=If enabled, the bird won't respawn once it flew away, even if the player dies in the room.
125+
placements.entities.SpringCollab2020/CustomBirdTutorial.tooltips.controls=The controls to display in the bubble, separated by commas. Each part can be:\n- a path to a texture in the Gui atlas (for example "tinyarrow")\n- a direction: Down, DownRight, Right, UpRight, Up, UpLeft, Left, DownLeft\n- a button: Jump, Dash, Grab, Talk, ESC, Pause, MenuLeft, MenuRight, MenuUp, MenuDown, MenuConfirm, MenuCancel, MenuJournal, QuickRestart\n- a dialog ID, if you prefix it with "dialog:" (for example, "dialog:TUTORIAL_DREAMJUMP")\n- plain text
126+
127+
# Custom Bird Tutorial Trigger
128+
placements.triggers.SpringCollab2020/CustomBirdTutorialTrigger.tooltips.birdId=The ID of the bird this trigger is controlling.
129+
placements.triggers.SpringCollab2020/CustomBirdTutorialTrigger.tooltips.showTutorial=If checked, the trigger will make the bird show the tutorial bubble. If unchecked, the trigger will make the bird fly away.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module SpringCollab2020CustomBirdTutorialTrigger
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Trigger "SpringCollab2020/CustomBirdTutorialTrigger" CustomBirdTutorialTrigger(x::Integer, y::Integer, width::Integer=Maple.defaultTriggerWidth, height::Integer=Maple.defaultTriggerHeight,
6+
birdId::String="birdId", showTutorial::Bool=true)
7+
8+
const placements = Ahorn.PlacementDict(
9+
"Custom Bird Tutorial (Spring Collab 2020)" => Ahorn.EntityPlacement(
10+
CustomBirdTutorialTrigger,
11+
"rectangle"
12+
)
13+
)
14+
15+
end

Entities/CustomBirdTutorial.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using Celeste.Mod.Entities;
2+
using Microsoft.Xna.Framework;
3+
using Monocle;
4+
using System.Collections.Generic;
5+
using System.Reflection;
6+
7+
namespace Celeste.Mod.SpringCollab2020.Entities {
8+
[CustomEntity("SpringCollab2020/CustomBirdTutorial")]
9+
[Tracked]
10+
class CustomBirdTutorial : BirdNPC {
11+
public string BirdId;
12+
private bool onlyOnce;
13+
private bool caw;
14+
15+
private bool triggered = false;
16+
private bool flewAway = false;
17+
18+
private BirdTutorialGui gui;
19+
20+
private static Dictionary<string, Vector2> directions = new Dictionary<string, Vector2>() {
21+
{ "Left", new Vector2(-1, 0) },
22+
{ "Right", new Vector2(1, 0) },
23+
{ "Up", new Vector2(0, -1) },
24+
{ "Down", new Vector2(0, 1) },
25+
{ "UpLeft", new Vector2(-1, -1) },
26+
{ "UpRight", new Vector2(1, -1) },
27+
{ "DownLeft", new Vector2(-1, 1) },
28+
{ "DownRight", new Vector2(1, 1) }
29+
};
30+
31+
public CustomBirdTutorial(EntityData data, Vector2 offset) : base(data, offset) {
32+
BirdId = data.Attr("birdId");
33+
onlyOnce = data.Bool("onlyOnce");
34+
caw = data.Bool("caw");
35+
Facing = data.Bool("faceLeft") ? Facings.Left : Facings.Right;
36+
37+
object info;
38+
object[] controls;
39+
40+
// parse the info ("title")
41+
string infoString = data.Attr("info");
42+
if (GFX.Gui.Has(infoString)) {
43+
info = GFX.Gui[infoString];
44+
} else {
45+
info = Dialog.Clean(infoString);
46+
}
47+
48+
// go ahead and parse the controls. Controls can be textures, VirtualButtons, directions or strings.
49+
string[] controlsStrings = data.Attr("controls").Split(',');
50+
controls = new object[controlsStrings.Length];
51+
for (int i = 0; i < controls.Length; i++) {
52+
string controlString = controlsStrings[i];
53+
54+
if (GFX.Gui.Has(controlString)) {
55+
// this is a texture.
56+
controls[i] = GFX.Gui[controlString];
57+
} else if (directions.ContainsKey(controlString)) {
58+
// this is a direction.
59+
controls[i] = directions[controlString];
60+
} else {
61+
FieldInfo matchingInput = typeof(Input).GetField(controlString, BindingFlags.Static | BindingFlags.Public);
62+
if (matchingInput?.GetValue(null)?.GetType() == typeof(VirtualButton)) {
63+
// this is a button.
64+
controls[i] = matchingInput.GetValue(null);
65+
} else if (controlString.StartsWith("dialog:")) {
66+
// treat that as a dialog key.
67+
controls[i] = Dialog.Clean(controlString.Substring("dialog:".Length));
68+
} else {
69+
// treat that as a plain string.
70+
controls[i] = controlString;
71+
}
72+
}
73+
}
74+
75+
gui = new BirdTutorialGui(this, new Vector2(0f, -16f), info, controls);
76+
}
77+
78+
public void TriggerShowTutorial() {
79+
if (!triggered) {
80+
triggered = true;
81+
Add(new Coroutine(ShowTutorial(gui, caw)));
82+
}
83+
}
84+
85+
public void TriggerHideTutorial() {
86+
if (triggered && !flewAway) {
87+
flewAway = true;
88+
89+
Add(new Coroutine(HideTutorial()));
90+
Add(new Coroutine(StartleAndFlyAway()));
91+
92+
if (onlyOnce) {
93+
SceneAs<Level>().Session.DoNotLoad.Add(EntityID);
94+
}
95+
}
96+
}
97+
}
98+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Celeste.Mod.Entities;
2+
using Celeste.Mod.SpringCollab2020.Entities;
3+
using Microsoft.Xna.Framework;
4+
5+
namespace Celeste.Mod.SpringCollab2020.Triggers {
6+
[CustomEntity("SpringCollab2020/CustomBirdTutorialTrigger")]
7+
class CustomBirdTutorialTrigger : Trigger {
8+
private string birdId;
9+
private bool showTutorial;
10+
11+
public CustomBirdTutorialTrigger(EntityData data, Vector2 offset) : base(data, offset) {
12+
birdId = data.Attr("birdId");
13+
showTutorial = data.Bool("showTutorial");
14+
}
15+
16+
public override void OnEnter(Player player) {
17+
base.OnEnter(player);
18+
19+
CustomBirdTutorial matchingBird = (CustomBirdTutorial)
20+
Scene.Tracker.GetEntities<CustomBirdTutorial>().Find(entity => entity is CustomBirdTutorial bird && bird.BirdId == birdId);
21+
22+
if (matchingBird != null) {
23+
if (showTutorial) {
24+
matchingBird.TriggerShowTutorial();
25+
} else {
26+
matchingBird.TriggerHideTutorial();
27+
}
28+
}
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)