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

977 lines
20 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/Puzzles/FightingGame.cs")]
public partial class FightingGame : Node2D
{
private enum Animations
{
Idle,
Walk,
BackWalk,
Hurt,
Attack1,
Attack2,
Block,
Hadouken,
Cheer,
Heal,
Surprise
}
[Signal]
public delegate void OnEndEventHandler();
public partial class Buffer
{
public int key;
public float time;
}
public partial class FigherData
{
public float hp = 100f;
public float stun;
public float knockback;
public float hyperArmor;
public float armorBuildup;
public float armorTime;
public float blockHold;
public Entity entity;
public Coroutine move;
public Color color = Main.colorWhite;
public bool block;
}
private readonly Dictionary<Animations, StringName> anims = new Dictionary<Animations, StringName>
{
{
Animations.Idle,
"Idle"
},
{
Animations.Walk,
"Walk"
},
{
Animations.BackWalk,
"BackWalk"
},
{
Animations.Hurt,
"Hurt"
},
{
Animations.Attack1,
"Attack1"
},
{
Animations.Attack2,
"Attack2"
},
{
Animations.Block,
"Block"
},
{
Animations.Hadouken,
"Hadouken"
},
{
Animations.Cheer,
"CheerBack"
},
{
Animations.Heal,
"SpellBack"
},
{
Animations.Surprise,
"SurpriseBack"
}
};
public static FightingGame instance;
[Export(PropertyHint.None, "")]
private Entity[] fighters;
[Export(PropertyHint.None, "")]
private AudioStream music;
[Export(PropertyHint.None, "")]
private Sprite2D soul;
[Export(PropertyHint.None, "")]
private PackedScene axisSphere;
[Export(PropertyHint.None, "")]
private PackedScene shockWave;
[Export(PropertyHint.None, "")]
private Node2D UI;
[Export(PropertyHint.None, "")]
private Node2D[] hpBars;
[Export(PropertyHint.None, "")]
private AnimationPlayer roundAnim;
[Export(PropertyHint.None, "")]
private RichTextLabel help;
public int round = 1;
public int tp;
public int itp;
private const int kanakoCost = 90;
private const int hadoukenCost = 15;
private const int kanakoHeal = 35;
public bool active;
private FigherData[] data = new FigherData[2];
private Coroutine busy;
private Coroutine healing;
private List<Node> proj = new List<Node>();
private float projTime;
private Node2D shockwave;
private readonly StringName hitbox = "Hit";
private List<Buffer> buffers = new List<Buffer>();
private const int enemyDist = 50;
private float aiTime;
private float blockTime;
private float comboTime;
private int combo;
public override void _EnterTree()
{
instance = this;
fighters[0].trigger.AreaEntered += PlayerHit;
fighters[1].trigger.AreaEntered += EnemyHit;
}
public override void _Process(double delta)
{
UI.Position = new Vector2(0f, Mathf.Lerp(UI.Position.Y, (!active) ? (-70) : 0, Main.deltaTime * 0.1f));
FigherData[] array = data;
if (array != null && array.Length != 0 && data[0] != null)
{
for (int i = 0; i < 2; i++)
{
hpBars[i].Scale = new Vector2(Mathf.Max(0f, data[i].hp / 100f), 1f);
fighters[i].Modulate = fighters[i].Modulate.Lerp(data[i].color, Main.deltaTime * 0.1f);
}
}
if (!active)
{
return;
}
Player.instance.canInput = false;
if (busy == null || busy.done)
{
for (int j = 0; j < 2; j++)
{
fighters[j].GlobalPosition = new Vector2(fighters[j].GlobalPosition.X, fighters[j].startPos.Y);
if (data[j].stun <= 0f && data[j].knockback <= 0f && (data[j].move == null || data[j].move.done))
{
if (j == 0)
{
Inputs();
}
else
{
EnemyAI();
}
}
else if (data[j].knockback > 0f)
{
DoKnockback(j);
}
else if (data[j].stun > 0f)
{
data[j].entity.Velocity = Vector2.Zero;
}
}
KanakoAnim();
}
DWMenu.instance.showHP = 5f;
BattleDR.UpdateTP(ref itp, in tp, 0, 1);
Cooldowns();
}
private void KanakoAnim()
{
Coroutine coroutine = healing;
if (coroutine == null || coroutine.done)
{
if (data[0].knockback > 0f)
{
Player.instance.followers[0].anim.Play(anims[Animations.Surprise]);
}
else
{
Player.instance.followers[0].anim.Play(anims[Animations.Cheer]);
}
}
}
private void DoKnockback(int i)
{
float num = Main.deltaTime * Mathf.Clamp(data[i].knockback, 0f, 10f) * 5f;
if (i == 0)
{
fighters[i].Velocity = Vector2.Left * num;
}
else
{
fighters[i].Velocity = Vector2.Right * num;
}
}
private IEnumerator ToastProj()
{
fighters[1].Velocity = Vector2.Zero;
fighters[1].Shake(30f, 2f);
Audio.PlaySound("snd_boost.wav");
fighters[1].anim.Play(anims[Animations.Attack2]);
for (float a = 0f; a < 10f; a += Main.deltaTime)
{
data[1].hyperArmor = 10f;
yield return null;
}
fighters[1].anim.Pause();
for (int i = 0; i < 3; i++)
{
fighters[1].Modulate = Main.colorYellow;
for (float a = 0f; a < 10f; a += Main.deltaTime)
{
data[1].hyperArmor = 10f;
yield return null;
}
}
fighters[1].anim.Play();
CameraController.Shake(15f, 2f);
Audio.PlaySound("snd_rudebuster_hit.wav");
Node node = BattleDR.NewBullet(in shockWave, fighters[1].GlobalPosition + new Vector2(-30f, 0f), this);
Timer obj = new Timer
{
Autostart = true,
WaitTime = 5.0,
OneShot = true
};
Timer timer = obj;
node.AddChild(obj, forceReadableName: false, InternalMode.Disabled);
timer.Timeout += node.QueueFree;
for (float a = 0f; a < 15f; a += Main.deltaTime)
{
yield return null;
}
fighters[1].anim.Pause();
for (float a = 0f; a < 45f; a += Main.deltaTime)
{
yield return null;
}
fighters[1].anim.Play(anims[Animations.Idle]);
yield return null;
if (Main.RandomRange(0, 100) < 30)
{
aiTime = 40f;
}
else
{
aiTime = 20f;
}
}
private IEnumerator ToastPunch()
{
fighters[1].Velocity = Vector2.Zero;
Audio.PlaySound("motor_swing_down.wav");
fighters[1].anim.Play(anims[Animations.Attack1]);
if ((round > 1 || data[1].hp < 65f) && Main.RandomRange(0, 100) < ((round == 1) ? 50 : 70))
{
for (float a = 0f; a < 22f; a += Main.deltaTime)
{
yield return null;
}
fighters[1].anim.Play(anims[Animations.Attack2]);
Audio.PlaySound("motor_upper_2.wav", 1f, 0.9f);
yield return null;
}
while ((StringName)fighters[1].anim.CurrentAnimation != anims[Animations.Idle])
{
yield return null;
}
if (Main.RandomRange(0, 100) < 30)
{
aiTime = 60f;
}
else
{
aiTime = 20f;
}
}
private void EnemyAI()
{
float num = Mathf.Abs(fighters[1].GlobalPosition.X - fighters[0].GlobalPosition.X);
data[1].block = false;
switch (round)
{
case 1:
if (!EnemyBase(num) && num < 50f)
{
data[1].move = Coroutine.Start(ToastPunch(), this);
}
break;
case 2:
if (projTime > 45f || proj.Count >= 2)
{
blockTime = 10f;
}
if (!EnemyBase(num) && num < 50f)
{
data[1].move = Coroutine.Start(ToastPunch(), this);
}
break;
case 3:
if (projTime > 30f || proj.Count >= 2)
{
blockTime = 10f;
}
if (num > 100f && Engine.GetFramesDrawn() % 3 == 0 && aiTime <= 0f)
{
data[1].move = Coroutine.Start(ToastProj(), this);
}
else if (!EnemyBase(num) && num < 50f)
{
data[1].move = Coroutine.Start(ToastPunch(), this);
}
break;
}
}
private bool EnemyBase(float d)
{
if (combo > 2)
{
blockTime = 80f;
}
if (blockTime > 0f && data[1].hyperArmor <= 0f)
{
fighters[1].Velocity = Vector2.Zero;
fighters[1].anim.Play(anims[Animations.Block]);
data[1].block = true;
return true;
}
if (aiTime > 0f)
{
Move(fighters[1], Animations.Walk, 0.8f);
return true;
}
if (d > 50f)
{
Move(fighters[1], Animations.Walk, -1f);
if (Engine.GetFramesDrawn() % 3 == 0 && Main.RandomRange(0, 100) <= 2)
{
aiTime = Main.RandomRange(30, 60);
}
return true;
}
return false;
}
private void Cooldowns()
{
if (blockTime > 0f)
{
blockTime -= Main.deltaTime;
}
if (aiTime > 0f)
{
aiTime -= Main.deltaTime;
}
if (comboTime > 0f)
{
comboTime -= Main.deltaTime;
}
else
{
combo = 0;
}
if (proj.Count > 0)
{
projTime += Main.deltaTime;
}
else
{
projTime = 0f;
}
for (int i = 0; i < 2; i++)
{
if (data[i] != null)
{
if (data[i].blockHold > 0f && (StringName)data[i].entity.anim.CurrentAnimation != anims[Animations.Block])
{
data[i].blockHold -= Main.deltaTime * 0.05f;
}
else
{
data[i].blockHold = 0f;
}
if (data[i].stun > 0f)
{
data[i].stun -= Main.deltaTime;
}
if (data[i].knockback > 0f)
{
data[i].knockback -= Main.deltaTime;
}
if (data[i].hyperArmor > 0f)
{
data[i].armorTime += Main.deltaTime;
data[i].hyperArmor -= Main.deltaTime * Mathf.Max(1f, 0.5f * Mathf.Floor(data[i].armorTime / 30f));
}
if (data[i].armorBuildup > 0f)
{
data[i].armorBuildup -= Main.deltaTime * (float)((!(data[i].hyperArmor > 0f)) ? 1 : 2);
}
}
}
for (int num = buffers.Count - 1; num >= 0; num--)
{
if (buffers[num].time <= 0f)
{
buffers.RemoveAt(num);
}
else
{
buffers[num].time -= Main.deltaTime;
}
}
}
private IEnumerator Attack()
{
fighters[0].Velocity = Vector2.Zero;
bool cancombo = false;
if ((StringName)fighters[0].anim.CurrentAnimation != anims[Animations.Attack1])
{
Audio.PlaySound("motor_swing_down.wav");
fighters[0].anim.Play(anims[Animations.Attack1]);
cancombo = true;
}
else
{
Audio.PlaySound("motor_upper_2.wav");
fighters[0].anim.Play(anims[Animations.Attack2]);
}
for (float a = 0f; a < 18f; a += Main.deltaTime)
{
yield return null;
}
while ((StringName)fighters[0].anim.CurrentAnimation != anims[Animations.Idle])
{
if (cancombo && Input.IsActionJustPressed(Main.keys[4]))
{
data[0].move = Coroutine.Start(Attack(), this);
break;
}
yield return null;
}
}
private IEnumerator HealBell()
{
Player.instance.followers[0].anim.Play(anims[Animations.Heal]);
Audio.PlaySound("snd_spellcast_ch1.wav");
Node p = Main.Particle("HealBell", new Vector2(0f, -50f), 1, fighters[0]);
for (float a = 0f; a < 70f; a += Main.deltaTime)
{
yield return null;
}
Audio.PlaySound("snd_power.wav");
data[0].hp = Mathf.Min(data[0].hp + 35f, 100f);
BattleDR.ShowFloatingText(35.ToString(), fighters[0].GlobalPosition, Main.colorGreen);
fighters[0].Modulate = Main.colorGreen;
while (GodotObject.IsInstanceValid(p))
{
yield return null;
}
}
private void Move(Entity entity, Animations anim, float spd = 1f)
{
entity.Velocity = Vector2.Right * spd * entity.baseSpeed;
entity.anim.Play(anims[anim]);
}
private IEnumerator Hadouken()
{
Audio.PlaySound("snd_shotmid.wav");
fighters[0].Velocity = Vector2.Zero;
fighters[0].anim.Play(anims[Animations.Hadouken]);
MoveAhead moveAhead = (MoveAhead)BattleDR.NewBullet(in axisSphere, fighters[0].GlobalPosition + new Vector2(26f, -23f), Room.current);
moveAhead.speed = Vector2.Right * 1.3f;
Area2D child = moveAhead.GetChild<Area2D>(0);
child.CollisionLayer = 8u;
child.SetMeta(hitbox, new int[2] { 8, 35 });
proj.Add(moveAhead);
Timer obj = new Timer
{
WaitTime = 6.0,
Autostart = true,
OneShot = true
};
Timer timer = obj;
moveAhead.AddChild(obj, forceReadableName: false, InternalMode.Disabled);
timer.Timeout += moveAhead.QueueFree;
for (float a = 0f; a < 30f; a += Main.deltaTime)
{
yield return null;
}
}
private void Inputs()
{
for (int i = 0; i < Main.keys.Length; i++)
{
if (Input.IsActionJustPressed(Main.keys[i]))
{
buffers.Add(new Buffer
{
key = i,
time = 20f
});
}
}
if (buffers.Count >= 2)
{
List<Buffer> list = buffers;
if (list[list.Count - 1].key == 2)
{
List<Buffer> list2 = buffers;
if (list2[list2.Count - 2].key == 2)
{
data[0].move = Coroutine.Start(Dash(), this);
return;
}
}
}
if (buffers.Count >= 3)
{
List<Buffer> list3 = buffers;
if (list3[list3.Count - 1].key == 4)
{
List<Buffer> list4 = buffers;
if (list4[list4.Count - 2].key == 3)
{
List<Buffer> list5 = buffers;
if (list5[list5.Count - 3].key == 1)
{
if (tp >= 15)
{
tp -= 15;
data[0].move = Coroutine.Start(Hadouken(), this);
return;
}
buffers.Clear();
}
}
}
}
if (Input.IsActionJustPressed(Main.keys[4]))
{
data[0].move = Coroutine.Start(Attack(), this);
return;
}
if (Input.IsActionJustPressed(Main.keys[6]) && (healing == null || healing.done) && tp >= 90)
{
healing = Coroutine.Start(HealBell(), this);
tp -= 90;
}
data[0].block = false;
if (Input.IsActionPressed(Main.keys[2]))
{
fighters[0].Velocity = Vector2.Left * fighters[0].baseSpeed;
fighters[0].anim.Play(anims[Animations.BackWalk]);
}
else if (Input.IsActionPressed(Main.keys[3]))
{
fighters[0].Velocity = Vector2.Right * fighters[0].baseSpeed;
fighters[0].anim.Play(anims[Animations.Walk]);
}
else if (Input.IsActionPressed(Main.keys[5]) || Input.IsActionPressed(Main.keys[1]))
{
fighters[0].anim.Play(anims[Animations.Block]);
fighters[0].Velocity = Vector2.Zero;
data[0].block = true;
}
else
{
fighters[0].anim.Play(anims[Animations.Idle]);
fighters[0].Velocity = Vector2.Zero;
}
}
private IEnumerator Dash()
{
Audio.PlaySound("leaf_dodge.wav");
fighters[0].Modulate = Main.colorGlow;
fighters[0].Velocity = Vector2.Left * fighters[0].baseSpeed * 3f;
fighters[0].anim.Play(anims[Animations.BackWalk]);
for (float a = 0f; a < 20f; a += Main.deltaTime)
{
yield return null;
}
}
private IEnumerator IntroStuff(bool start = false)
{
if (start)
{
data = new FigherData[2]
{
new FigherData
{
entity = fighters[0]
},
new FigherData
{
entity = fighters[1]
}
};
}
for (int i = 0; i < 2; i++)
{
fighters[i].anim.Play(anims[Animations.Idle]);
}
roundAnim.Play("Round" + round);
AudioStreamPlayer sound = Audio.PlaySound("snd_boxing_round" + round + "_bc.wav");
yield return null;
while ((GodotObject.IsInstanceValid(sound) && sound.Playing) || data[1].hp < 100f)
{
for (int j = 0; j < 2; j++)
{
fighters[j].GlobalPosition = fighters[j].GlobalPosition.Lerp(fighters[j].startPos, Main.deltaTime * 0.1f);
}
if (data[1].hp < 100f)
{
data[1].hp += 1f;
}
yield return null;
}
data[1].hp = 100f;
roundAnim.Play("Fight");
sound = Audio.PlaySound("snd_boxing_fight_bc.wav");
yield return null;
while (sound.Playing)
{
yield return null;
}
for (int k = 0; k < 2; k++)
{
Audio.PlaySound("snd_bell_ch1.wav");
for (float a = 0f; a < 15f; a += Main.deltaTime)
{
yield return null;
}
}
Audio.ChangeMusic(music);
help.Visible = true;
if (round == 1)
{
help.Text = Texts.common[121].Replace("[VALUE]", 90.ToString()).Replace("@", "\n");
}
}
public void Start(bool setPos = true)
{
base.ProcessMode = ProcessModeEnum.Inherit;
active = true;
tp = 0;
round = 1;
if (setPos)
{
for (int i = 0; i < fighters.Length; i++)
{
fighters[i].startPos = fighters[i].GlobalPosition;
}
}
busy = Coroutine.Start(IntroStuff(start: true));
}
private void PlayerHit(Node other)
{
if (other.HasMeta(hitbox))
{
Damage(0, (int[])other.GetMeta(hitbox));
}
}
private void EnemyHit(Node other)
{
if (other.HasMeta(hitbox))
{
bool noTP = false;
if (other.GetParent() is MoveAhead moveAhead)
{
proj.Remove(moveAhead);
moveAhead.QueueFree();
noTP = true;
}
Damage(1, (int[])other.GetMeta(hitbox), noTP);
}
}
private void Damage(int id, int[] hitData, bool noTP = false)
{
Main.Particle("ShootHit", data[id].entity.GlobalPosition + new Vector2(Main.RandomRange(-10, 10), -25 + Main.RandomRange(-10, 10)), 1, null, localSpace: false);
if (data[id].block)
{
data[id].hp -= data[id].blockHold + 1f;
if (data[id].blockHold < 5f)
{
data[id].blockHold += 1f;
}
if (id == 1)
{
data[id].armorBuildup += hitData[1] * 5;
}
if (data[id].armorBuildup >= 100f)
{
data[id].hyperArmor += hitData[1];
data[id].entity.Modulate = Main.colorYellow;
}
else
{
data[id].entity.Modulate = Main.colorGlow;
}
if (id == 0 && !noTP)
{
tp = Mathf.Min(tp + 5 - (int)data[id].blockHold, 100);
}
Audio.PlaySound("snd_graze.wav");
}
else
{
data[id].hp -= (float)hitData[0] * ((data[id].hyperArmor > 0f) ? 0.5f : 1f);
if (id == 1)
{
combo++;
comboTime = 60f;
}
else
{
Audio.PlaySound("snd_hurt1.wav");
}
if (data[id].hyperArmor <= 0f)
{
if (data[id].armorBuildup >= 100f)
{
data[id].entity.Modulate = Main.colorYellow;
Audio.PlaySound("snd_justice_effect.wav");
data[id].armorBuildup += 60f;
data[id].hyperArmor += (float)hitData[1] * 1.5f;
if (id == 1 && !noTP)
{
tp = Mathf.Min(tp + 1, 100);
}
}
else
{
Audio.PlaySound("punch_ish_1.wav");
data[id].entity.Modulate = Main.colorGlow;
data[id].knockback = hitData[0] * 5;
Coroutine move = data[id].move;
if (move != null && !move.done)
{
Coroutine.Stop(data[id].move, now: true);
data[id].move = null;
}
if (id == 1)
{
data[id].armorBuildup += (float)hitData[1] * ((id == 0) ? 1.5f : 2f);
}
data[id].entity.Velocity = Vector2.Zero;
data[id].entity.anim.Play(anims[Animations.Hurt]);
if (id == 1 && !noTP)
{
tp = Mathf.Min(tp + Mathf.Min(hitData[1] / 10 * combo, 10), 100);
}
}
}
else
{
if (id == 1 && !noTP)
{
tp = Mathf.Min(tp + 1, 100);
}
data[id].entity.Modulate = Main.colorYellow;
data[id].hyperArmor += 30f;
}
data[id].entity.Shake(10f, 2f);
}
if (data[id].hp <= 0f && (busy == null || busy.done))
{
if (id == 0)
{
fighters[0].anim.Play(anims[Animations.Hurt]);
Coroutine.Start(GameOver.DoGameOver(ChujinDWEvents.instance.nodes[8].GlobalPosition));
base.ProcessMode = ProcessModeEnum.Disabled;
busy = Coroutine.Start(Reset());
}
else
{
StopRoutines();
fighters[1].anim.Play(anims[Animations.Hurt]);
busy = Coroutine.Start(EndRound());
}
}
}
private void StopRoutines()
{
Coroutine.Stop(healing);
for (int i = 0; i < 2; i++)
{
Coroutine.Stop(data[i].move);
}
}
private IEnumerator Reset()
{
for (float a = 0f; a < 30f; a += Main.deltaTime)
{
yield return null;
}
for (int i = 0; i < 3; i++)
{
((CpuParticles2D)fighters[1].extras[i + 1]).Emitting = false;
}
StopRoutines();
for (int j = 0; j < 2; j++)
{
fighters[j].GlobalPosition = fighters[j].startPos;
fighters[j].anim.Play(anims[Animations.Idle]);
}
data = new FigherData[2]
{
new FigherData
{
entity = fighters[0]
},
new FigherData
{
entity = fighters[1]
}
};
round = 1;
tp = 0;
Party.UpdateAnim();
for (float a = 0f; a < 30f; a += Main.deltaTime)
{
yield return null;
}
}
private IEnumerator EndRound()
{
help.Visible = false;
CameraController.Shake(60f, 2f);
fighters[1].anim.Play(anims[Animations.Hurt]);
if (round == 1)
{
((CpuParticles2D)fighters[1].extras[1]).Emitting = true;
data[1].color = Main.colorWhite.Lerp(Main.colorRed, 0.3f);
}
else if (round == 2)
{
((CpuParticles2D)fighters[1].extras[2]).Emitting = true;
((CpuParticles2D)fighters[1].extras[3]).Emitting = true;
data[1].color = Main.colorWhite.Lerp(Main.colorRed, 0.6f);
}
round++;
Audio.PlaySound("snd_explosion.wav");
Engine.TimeScale = 0.5;
if (round > 3)
{
Audio.ChangeMusic((Audio.AudioBlock)null, 5f);
CameraController.Transition(Main.colorWhite, 100f);
}
for (int i = 0; i < 2; i++)
{
data[i].knockback = 60f;
}
while (data[0].knockback > 0f)
{
for (int j = 0; j < 2; j++)
{
DoKnockback(j);
}
yield return null;
}
Engine.TimeScale = 1.0;
for (float a = 0f; a < 60f; a += Main.deltaTime)
{
yield return null;
}
if (round > 3)
{
active = false;
while (!CameraController.transitionRoutine.done)
{
yield return null;
}
yield return null;
for (int k = 0; k < 2; k++)
{
fighters[k].GlobalPosition = fighters[k].startPos;
fighters[k].Velocity = Vector2.Zero;
}
fighters[0].anim.Play(anims[Animations.Idle]);
UI.Visible = false;
EmitSignal(SignalName.OnEnd);
}
else
{
busy = Coroutine.Start(IntroStuff());
}
}
}