最终整理版

This commit is contained in:
2026-06-03 17:04:06 +08:00
commit 959055ce90
1240 changed files with 80570 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
extends NpcScript
# Requirements
var tributeItemID : int = DB.GetCellHash("Apple")
const tributeItemCount : int = 5
const playerLevelRequirement : int = 1
#
func OnStart():
if IsTriggering():
Chat("Focus on your mission!")
else:
Mes("Ah, another challenger! This cave is a proving ground, where the waves of corrupted creatures test your endurance. Think you're ready for what's inside?")
QuestionStart()
func QuestionStart():
Choice("I'm ready. Bring on the waves.", StartFight)
Choice("I need to go. This feels wrong.", Farewell)
Choice("What's this place? It feels strange.", ExplainCave)
Choice("Waves of monsters? Why are they coming?", ExplainWaves)
func ExplainCave():
Mes("Ah, this cave? It's... Alive, in a way. You feel it too, don't you?")
Mes("It breathes Kaore, the corrupted Mana. Everything here was once... More alive. But now it's just hungry, like me.")
QuestionsCave()
func QuestionsCave():
Choice("No more questions.", QuestionStart)
Choice("Who are you? You seem off.", ExplainNpc)
Choice("What exactly happened to you?", ExplainNpcCorruption)
Choice("Why is the cave like this?", ExplainCaveOrigin)
func ExplainWaves():
Mes("Waves... Yes, waves of those lost to Kaore. They are drawn here, hungry for more of the decaying Mana.")
Mes("You will fight them, over and over. But beware... With each wave, the cave itself grows more restless. You might not make it to the end.")
QuestionsWaves()
func QuestionsWaves():
Choice("No more questions.", QuestionStart)
Choice("What happens if I fail?", ExplainFailure)
Choice("Is there no way to cleanse the corruption?", ExplainCorruption)
Choice("Why does Kaore make things this way?", ExplainKaore)
func ExplainNpc():
Mes("Me? Oh, just a fellow survivor... Or maybe a guide? I can't tell anymore.")
Mes("I've spent too long near the Kaore. It's in my blood, in my thoughts... But it's okay. I think?")
Mes("But never mind me. You're here to fight, aren't you?")
QuestionsCave()
func ExplainNpcCorruption():
Mes("Oh, it started small... Whispers in the back of my mind, a strange craving for... Something.")
Mes("But now? I don't remember what I was before. Maybe it's better this way.")
Mes("You don't want this. But here we are.")
QuestionsCave()
func ExplainCaveOrigin():
Mes("This cave was once a Mana wellspring, a place of life and power.")
Mes("But after the Aethyra War, the Mana decayed into Kaore. Now its nothing more than a trap for those who wander too close.")
Mes("But don't worry. You'll either win... Or join the rest of us.")
QuestionsCave()
func ExplainFailure():
Mes("Even if you fall in battle, you need not fear. The Zielite Amulet you carry is more powerful than you may know.")
Mes("When you perish, your Zielite Amulet will pull your soul to the nearest Soul Menhir. Its the only reason I allow people like you to risk their lives here.")
QuestionsWaves()
func ExplainCorruption():
Mes("Ah, the corruption... Kaore, it's what Mana becomes when its left to decay.")
Mes("Mana flows through all living things. When it's pure, it nurtures life. But here... It's been stagnant for far too long.")
Mes("Once the Mana Trees was lost, the balance was broken. Now Kaore festers in places like this.")
Mes("It seeps into everything: creatures, the land, even people. And once you're touched by it, there's no going back.")
QuestionsWaves()
func ExplainKaore():
Mes("Kaore... It's the decayed Mana. It festers where life was once vibrant. Here, in this cave, it thrives.")
Mes("It changes things... Makes them hostile, makes them... Desperate.")
Mes("You'll feel it too if you stay long enough.")
QuestionsWaves()
func StartFight():
var alivePlayerCount : int = AlivePlayerCount()
if IsTriggering():
if alivePlayerCount == 0:
CallGlobal("OnCancel")
else:
Mes("The fight has already begun. You can't start another one now.")
elif alivePlayerCount > 0:
if not HasItem(tributeItemID, tributeItemCount):
Mes("You'll need to bring me 5 apples as a tribute before we begin.")
elif own.stat.level < playerLevelRequirement:
Mes("You're not strong enough for this fight. Come back when you've reached level %d." % playerLevelRequirement)
else:
RemoveItem(tributeItemID, tributeItemCount)
Chat("Ah, you're ready for this! The fight begins in 10 seconds, brace yourself!")
Action(Trigger)
+133
View File
@@ -0,0 +1,133 @@
extends NpcScript
# Mission parameters
var rewardExp : int = 1000
var rewardGP : int = 10000
# Wave parameters
var monstersPool : Array[EntityData] = []
const checkDelay : float = 2.0
const waveDelay : float = 300.0
const maxWave : int = 10
const spawnCenter : Vector2i = Vector2i(54 * 32, 67 * 32)
const spawnRadius : Vector2i = Vector2i(200, 200)
const startFightDelay : float = 10.0
const warmUpTickDelay : float = 1.0
# Local variables
var waveTimer : Timer = null
var waveCount : int = 0
var originalPlayerCount : int = 0
var waveMaxMonsters : int = 0
var playerList : Array[PlayerAgent] = []
#
func OnStart():
for mobID in DB.EntitiesDB:
var mob : EntityData = DB.EntitiesDB[mobID]
if !!(mob._behaviour & AICommons.Behaviour.AGGRESSIVE) and !(mob._behaviour & AICommons.Behaviour.IMMOBILE) and !mob._isBoss:
monstersPool.append(mob)
func OnCancel():
ClearTimer(waveTimer)
ClearPlayerList()
Reset()
if IsTriggering():
Trigger()
ClearTracker()
KillMonsters()
func OnTrigger():
AddTimer(npc, startFightDelay, StartFight)
TickWarmUp(0)
func TickWarmUp(tick : int):
if not IsTriggering():
return
DisplayTracker("Warm Up", tick, int(startFightDelay), "s")
if tick < int(startFightDelay):
AddTimer(npc, warmUpTickDelay, TickWarmUp.bind(tick + 1), "WarmUpTimer")
#
func RemoveFromPlayerList(player : PlayerAgent):
playerList.erase(player)
func ClearPlayerList():
for player in playerList:
Callback.ClearOneShot(player.tree_exiting)
Callback.ClearOneShot(player.agent_killed)
playerList.clear()
func StartFight():
Reset()
var ownInstance : WorldInstance = WorldAgent.GetInstanceFromAgent(own)
if ownInstance:
for player : PlayerAgent in ownInstance.players:
if ActorCommons.IsAlive(player):
playerList.append(player)
Callback.OneShotCallback(player.tree_exiting, RemoveFromPlayerList.bind(player))
Callback.OneShotCallback(player.agent_killed, RemoveFromPlayerList.bind(player))
originalPlayerCount = playerList.size()
waveTimer = AddTimer(npc, waveDelay, TimeoutWave)
NextWave()
func NextWave():
waveCount += 1
if waveCount > maxWave:
Reward()
else:
Notification("Wave %d." % waveCount)
SpawnMonsters()
waveMaxMonsters = AliveMonsterCount()
DisplayTracker("Monsters", 0, waveMaxMonsters)
waveTimer.start(waveDelay)
AddTimer(npc, checkDelay, CheckWave, "CheckTimer")
func Reset():
waveTimer = null
waveCount = 0
originalPlayerCount = 0
waveMaxMonsters = 0
func Reward():
Notification("Congrats, you won")
ClearTracker()
for player : PlayerAgent in playerList:
NpcCommons.AddExp(player, rewardExp)
NpcCommons.AddGP(player, rewardGP)
ClearPlayerList()
Reset()
if IsTriggering():
Trigger()
func SpawnMonsters():
var isStuck : bool = false
var remainingPoints : int = (5 * waveCount) + ceili(originalPlayerCount * 0.7)
monstersPool.shuffle()
while(remainingPoints > 0 and not isStuck):
isStuck = true
for mob in monstersPool:
var mobPoint : int = mob._stats["Level"] if "Level" in mob._stats else 1
if mobPoint > 0 and mobPoint <= remainingPoints:
var spawnAmount : int = randi_range(1, int(remainingPoints / floor(mobPoint)))
remainingPoints -= mobPoint * spawnAmount
Spawn(mob._id, spawnAmount, spawnCenter, spawnRadius)
func CheckWave():
if not IsTriggering():
return
if AlivePlayerCount() == 0:
OnCancel()
else:
var mobCount : int = AliveMonsterCount()
if mobCount > 0:
waveMaxMonsters = max(waveMaxMonsters, mobCount)
DisplayTracker("Monsters", waveMaxMonsters - mobCount, waveMaxMonsters)
AddTimer(npc, checkDelay, CheckWave, "CheckTimer")
else:
NextWave()
func TimeoutWave():
Notification("You were not able to clear the corruption in time.")
OnCancel()