2025-05-13 19:22:01 +08:00

783 lines
18 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using Godot;
using Godot.Bridge;
using Godot.NativeInterop;
[ScriptPath("res://Scripts/Envirioment/Fish.cs")]
public partial class Fish : Node2D
{
private enum State
{
None,
Casting,
Main,
Reel
}
public enum FishTypes
{
Bass,
Bluegill,
Koi,
Snakehead,
Gar,
Catfish,
Eel,
Trout,
Froggit,
Carp,
Salmon
}
public partial class FishEntity
{
public FishTypes type;
public float size;
public float distance;
public float lifeTime;
public float idle;
public float maxLife;
public int hp;
public int state;
public Sprite2D sprite;
public Vector2 dir;
public Vector2 target;
public void Flee()
{
state = 3;
idle = 0f;
}
public float GetSize()
{
return Mathf.Lerp(difficulty[type][0], difficulty[type][1], size);
}
}
[Export(PropertyHint.None, "")]
private Node2D[] partyPos;
[Export(PropertyHint.None, "")]
private Sprite2D cursor;
[Export(PropertyHint.None, "")]
private Sprite2D cast;
[Export(PropertyHint.None, "")]
private AudioStream reelMusic;
[Export(PropertyHint.None, "")]
private AudioStream castMusic;
private CpuParticles2D part;
private Node2D prompt;
private static readonly Dictionary<FishTypes, int[]> difficulty = new Dictionary<FishTypes, int[]>
{
{
FishTypes.Bass,
new int[2] { 3, 6 }
},
{
FishTypes.Bluegill,
new int[2] { 2, 4 }
},
{
FishTypes.Koi,
new int[2] { 3, 8 }
},
{
FishTypes.Snakehead,
new int[2] { 8, 12 }
},
{
FishTypes.Gar,
new int[2] { 8, 15 }
},
{
FishTypes.Catfish,
new int[2] { 7, 15 }
},
{
FishTypes.Eel,
new int[2] { 3, 7 }
},
{
FishTypes.Trout,
new int[2] { 3, 6 }
},
{
FishTypes.Froggit,
new int[2] { 2, 8 }
},
{
FishTypes.Carp,
new int[2] { 4, 6 }
},
{
FishTypes.Salmon,
new int[2] { 5, 7 }
}
};
private Texture2D[] texs = new Texture2D[5]
{
GD.Load<Texture2D>("res://Sprites/Props/Fish/FishBobber2.tres"),
GD.Load<Texture2D>("res://Sprites/Props/Fish/FishBobber.tres"),
GD.Load<Texture2D>("res://Sprites/Props/Fish/FishBobber3.tres"),
GD.Load<Texture2D>("res://Sprites/Props/Fish/FishBobber4.tres"),
GD.Load<Texture2D>("res://Sprites/Props/Fish/FishShadow.tres")
};
private Texture2D[] fishTex = Array.Empty<Texture2D>();
private const float radius = 100f;
private const int max = 5;
private Coroutine action;
private State state;
private int combo;
private int promptDir;
private float time;
private float spawn;
private float promptTime;
private FishEntity fishing;
private string[] fishNames;
public List<FishEntity> fishes = new List<FishEntity>();
private readonly float[] promptAngle = new float[4] { 180f, 0f, 90f, 270f };
private void StartFish()
{
if (SaveFile.current.followers.Count > 0)
{
TextSystem.GetText("FishCant");
}
else if (!SaveFile.HasFlag(SaveFile.Flags.AfterDinnerDay1))
{
TextSystem.GetText("KanakoFishD1");
}
else
{
Main.inEvent = Coroutine.Start(Routine());
}
}
public override void _EnterTree()
{
fishNames = FileAccess.Open(Texts.textFolder + "FishNames.txt", FileAccess.ModeFlags.Read).GetAsText(skipCr: true).Split('\n');
part = cast.GetChild<CpuParticles2D>(0);
prompt = cast.GetChild<Node2D>(1);
}
public override void _Process(double delta)
{
Coroutine coroutine = action;
if (coroutine != null && coroutine.done)
{
action = null;
}
FishScale();
}
private IEnumerator Routine()
{
state = State.None;
combo = 0;
time = 0f;
spawn = 0f;
if (SaveFile.HasFlag(SaveFile.Flags.KanakoFollowing) && !SaveFile.HasFlag(SaveFile.Flags.KanakoFishFirstTalk))
{
SaveFile.AddFlag(SaveFile.Flags.KanakoFishFirstTalk);
TextSystem.GetText("KanakoFishD2");
while (TextSystem.instance.Visible)
{
yield return null;
}
}
TextSystem.GetText("FishPrompt");
while (TextSystem.instance.Visible)
{
yield return null;
}
if (TextSystem.lastPrompt != 0)
{
yield break;
}
Party.ChangeCollider(state: false);
IList<Node2D> list = partyPos;
Party.MoveTo(Common.PosFromNode(in list), 0.5f, align: true, Entity.Direction.West);
while (!Party.IsStopped())
{
yield return null;
}
Coroutine c = Coroutine.Start(CameraController.Pan(base.GlobalPosition + 50f * Vector2.Left, 0.5f));
Audio.ChangeMusic(castMusic, 1f);
while (true)
{
if (c.done)
{
Coroutine routine = Audio.routine;
if (routine == null || routine.done)
{
break;
}
}
yield return null;
}
SetAnims();
state = State.Casting;
for (int i = 0; i < fishes.Count; i++)
{
if (GodotObject.IsInstanceValid(fishes[i].sprite))
{
fishes[i].sprite.QueueFree();
}
}
fishes.Clear();
while (GameLoop())
{
yield return null;
}
state = State.None;
cursor.Visible = false;
Party.UpdateAnim();
c = Coroutine.Start(CameraController.PanToObj(Player.instance, 0.5f));
while (!c.done)
{
yield return null;
}
Party.ChangeCollider(state: true);
Room.current.DoMusic(0);
Player.instance.ResetFollowStep();
}
private Vector2 GetRandomPoint(float mult = 1f)
{
return (Vector2.Up * (float)GD.RandRange(0.0, 100f * mult)).Rotated(Mathf.DegToRad(GD.RandRange(0, 360)));
}
private bool GameLoop()
{
time += Main.deltaTime;
Coroutine coroutine = action;
if (coroutine != null && !coroutine.done)
{
return true;
}
switch (state)
{
case State.Casting:
FishLoop();
if (!cursor.Visible)
{
cursor.GlobalPosition = base.GlobalPosition;
cursor.Visible = true;
break;
}
cursor.RotationDegrees += Main.deltaTime * -0.75f;
cursor.GlobalPosition += Main.GetDirection().Normalized() * 2f;
cursor.Position = cursor.Position.LimitLength(85f);
if (Input.IsActionJustPressed(Main.keys[4]))
{
action = Coroutine.Start(Cast());
state = State.Main;
return true;
}
if (!Input.IsActionJustPressed(Main.keys[5]) && !Input.IsActionJustPressed(Main.keys[6]))
{
break;
}
Audio.PlaySound(Audio.commonSounds[0]);
return false;
case State.Main:
FishLoop();
CastTexture();
if (Input.IsActionJustPressed(Main.keys[4]))
{
if (fishing != null)
{
for (int j = 0; j < fishes.Count; j++)
{
if (fishes[j] != fishing)
{
fishes[j].state = 3;
}
}
Audio.PlaySound("snd_grab.wav");
Player.instance.anim.Play("FishReel");
if (SaveFile.HasFlag(SaveFile.Flags.KanakoFollowing))
{
Party.party[1].oEntity.anim.Play("SitLook2");
}
Player.instance.shakeIntensity = 1f;
part.Emitting = true;
state = State.Reel;
BattleDR.ShowFloatingText(Texts.common[123], cast.GlobalPosition);
}
else
{
CancelReel();
}
}
else if ((Input.IsActionJustPressed(Main.keys[5]) || Input.IsActionJustPressed(Main.keys[6])) && fishing == null)
{
CancelReel();
}
break;
case State.Reel:
Player.instance.shake = 5f;
FishLoop();
if (!prompt.Visible)
{
promptTime = 80f;
promptDir = GD.RandRange(0, 3);
prompt.RotationDegrees = promptAngle[promptDir];
prompt.Visible = true;
}
if (promptTime > 0f)
{
prompt.Modulate = ((Mathf.Sin(time * 0.2f) > 0f) ? Main.colorWhite : Main.colorDark);
promptTime -= Main.deltaTime;
for (int i = 0; i <= 3; i++)
{
if (Input.IsActionJustPressed(Main.keys[i]))
{
if (i == promptDir)
{
fishing.hp--;
action = Coroutine.Start(PromptSuccess());
}
else
{
Fail();
}
}
}
}
else
{
Fail();
}
break;
}
return true;
}
private void Fail()
{
Audio.PlaySound("snd_break1.wav");
BattleDR.ShowFloatingText(Texts.common[122], cast.GlobalPosition);
combo = 0;
state = State.Casting;
RemoveFish(0);
fishing = null;
CancelReel();
if (SaveFile.HasFlag(SaveFile.Flags.KanakoFollowing))
{
Party.party[1].oEntity.anim.Play("SitDraw");
}
}
private IEnumerator PromptSuccess()
{
Audio.PlaySound("snd_shineselect.wav");
prompt.Modulate = Main.colorYellow;
for (float a = 0f; a < 20f; a += Main.deltaTime)
{
yield return null;
}
prompt.Modulate = Main.colorWhite;
prompt.Visible = false;
if (fishing.hp <= 0)
{
combo++;
action = Coroutine.Start(Cast(back: true));
}
}
private void CancelReel()
{
part.Emitting = false;
prompt.Visible = false;
action = Coroutine.Start(Cast(back: true));
}
private void CastTexture()
{
cast.Texture = texs[(fishing != null) ? 3 : ((Mathf.Sin(time * 0.2f) > 0f) ? 1 : 2)];
}
private void FishLoop()
{
for (int num = fishes.Count - 1; num >= 0; num--)
{
if (fishing == null && (fishes[num].state == 1 || fishes[num].state == 2) && fishes[num].distance < 16f)
{
fishes[num].idle = 0f;
fishes[num].state = 4;
}
switch (fishes[num].state)
{
case 0:
if (fishes[num].lifeTime > 60f)
{
fishes[num].state = 1;
fishes[num].idle = GD.RandRange(60, 200);
}
break;
case 1:
if (fishes[num].lifeTime > 1600f)
{
fishes[num].Flee();
}
else if (fishes[num].idle <= 0f)
{
fishes[num].state = 2;
fishes[num].target = base.GlobalPosition + GetRandomPoint(0.8f);
fishes[num].dir = fishes[num].sprite.GlobalPosition.DirectionTo(fishes[num].target);
}
else
{
fishes[num].idle -= Main.deltaTime;
}
break;
case 2:
if (fishes[num].sprite.GlobalPosition.DistanceTo(fishes[num].target) > 5f)
{
fishes[num].sprite.GlobalPosition += fishes[num].dir * 0.25f * Main.deltaTime;
}
else
{
fishes[num].state = 0;
}
break;
case 3:
if (fishes[num].idle < 60f)
{
fishes[num].idle += Main.deltaTime;
}
else
{
RemoveFish(num);
}
break;
case 4:
if (fishes[num].idle < 30f)
{
fishes[num].idle += Main.deltaTime;
break;
}
fishes[num].idle = 0f;
fishes[num].state = 5;
break;
case 5:
if (fishes[num].distance > 1f)
{
fishes[num].sprite.GlobalPosition += fishes[num].sprite.GlobalPosition.DirectionTo(cast.GlobalPosition) * Mathf.Clamp(fishes[num].distance / 2f, 0f, 1f) * 0.2f * Main.deltaTime;
break;
}
fishes[num].idle = 0f;
if (fishing != null)
{
fishes[num].state = 3;
break;
}
Audio.PlaySound("snd_swallow_ch1.wav");
Main.Particle("WaterBurst", fishes[num].sprite.GlobalPosition);
fishing = fishes[num];
fishes[num].state = 6;
break;
case 6:
if (state == State.Reel)
{
break;
}
if (fishes[num].idle < 60f)
{
fishes[num].idle += Main.deltaTime;
break;
}
if (fishing == fishes[num])
{
fishing = null;
}
fishes[num].Flee();
BattleDR.ShowFloatingText(Texts.common[122], cast.GlobalPosition);
break;
}
}
if (spawn > 0f)
{
spawn -= Main.deltaTime;
}
else if ((state == State.Main || state == State.Casting) && fishes.Count < 5)
{
spawn = GD.RandRange(120, 300);
SpawnFish();
}
}
private void RemoveFish(int i)
{
fishes[i].sprite.QueueFree();
fishes.RemoveAt(i);
}
private void SpawnFish()
{
fishes.Add(new FishEntity
{
type = (FishTypes)GD.RandRange(0, 10),
size = (float)GD.RandRange(0.5, 1.2999999523162842),
distance = 99999f,
maxLife = GD.RandRange(1600, 2800)
});
List<FishEntity> list = fishes;
FishEntity fishEntity = list[list.Count - 1];
Dictionary<FishTypes, int[]> dictionary = difficulty;
List<FishEntity> list2 = fishes;
float num = dictionary[list2[list2.Count - 1].type][0];
Dictionary<FishTypes, int[]> dictionary2 = difficulty;
List<FishEntity> list3 = fishes;
float to = dictionary2[list3[list3.Count - 1].type][1];
List<FishEntity> list4 = fishes;
fishEntity.hp = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(num, to, list4[list4.Count - 1].size)), 3, 8);
List<FishEntity> list5 = fishes;
FishEntity fishEntity2 = list5[list5.Count - 1];
Sprite2D obj = new Sprite2D
{
Texture = texs[4],
Scale = Vector2.Zero,
Position = GetRandomPoint(0.8f)
};
Sprite2D node = obj;
fishEntity2.sprite = obj;
AddChild(node, forceReadableName: false, InternalMode.Disabled);
}
private IEnumerator Cast(bool back = false)
{
cursor.Visible = false;
if (!back)
{
Player.instance.anim.Play("FishCast");
}
else
{
Audio.PlaySound("snd_splash.wav");
part.Emitting = false;
Player.instance.anim.PlayBackwards("FishCast");
FishEntity fishEntity = fishing;
if (fishEntity != null && fishEntity.hp <= 0)
{
fishing.sprite.Visible = false;
}
}
Audio.PlaySound("snd_swing.wav");
float a;
for (a = 0f; a < 5f; a += Main.deltaTime)
{
yield return null;
}
cast.Visible = true;
cast.Texture = texs[0];
Vector2 sp = Player.instance.GlobalPosition + Vector2.Up * 32f;
Vector2 m = sp.Lerp(cursor.GlobalPosition, 0.5f) + Vector2.Up * 50f;
a = 0f;
for (float b = 60f; a < b; a += Main.deltaTime)
{
cast.GlobalPosition = Common.Beizier(sp, m, cursor.GlobalPosition, back ? (1f - a / b) : (a / b));
yield return null;
}
if (back)
{
cast.Visible = false;
state = State.Casting;
FishEntity fishEntity2 = fishing;
if (fishEntity2 != null && fishEntity2.hp <= 0)
{
BattleDR.ShowFloatingText(Texts.common[124], Player.instance.GlobalPosition);
Texts.TextBlock[] diagClone = Texts.GetDiagClone("FishGet");
Audio.PlaySound("snd_item_ch1.wav");
Player.instance.anim.Play("SideIdleHat");
float b = fishing.GetSize();
diagClone[0].text = diagClone[0].text.Replace("[NAME]", fishNames[(int)fishing.type]).Replace("[SIZE]", b.ToString("0.00"));
diagClone[0].portrait = "Fish" + fishing.type;
RemoveFish(0);
if (SaveFile.HasFlag(SaveFile.Flags.KanakoFollowing))
{
Party.party[1].oEntity.anim.Play("SitHappy");
}
TextSystem.SingleText(diagClone);
while (TextSystem.instance.Visible)
{
yield return null;
}
if (!SaveFile.current.fishes.ContainsKey((int)fishing.type))
{
SaveFile.current.fishes.Add((int)fishing.type, new float[2]);
}
SaveFile.current.fishes[(int)fishing.type][0] += 1f;
if (b > SaveFile.current.fishes[(int)fishing.type][1])
{
SaveFile.current.fishes[(int)fishing.type][1] = b;
}
fishing = null;
if (SaveFile.HasFlag(SaveFile.Flags.KanakoFollowing))
{
Party.party[1].oEntity.anim.Play("SitDraw");
}
Player.instance.anim.Play("FishOut");
}
yield break;
}
Audio.PlaySound("snd_splash.wav");
Main.Particle("WaterBurst", cursor.GlobalPosition);
for (int i = 0; i < fishes.Count; i++)
{
if (fishes[i].sprite.GlobalPosition.DistanceTo(cast.GlobalPosition) < 20f)
{
fishes[i].Flee();
}
}
}
private void SetAnims()
{
Party.UpdateFollowerEntities();
for (int i = 0; i < SaveFile.current.activeParty.Count; i++)
{
switch (Party.party[SaveFile.current.activeParty[i]].oEntity.id)
{
case Entity.IDs.Clover:
Party.party[SaveFile.current.activeParty[i]].oEntity.sprite.FlipH = true;
Party.party[SaveFile.current.activeParty[i]].oEntity.anim.Play("FishOut");
break;
case Entity.IDs.Kanako:
Party.party[SaveFile.current.activeParty[i]].oEntity.sprite.FlipH = false;
Party.party[SaveFile.current.activeParty[i]].oEntity.anim.Play("SitDraw");
break;
}
}
}
private void FishScale()
{
float weight = Main.deltaTime * 0.05f;
for (int i = 0; i < fishes.Count; i++)
{
if (state == State.None || fishes[i].state == 3)
{
fishes[i].sprite.Scale = fishes[i].sprite.Scale.Lerp(Vector2.Zero, weight);
}
else
{
fishes[i].sprite.Scale = fishes[i].sprite.Scale.Lerp((Mathf.Clamp(Mathf.InverseLerp(4f, 10f, fishes[i].GetSize()), 0.5f, 1.5f) + Mathf.Sin(fishes[i].lifeTime * 0.1f) * 0.1f) * Vector2.One, weight);
fishes[i].lifeTime += Main.deltaTime;
}
fishes[i].sprite.Modulate = Main.colorClear.Lerp(Main.colorWhite, fishes[i].sprite.Scale.Length());
if (state == State.Main)
{
fishes[i].distance = fishes[i].sprite.GlobalPosition.DistanceTo(cast.GlobalPosition);
}
else
{
fishes[i].distance = 99999f;
}
}
}
private void FishFanEventD2()
{
if (SaveFile.HasFlag(SaveFile.Flags.FishGuyTaskDay2))
{
TextSystem.GetText("FishFanD2After");
}
else if (!SaveFile.HasFlag(SaveFile.Flags.FishGuyFirstTalk))
{
TextSystem.GetText("FishFanFirstDiag");
SaveFile.AddFlag(SaveFile.Flags.FishGuyFirstTalk);
}
else if (SaveFile.current.fishes.Count >= 5)
{
TextSystem.GetText("FishFanD2Got");
SaveFile.current.gold += 5;
SaveFile.AddFlag(SaveFile.Flags.FishGuyTaskDay2);
}
else
{
TextSystem.GetText("FishFanD2");
}
}
private void FishSign()
{
Main.inEvent = Coroutine.Start(FishSignRoutine());
}
private IEnumerator FishSignRoutine()
{
TextSystem.GetText("FishSign");
while (TextSystem.instance.Visible)
{
yield return null;
}
if (TextSystem.lastPrompt == 0)
{
TextSystem.GetText("FishInstructions");
}
else
{
if (TextSystem.lastPrompt != 1)
{
yield break;
}
if (SaveFile.current.fishes.Count == 0)
{
TextSystem.GetText("FishNone");
yield break;
}
foreach (int key in SaveFile.current.fishes.Keys)
{
Texts.TextBlock[] diagClone = Texts.GetDiagClone("FishStat");
diagClone[0].text = diagClone[0].text.Replace("[NAME]", fishNames[key]).Replace("[SIZE]", SaveFile.current.fishes[key][1].ToString("0.00")).Replace("[AMT]", SaveFile.current.fishes[key][0].ToString());
Texts.TextBlock obj = diagClone[0];
FishTypes fishTypes = (FishTypes)key;
obj.portrait = "Fish" + fishTypes;
TextSystem.SingleText(diagClone);
while (TextSystem.instance.Visible)
{
yield return null;
}
}
}
}
}