最终整理版

This commit is contained in:
2026-06-03 17:04:06 +08:00
commit 959055ce90
1240 changed files with 80570 additions and 0 deletions
@@ -0,0 +1,37 @@
extends NpcScript
#
func OnStart():
Mes("被派到内墙值守,算是守卫里难得的轻松差事。")
Mes("外墙那边要盯野兽和沙暴,我这里主要负责把好奇的人拦在宫门外。")
Mes("红女王 正在照料她的花园。除非你真有要紧事,否则别打扰她。")
OnMainChoice()
# Main choice loop
func OnMainChoice():
Choice("这座大建筑是什么?", OnCastle)
Choice("西边通向哪里?", OnWest)
Choice("南边是什么地方?", OnSouth)
Choice("我先走了。", Farewell)
# Answers
func OnCastle():
Mes("那是 红女王 的城堡,也是 图利姆沙 的中枢。")
Mes("如果你有真正重要的消息,可以申请觐见;如果只是想看热闹,我建议你转身去市场。")
Mes("城堡里还有大陆第二大的藏书馆,书量几乎能追上 玛纳伊尔 塔。法师们听到这句通常会假装没听见。")
OnMainChoice()
func OnWest():
Mes("西边是 祖尼石台地 和更荒的石台地。路不直,风也不讲道理。")
Mes("那一带有蛇、盗贼留下的旧营地,也有比我们更懂沙漠的部族。别把陌生土地当成空地图。")
Mes("真要过去,就贴着路标走,水袋装满,别随便碰看起来像祭坛或陷阱的东西。两者通常都不欢迎客人。")
OnMainChoice()
func OnSouth():
Mes("南边能看到旧仙人掌田和废弃住区。那里以前有人住,现在只剩风声和倒下的围栏。")
Mes("沙暴一年比一年近,最后把大家逼回城墙后面。")
Mes("卡奥雷 最近又不安分,守卫队不想再失去一片街区。你如果去南边,至少带一只可靠的宠物。")
OnMainChoice()
func Farewell():
Chat("别在宫门前逗留太久。")
+25
View File
@@ -0,0 +1,25 @@
extends NpcScript
#
func OnStart():
match randi_range(0, 5):
0: SeenClay()
1: GoingToSea()
2: Circles()
3: Chat("那块石头像不像一只睡着的小宠物?我觉得很像。")
4: Chat("小心躲起来的海盗!他们最喜欢抢没有写名字的藏宝图。")
5: Chat("别擦掉地上的粉笔线,我还没画完港口呢!")
func SeenClay():
Mes("你看到我的粉笔了吗?")
Mes("我猜是守卫拿走了。他们站岗太无聊,肯定想偷偷画自己的城堡。")
func GoingToSea():
Mes("总有一天我要坐船出海,去看看 图利姆沙 以外的世界。")
Mes("我听说北边有一座城市,沙子又白又冷,踩上去会像面粉一样响。")
func Circles():
Mes("你会画很圆很圆的圆吗?我会!爸爸教我的。")
Mes("薇画不圆,她画出来的圆像吃饱了的方块。")
Mes("她不喜欢我笑,所以玩井字棋时总要执叉号。")
Mes("我现在叫她狂野的叉。她说这个名字听起来像冒险队队长。")
@@ -0,0 +1,7 @@
extends NpcScript
#
func OnStart():
match GetQuest(ProgressCommons.Quest.GRAIN_IN_THE_SAND):
ProgressCommons.GRAIN_IN_THE_SAND.STARTED:
Chat("一个封好的货桶。蜡封颜色不对,不是里斯基姆要找的那批。")
+50
View File
@@ -0,0 +1,50 @@
extends NpcScript
#
const QUEST_ID : int = ProgressCommons.Quest.TULIMSHAR_OLD_FRIENDSHIP
var sealedLettersID : int = DB.GetCellHash("Sealed Letters")
#
func OnStart():
var questState : int = GetQuest(QUEST_ID)
match questState:
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.ENVELOPES_FOUND:
ReceiveLetters()
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.LETTERS_DELIVERED, \
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.REWARDS_WITHDREW:
FreeRoaming()
_:
TulimsharWestWallLightTrigerGlobal.CallGuard(own)
func ReceiveLetters():
if HasItem(sealedLettersID):
Mes("你是谁?")
Choice("弗罗斯特让我来的。他让我把这些给你。", GiveLetters)
else:
Mes("你看起来有话要说。可你手里什么都没有。")
Mes("有东西要给我时再回来。我还有墙要巡。")
func GiveLetters():
Mes("什么。他让你带这些来?")
Narrate("议员博恩斯翻看那些信,手指在封口处停了很久。")
Mes("我认得这笔迹。")
Mes("很旧了。那时 女王 还把我们分派在不同岗位,我们甚至还没守同一段墙。")
Mes("弗罗斯特处理人。我处理石头。分工就是这样。")
Mes("有一阵子,这办法很好。")
Mes("后来 女王 不断加压。更多命令,更多临时要求,而且永远不能只把事情做好。")
Mes("弗罗斯特开始给一切立规程。我说,我们已经够难了,不需要再多一层压力。")
Mes("他说我把他关在外面,总是不告诉任何人就做决定。")
Mes("也许他说得对。语言不是我的工具,从来不是。")
Mes("我只想把东西建好,然后让别人停止争论我为什么这么建。")
Mes("最后我叫他走。他走了。故事到此为止。")
Mes("...")
Mes("显然并没有。否则我不会还在这里,守着一段没人真的想攻破的墙。")
Mes("除了你。不过既然弗罗斯特让你来,那另当别论。")
RemoveItem(sealedLettersID)
SetQuest(QUEST_ID, ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.LETTERS_DELIVERED)
Mes("把这个信封带回去给他。他会知道里面是什么。")
Mes("再告诉他,城墙还在。就这句。他会明白。")
func FreeRoaming():
Mes("又是你。走廊现在对你开放,我说话算数。")
Mes("我旁边的图书架后有条通道,可以通到城墙外。知道的人不多,别让它变成市场传闻。")
+137
View File
@@ -0,0 +1,137 @@
extends NpcScript
#
func OnStart():
var questState : int = GetQuest(ProgressCommons.Quest.TUTORIAL)
if questState < ProgressCommons.TUTORIAL.ELANORE_DONE:
OnSendToKaelForRecovery()
elif questState < ProgressCommons.TUTORIAL.KAEL_DONE:
OnSendToKael()
elif questState < ProgressCommons.TUTORIAL.EKINU_DONE:
OnKaelReport()
else:
OnComplete()
#
func OnSendToKaelForRecovery():
Mes("你已经醒了?")
LookAtNpc("Kael")
Mes("先去找凯尔。他负责确认你能不能上路,也会给你说明为什么必须选择一只初始宠物。")
ResetCamera()
func OnSendToKael():
Mes("你看见那些沙虫了吗?")
Mes("恶心,但不能忽视。听老人说,它们以前没这么大,也没这么多。")
Mes("仙人掌农户受影响最大。沙虫啃坏作物,仙人掌怪又被 卡奥雷 侵蚀,再拖下去城里的药材和食物都会出问题。")
LookAtNpc("Kael")
Mes("看守凯尔在东边的仙人掌田附近。")
ResetCamera()
Mes("如果你想证明自己不是巡逻队捡回来的麻烦,就去帮他清理田地。")
# Kael's report
func OnKaelReport():
Mes("凯尔那边处理完了?")
Choice("处理完了。凯尔说我能帮你们的远征。", OnExpedition)
func OnExpedition():
Mes("嗯,他判断得没错。")
Mes("我们确实还缺一个能应付突发战斗的人。城墙不能抽走太多守卫,你这种外来帮手反而合适。")
Mes("你要往南走,去沙漠风暴矿洞。名字不是吓唬人的,那片谷地风向混乱,沙会把路和怪物一起藏起来。")
Choice("为什么要去那里?", OnWhyExpedition)
Choice("我具体要做什么?", OnJobExplanation)
func OnWhyExpedition():
Mes("那片矿洞以前盛产铁矿,整片区域又算红女王的私产,所以王宫一直惦记着它。")
Mes("后来沙暴加重,矿工不是被风吞掉,就是被矿道里的怪物袭击,矿洞才被迫废弃。")
Mes("现在王宫缺钱,女王又下令不惜代价重开矿洞。")
Mes("她当然不会亲自去确认下面有什么危险,受苦的永远是被派去干活的人。")
Choice("我具体要做什么?", OnJobExplanation)
func OnJobExplanation():
Mes("你的任务是侦察矿洞路线,确认旧矿道里有没有足以杀死矿工的大威胁。")
Mes("能避开的就记录,必须清理的就处理。别把每场战斗都当成证明自己的机会。")
if GetQuest(ProgressCommons.Quest.TUTORIAL) < ProgressCommons.TUTORIAL.EKINU_DONE:
var shortSwordID : int = DB.GetCellHash("Short Sword")
var desertGogglesID : int = DB.GetCellHash("Desert Goggles")
Mes("出城前不能让你空手去。")
Mes("拿着短剑和沙漠护目镜。风沙会抢走视线,怪物会抢走犹豫的时间。")
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.EKINU_DONE)
AddItem(shortSwordID)
AddItem(desertGogglesID)
AddExp(50)
Mes("这些足够让你自保,但不保证你能赢下所有战斗。")
Mes("遇到危险就跑。死在沙地里没有荣耀,只会让别人多挖一个坑。")
TeachSkill(DB.GetCellHash("Run"))
DisplayActions(["gp_run"])
Narrate("按住奔跑键可以冲刺,用来赶路或脱离危险。")
Narrate("冲刺会消耗耐力,别在真正需要逃命前把耐力耗空。")
Mes("差不多准备好了。")
LookAtNpc("Gilgames")
Mes("不过出城前先找吉尔伽美什说几句。他守着城门,见过太多新人把简单错误变成遗言。")
ResetCamera()
Mes("其他守卫已经先出发了。到城外找看守道森,他会告诉你矿洞入口怎么走。")
if GetQuest(ProgressCommons.Quest.MINE_EXPLORATION) < ProgressCommons.MINE_EXPLORATION.STARTED:
SetQuest(ProgressCommons.Quest.MINE_EXPLORATION, ProgressCommons.MINE_EXPLORATION.STARTED)
OnMainChoice()
# Main choice loop
func OnMainChoice():
if GetQuest(ProgressCommons.Quest.MINE_EXPLORATION) == ProgressCommons.MINE_EXPLORATION.STARTED:
Choice("再说一遍我的任务。", OnJobExplanation)
Choice("红女王是什么样的人?", OnRedQueen)
Choice("遇到危险怎么办?", OnStrangerDanger)
Choice("明白了,我出发。", Farewell)
# Attack and flee
func OnStrangerDanger():
Mes("能先手就先手,不能赢就撤。")
Mes("你的宠物不是用来替你送死的,受伤太重就换路线、补给、或者直接回城。")
DisplayActions(["gp_target"])
DisplayActions(["gp_interact"])
DisplayActions(["gp_run"])
Narrate("按住奔跑键可以冲刺,也可以在危险时拉开距离。")
Narrate("冲刺会消耗耐力,需要时再用。")
OnMainChoice()
# 红女王 chain
func OnRedQueen():
Mes("红女王,正式点叫卡罗琳娜一世,是这座城名义上的统治者。")
Mes("她还自称 托诺里 女王,好像城墙外的沙子也会听她命令。")
Mes("她说自己继承了古代 白金王朝 的血统。实际情况是,她父亲原本只是个非常聪明的仙人掌农户。")
Choice("听起来你很讨厌她。", OnDislikeQueen)
Choice("她父亲是农户?", OnRedQueenFather)
func OnRedQueenFather():
Mes("是。他让临终的老国王相信自己是私生子。")
Mes("老国王没有别的孩子,又确实喜欢他,就把继承权给了他。公平地说,她父亲很聪明,也比现在的女王更懂治理。")
Mes("可他死后,女儿只剩下王冠和故事。没人真心喜欢她,所以她更需要血统传说来撑住面子。")
Choice("听起来你很讨厌她。", OnDislikeQueen)
func OnDislikeQueen():
Mes("讨厌这个词太轻了。")
Mes("她冷漠、自私,只关心王宫花园和自己的脸面。")
Mes("别到处说是我讲的。巡逻队不怕风沙,但没人想因为几句真话被王宫找麻烦。")
Mes("她关心那些花,胜过关心被派去矿洞送命的人。")
Choice("那我们最好先做正事。", OnMainChoice)
# Expedition started
func Farewell():
if randi() % 2:
Chat("保持警觉。")
else:
Chat("完整地回来。")
func OnComplete():
var questState : int = GetQuest(ProgressCommons.Quest.MINE_EXPLORATION)
if questState == ProgressCommons.MINE_EXPLORATION.STARTED:
Mes("准备出发时,去城外找看守道森。")
else:
Mes("你很幸运,在哥布林发现你之前先被我们捡到。看到你恢复得不错,我也放心些。")
OnMainChoice()
+164
View File
@@ -0,0 +1,164 @@
extends NpcScript
#
func OnStart():
var questState : int = GetQuest(ProgressCommons.Quest.TUTORIAL)
match questState:
ProgressCommons.TUTORIAL.INACTIVE:
OnFirstMeeting()
ProgressCommons.TUTORIAL.INTRO_ITEMS_GIVEN:
OnSendToKaelForCheck()
ProgressCommons.TUTORIAL.POTION_GIVEN:
OnSendToKaelForCheck()
ProgressCommons.TUTORIAL.CLOTHES_GIVEN:
OnSendToKaelForCheck()
ProgressCommons.TUTORIAL.UI_EXPLAINED:
OnSendToKaelForCheck()
_:
Mes("你回来了。需要药剂,还是想问些城里的事?")
OnMainChoice()
# First meeting
func OnFirstMeeting():
var waterBottleID : int = DB.GetCellHash("Water Bottle")
var cactusSourCandyID : int = DB.GetCellHash("Cactus Sour Candy")
Mes("醒了?欢迎来到图利姆沙。")
Mes("你差一点就倒在城墙看不见的地方了。巡逻队在沙地边缘发现你时,你已经晒到说不出话。")
Mes("我是埃拉诺。先喝点水,再吃这块仙人掌酸糖。别急着站太久,你的身体还没完全缓过来。")
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.INTRO_ITEMS_GIVEN)
AddItem(waterBottleID)
AddItem(cactusSourCandyID)
OnFeelingChoice()
func OnFeelingChoice():
Choice("我好多了,谢谢。", OnFeelingBetter)
Choice("还是有点虚弱。", OnFeelingWeak)
func OnFeelingWeak():
var cactusDrinkID : int = DB.GetCellHash("Cactus Drink")
Mes("那再拿一杯仙人掌饮料。剩下的就只能靠时间了,亲爱的。")
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.POTION_GIVEN)
AddItem(cactusDrinkID)
OnFeelingBetter()
func OnFeelingBetter():
Mes("能回答问题就好。关于你为什么会独自出现在沙漠里,凯尔会继续问。")
Mes("他负责巡逻队的初步确认,也负责给刚恢复的人安排第一只同行宠物。")
OnSendToKaelForCheck()
func OnGiveStarterClothes():
var cottonShirtID : int = DB.GetCellHash("Cotton Shirt")
var linenShortsID : int = DB.GetCellHash("Shorts")
Mes("不管你从哪里来,能站起来就是好事。图利姆沙需要愿意帮忙的人。")
Mes("最近沙漠越来越不安稳。东边山里有一群迷信 卡奥雷 的狂热者,袭击旅人,甚至曾经差点冲破城墙。")
Mes("先把这身破布换掉吧。城里人会看衣服判断你是不是刚从沙地里被捡回来。")
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.CLOTHES_GIVEN)
AddItem(cottonShirtID, 1, "Used")
AddItem(linenShortsID, 1, "Used")
OnExplainUI()
func OnExplainUI():
Mes("放你到城里乱跑之前,我先讲几件保命的事。")
HighlightUI(UICommons.UITarget.STATINDICATOR)
Narrate("这些是你的关键状态。探索和战斗时要随时留意。")
HighlightUI(UICommons.UITarget.HEALTHBAR)
Narrate("生命值代表你还能承受多少伤害。")
HighlightUI(UICommons.UITarget.MANABAR)
Narrate("玛纳 支撑技能释放。玛纳 不足时,许多能力无法使用。")
HighlightUI(UICommons.UITarget.STAMINABAR)
Narrate("耐力影响奔跑、攻击和体力动作。耗尽后会明显变慢。")
HighlightUI(UICommons.UITarget.MENUINDICATOR)
Narrate("菜单可以查看背包、技能、任务、设置等信息。")
HighlightUI(UICommons.UITarget.ACTION_BAR)
Narrate("快捷栏可以放常用技能和物品。战斗时每一秒都很重要。")
HighlightUI(UICommons.UITarget.INVENTORY)
Narrate("背包窗口可以管理物品、装备、药剂和收集品。")
HighlightUI(UICommons.UITarget.NONE)
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.UI_EXPLAINED)
Action(OnMainChoice)
# Main choice loop
func HasAllIngredients() -> bool:
return HasItem(DB.GetCellHash("Maggot Slime"), 6) and HasItem(DB.GetCellHash("Water Bottle")) and HasItem(DB.GetCellHash("Cactus Drink"))
func OnMainChoice():
var sideQuestState : int = GetQuest(ProgressCommons.Quest.ELANORE_POTION)
if sideQuestState == ProgressCommons.ELANORE_POTION.STARTED and HasAllIngredients():
Choice("我带来了你要的材料。", OnPotionQuestTurnIn)
elif sideQuestState == ProgressCommons.ELANORE_POTION.STARTED:
Choice("药剂材料还差什么?", OnPotionQuestReminder)
else:
Choice("有什么我能帮忙的吗?", OnHelpWithPotions)
Choice("卡奥雷 是什么?", OnExplainKaore)
Choice("你是谁?", OnExplainSelf)
if GetQuest(ProgressCommons.Quest.TUTORIAL) >= ProgressCommons.TUTORIAL.ELANORE_DONE:
Choice("谢谢,我先走了。", Farewell)
# Help with potions
func OnHelpWithPotions():
Mes("当然。我经常给守卫和居民调制治疗药剂,伤口、脱水和轻度 卡奥雷 侵蚀都能先稳住。")
Mes("药剂需要材料。你刚恢复,先做点不太危险的收集工作,也能熟悉城外生态。")
Mes("我需要 6 份沙虫黏液、1 瓶水和 1 杯仙人掌饮料。带回来后,我会给你一瓶仙人掌药剂。")
SetQuest(ProgressCommons.Quest.ELANORE_POTION, ProgressCommons.ELANORE_POTION.STARTED)
if GetQuest(ProgressCommons.Quest.TUTORIAL) < ProgressCommons.TUTORIAL.ELANORE_DONE:
OnSendToKael()
else:
OnMainChoice()
func OnPotionQuestReminder():
Mes("还需要 6 份沙虫黏液、1 瓶水和 1 杯仙人掌饮料。收齐后再来找我。")
OnMainChoice()
func OnPotionQuestTurnIn():
Mes("谢谢你。这些材料够我马上调一批新的治疗药剂。")
Mes("在城门边帮人,比困在高塔或王宫里讨论规矩有用得多。图利姆沙的领导者真该多看看这里。")
RemoveItem(DB.GetCellHash("Maggot Slime"), 6)
RemoveItem(DB.GetCellHash("Water Bottle"))
RemoveItem(DB.GetCellHash("Cactus Drink"))
SetQuest(ProgressCommons.Quest.ELANORE_POTION, ProgressCommons.ELANORE_POTION.INACTIVE)
AddItem(DB.GetCellHash("Cactus Potion"))
OnMainChoice()
# What is 卡奥雷
func OnExplainKaore():
Mes("玛纳 是连接生命的能量。卡奥雷 则像腐败的 玛纳,它不滋养生命,只会扭曲、侵蚀,让生物变得狂躁甚至半死不活。")
LookAtNpc("Nina")
Mes("如果你想知道更多,去问我的学徒 妮娜。她守着城里的 灵魂石碑,对 玛纳 和 卡奥雷 的解释比我更系统。")
ResetCamera()
OnMainChoice()
# Who are you
func OnExplainSelf():
Mes("我是埃拉诺,图利姆沙和周边土地的 卡维。外人通常把我们称作德鲁伊。")
Mes("我所属的 卡乌马图阿 传承守护着很古老的知识。不过现在你不用背这些名词。先活下来,再慢慢了解这个世界。")
OnMainChoice()
func Farewell():
if randi() % 2:
Chat("出城前检查水和药。")
else:
Chat("别一个人硬闯沙暴。")
# Tutorial conclusion
func OnSendToKael():
Mes("我不能把你留在这里一整天。你需要在城里站稳脚跟。")
Mes("看守凯尔正负责城墙内侧的巡逻和仙人掌田。如果你想找点正经事做,先去见他。")
LookAtNpc("Kael")
Mes("他就在东北边,靠近几棵棕榈树。")
Mes("沿着这面墙走到拐角,再往上走就能看见他。")
ResetCamera()
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.ELANORE_DONE)
DisplayActions(["gp_interact", "gp_target"])
Narrate("靠近 角色 后使用互动键可以交谈,也可以先用目标键选中对象。")
func OnSendToKaelForCheck():
Mes("先去找凯尔。")
Mes("你的身体能不能撑住外面的风、你醒来前发生了什么、还有初始宠物的选择,都由他来说明。")
LookAtNpc("Kael")
Mes("他就在东北边,靠近几棵棕榈树。沿着这面墙走到拐角,再往上走就能看见他。")
ResetCamera()
DisplayActions(["gp_interact", "gp_target"])
Narrate("靠近凯尔后使用互动键交谈。")
@@ -0,0 +1,7 @@
extends NpcScript
#
func OnAreaEnter(player : PlayerAgent):
if player and not player.ownScript:
if player.progress.GetQuest(ProgressCommons.Quest.TUTORIAL) == ProgressCommons.TUTORIAL.INACTIVE:
own.Interact(player)
@@ -0,0 +1,12 @@
extends NpcScript
#
func OnStart():
match GetQuest(ProgressCommons.Quest.GRAIN_IN_THE_SAND):
ProgressCommons.GRAIN_IN_THE_SAND.STARTED:
OnSearch()
func OnSearch():
SetQuest(ProgressCommons.Quest.GRAIN_IN_THE_SAND, ProgressCommons.GRAIN_IN_THE_SAND.SEARCHED_CRATES)
Mes("深蓝色蜡封上压着 阿尔蒂斯 的纹章。就是这一桶。")
Mes("里面是几袋磨得很细的面粉,和里斯基姆描述的一样。")
+107
View File
@@ -0,0 +1,107 @@
extends NpcScript
#
const QUEST_ID : int = ProgressCommons.Quest.TULIMSHAR_OLD_FRIENDSHIP
var sealedLettersID : int = DB.GetCellHash("Sealed Letters")
var heavyEnvelopeID : int = DB.GetCellHash("Heavy Envelope")
#
func OnStart():
var questState : int = GetQuest(QUEST_ID)
match questState:
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.INACTIVE:
QuestInactive()
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.STARTED:
QuestStarted()
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.ENVELOPES_FOUND:
QuestEnvelopesFound()
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.LETTERS_DELIVERED:
QuestRewards()
ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.REWARDS_WITHDREW:
QuestCompleted()
func QuestInactive():
Mes("小心脚下,新的仙人掌刚冒头,刺比看起来更快。")
Mes("抱歉,我不太习惯有人来这张长椅边聊天。离开宫里以后,访客就少多了。")
Mes("我以前是宫廷顾问。几年前辞了职,那地方每个人肩上都压着太多东西。")
Mes("现在这里只有我、土和仙人掌。至少种出来的东西能帮城里人熬过旱季。")
Mes("你有没有反复想过同一个决定?如果当时换一种说法、晚一点开口,事情会不会完全不同?")
Choice("你在想什么?", Lore)
Choice("我该走了。", Dismiss)
func QuestStarted():
Mes("还在找吗?西墙走廊不欢迎没有许可的人。")
Choice("再说说博恩斯。", Lore)
Choice("信在哪里?", Directions)
Choice("我会处理。", Dismiss)
func QuestEnvelopesFound():
Mes("你找到了。好,好。")
Mes("把封好的信交给议员博恩斯。我刚才看见你离开后,他立刻进了走廊。")
Mes("他的直觉一直很准。也可能是你太努力不被发现,反而弄出了动静。总之,他会在那里面。")
func QuestRewards():
if HasItem(heavyEnvelopeID):
Mes("你回来了。他说了什么。")
Mes("这个信封,很重。")
Mes("...")
Mes("金币。他把金币放在这里,还写了我的名字。")
Mes("女王 第一次把我们派到同一段城墙时,我们各自留了一份钱给对方。万一哪天事情糟到必须离开,至少还有路费。")
Mes("我以为我走之后,他早就把这份用掉了。如果他真用了,我也不会怪他。")
Mes("我配不上这封信。它该给一个比我更懂得做朋友的人。")
Mes("拿着吧。你一个下午做成的事,比我们这些年做得都多。")
RemoveItem(heavyEnvelopeID)
AddGP(1000)
SetQuest(QUEST_ID, ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.REWARDS_WITHDREW)
else:
Mes("你找到博恩斯了吗?他在西墙走廊里。")
func QuestCompleted():
Mes("今年的仙人掌长势不错。看起来,只要根还在,很多东西都能重新开始。")
Mes("我一直在想,我应该去那条走廊。不是今天,但我已经决定会去。")
Mes("他一直留着那些信。这说明有些门并没有真的关死,对吧?")
func Lore():
Mes("我曾经有个同僚,议员博恩斯。很聪明的人,什么都能修,什么都能造。")
Mes("只是他话少。比起解释一堵墙为什么该重修,他宁愿直接把墙拆了再砌好。")
Mes("而我负责和人打交道:排班、调解争执、让每个人知道下一步该做什么。")
Mes("我们一起撑住这些城墙很多年。在 女王 手下,这不算小事。")
Choice("后来发生了什么?", Conflict)
Choice("我该走了。", Dismiss)
func Conflict():
Mes("女王 的命令越来越多,而且永远要按她的方式做。")
Mes("我开始制定规程。什么事都写流程,什么错误都加一条规则。我以为只要把一切整理清楚,压力就不会压垮大家。")
Mes("博恩斯不这么看。他说我只是把更多压力堆到已经快撑不住的人身上。")
Mes("可他也会直接动手、直接决定,不告诉我,也不告诉别人。我总是在事后才知道。")
Mes("我们都想守住同一段墙。只是走着走着,我们不再讨论怎么守,而是在争谁的办法才算正确。")
Mes("现在回头看,我犯过太多错,没资格说自己全对。但那时候,我们谁也不肯退一步。")
Mes("最后他让我离开。我真的离开了。我不为这件事骄傲,只是当时觉得自己碍了所有人的路。")
Mes("那已经是很多年前了,可我还是常常想起。")
if GetQuest(QUEST_ID) == ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.INACTIVE:
Choice("我能帮上什么吗?", Quest)
Choice("听起来很遗憾。", Dismiss)
func Quest():
Mes("其实,也许有。")
Mes("就在我们左边的西墙走廊尽头,穿过议员博恩斯的巡逻区,有个小房间,里面有一排书架。")
Mes("书架上有一个信封。那是我们当年被 女王 分派到不同岗位时写给彼此的信,在一切变坏之前。")
Mes("我不知道它能不能改变什么。但也许他重新读到那些字,会想起我们并不总是这样。")
Mes("走廊有守卫巡逻。贴着阴影走,别站进亮处。被发现的话,守卫会把你带出去。")
Choice("我去取。", Accept)
Choice("现在不行。", Decline)
func Directions():
Mes("就在左边西墙走廊的尽头,图书架上方。")
Mes("记住,别进亮处。守卫接到的是严格命令:没有许可的人一律带离。")
func Accept():
SetQuest(QUEST_ID, ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.STARTED)
Mes("谢谢你,真的。")
Directions()
func Decline():
Chat("我明白。这对陌生人来说,确实要求太多了。")
func Dismiss():
Chat("好吧。仙人掌不会自己照顾自己。")
@@ -0,0 +1,58 @@
extends NpcScript
#
func OnStart():
var questState : int = GetQuest(ProgressCommons.Quest.TUTORIAL)
if questState >= ProgressCommons.TUTORIAL.EKINU_DONE:
Mes("你就是艾基努临时收下的新人?很好,至少你还知道先问守门人。")
Mes("出城之后,沙子不会听你解释,怪物也不会等你准备好。想问什么,现在问。")
OnTutorialResume()
else:
Mes("离城门远点。还没得到巡逻队许可的人,不要堵在这里。")
# Common questions
func OnTutorialResume():
Choice("我该怎么变强?", OnStat)
Choice("如果迷路了怎么办?", OnMinimap)
Choice("背包和快捷栏怎么用?", OnShortcut)
Choice("城外最该小心什么?", OnWarning)
Choice("没问题了。", Farewell)
func Farewell():
Chat("活着回来。别让我多写一份报告。")
# Explanations
func OnStat():
Mes("完成委托、战斗、探索和帮助别人都会让你成长。别只盯着一条路,托诺里 的麻烦从来不排队。")
HighlightUI(UICommons.UITarget.STAT)
Narrate("升级后可以获得属性点。按照你的战斗方式分配,再确认选择。")
Narrate("力量提高物理伤害、移动速度和负重。")
Narrate("敏捷影响攻击节奏、攻击距离和闪避能力。")
Narrate("体质提高生命值、恢复能力和整体防御。")
Narrate("耐力让你跑得更久、恢复更快,也更容易连续行动。")
Narrate("专注影响 玛纳,并让技能更稳定地发挥效果。")
HighlightUI(UICommons.UITarget.STATINDICATOR)
Narrate("出城前看一眼生命、玛纳 和耐力。带着空条冲进荒野,不叫勇敢,叫给巡逻队添工作。")
HighlightUI(UICommons.UITarget.NONE)
Mes("你会需要每一点优势。沙漠里的敌人不一定强,但它们很擅长等你犯错。")
OnTutorialResume()
func OnMinimap():
Mes("地图是你的第二双眼睛。看不清路时先看地图,别硬凭记忆穿沙暴。")
HighlightUI(UICommons.UITarget.MINIMAP)
Narrate("迷路时可以在地图上确认目标位置,再朝目标移动。")
HighlightUI(UICommons.UITarget.NONE)
OnTutorialResume()
func OnShortcut():
Mes("常用的药、捕捉道具和技能都放到顺手的位置。等沙蝎扑到脸上再翻包,就太晚了。")
HighlightUI(UICommons.UITarget.ACTION_BAR)
Narrate("可以把常用物品、技能或动作放进快捷栏,战斗和探索时更快使用。")
HighlightUI(UICommons.UITarget.NONE)
OnTutorialResume()
func OnWarning():
Mes("第一,小心风。风沙会遮住路,也会遮住正在靠近你的东西。")
Mes("第二,别把宠物当成工具。它们会救你的命,但前提是你也照顾它们。")
Mes("第三,别相信王宫说的每一句话。巡逻队守的是城,不是红女王的面子。")
OnTutorialResume()
+232
View File
@@ -0,0 +1,232 @@
extends NpcScript
# Behaviour
const ThresholdHandValue : int = 17
# Card utilities
static var RANKS : PackedStringArray = ["王牌", "2", "3", "4", "5", "6", "7", "8", "9", "10", "侍从", "王后", "国王"]
static var SUITS : PackedStringArray = [
"黑桃",
"红心",
"方片",
"梅花"
]
# Game state
var deck : Array = []
var playerHand : Array = []
var dealerHand : Array = []
# Helper
static func _CardName(card : int) -> String:
return "%s%s" % [SUITS[floori(card / 13.0)], RANKS[card % 13]]
static func _CardValue(card : int) -> int:
var rank : int = card % 13
if rank == 0: return 11
if rank >= 10: return 10
return rank + 1
static func _HandValue(hand : Array) -> int:
var value : int = 0
var aces : int = 0
for card : int in hand:
value += _CardValue(card)
if card % 13 == 0:
aces += 1
while value > 21 and aces > 0:
value -= 10
aces -= 1
return value
static func _HandStr(hand : Array) -> String:
var parts : PackedStringArray = []
for card : int in hand:
parts.append(_CardName(card))
return ", ".join(parts) + " (%d)" % _HandValue(hand)
static func _NewDeck() -> Array:
var d : Array = []
d.resize(52)
for i : int in range(52):
d[i] = i
d.shuffle()
return d
# General flow
func OnStart():
Mes("欢迎,欢迎!你是来找人,还是来找一局好牌?")
Mes("听过 21 点 吗?别被名字吓到,规矩简单,胆量和分寸才是关键。")
Mes("坐下吧。图利姆沙 的风会吹乱很多东西,但吹不乱一副洗好的牌。")
DisplayChoices()
func DisplayChoices():
Choice("挑战 海兰德", StartDealer)
Choice("帮我找个对手", StartPvP)
Choice("讲讲规则", ShowRules)
Choice("今天先不玩", Decline)
func ShowRules():
Mes("规则很简单:尽量接近 21 点,但不能超过。")
Mes("数字牌按牌面算,花牌都算 10。王牌可以算 1,也可以算 11,看哪种对你更有利。")
Mes("比我点数高且不爆牌,你就赢。超过 21,就是爆牌。")
Mes("这游戏最难的不是加法,是知道什么时候该停手。许多商人一辈子都没学会。")
DisplayChoices()
func Decline():
Chat("想玩牌的时候来找我。好牌不会等人太久。")
func OnQuit():
if npc and npc.ownScript:
npc.ownScript.call("LeavePvP", own)
super.OnQuit()
# Player vs Dealer Mode
func StartDealer():
deck = _NewDeck()
playerHand = [deck.pop_back(), deck.pop_back()]
dealerHand = [deck.pop_back(), deck.pop_back()]
_DealerTurn()
func _DealerTurn():
if _HandValue(playerHand) == 21:
_DealerReveal()
else:
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("我明面上的牌:%s" % _CardName(dealerHand[0]))
Choice("要牌", _DealerHit)
Choice("停牌", _DealerReveal)
func _DealerHit():
playerHand.append(deck.pop_back())
var value : int = _HandValue(playerHand)
if value > 21:
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("哎呀,爆牌了!贪心常常只差一张牌,我见过太多次。")
Choice("再来一局", StartDealer)
Choice("离开", Decline)
elif value == 21:
_DealerReveal()
else:
_DealerTurn()
func _DealerReveal():
while _HandValue(dealerHand) < ThresholdHandValue:
dealerHand.append(deck.pop_back())
var pv : int = _HandValue(playerHand)
var dv : int = _HandValue(dealerHand)
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("我的手牌:%s" % _HandStr(dealerHand))
if dv > 21:
Mes("哈!老手也有伸手太长的时候。你赢得漂亮。")
elif pv > dv:
Mes("嗯,不错。你知道什么时候该停。")
elif dv > pv:
Mes("经验又赢了一次。别难过,多数人第一次都输给我。")
else:
Mes("%d 点平局!胆子不错,我承认这一点。" % pv)
Choice("再来一局", StartDealer)
Choice("离开", Decline)
# Player vs Player Mode
func StartPvP():
var result : int = npc.ownScript.call("JoinPvP", own)
if result == 0:
Mes("好,让我们看看还有谁有胆量坐上这张桌子。稍等。")
Choice("查看", _PvPCheck)
Choice("取消", _PvPCancel)
else:
_PvPBegin()
func _PvPCheck():
var game : Dictionary = npc.ownScript.call("GetGame", own)
if game.is_empty():
Mes("耐心点。好牌手要先学会等待。")
Choice("查看", _PvPCheck)
Choice("取消", _PvPCancel)
elif game.has("result"):
_PvPShowResult(game)
else:
_PvPBegin()
func _PvPCancel():
npc.ownScript.call("LeavePvP", own)
Chat("没有牌局?可惜。也许风向一变,你又想玩了。")
func _PvPBegin():
playerHand = npc.ownScript.call("GetHand", own)
Mes("哈!对手来了。让我看看你们谁更懂停手。")
_PvPTurn()
func _PvPTurn():
Mes("你的手牌:%s" % _HandStr(playerHand))
if _HandValue(playerHand) == 21:
Mes("21 点!")
_PvPStand()
else:
Choice("要牌", _PvPHit)
Choice("停牌", _PvPStand)
func _PvPHit():
var card : int = npc.ownScript.call("DrawCard", own)
if card < 0:
_PvPStand()
return
playerHand.append(card)
var value : int = _HandValue(playerHand)
if value > 21:
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("爆牌!刚才心里那点犹豫,其实已经给过你提醒了。")
npc.ownScript.call("FinishHand", own, value, true)
_PvPWait()
elif value == 21:
Mes("你的手牌:%s" % _HandStr(playerHand))
_PvPStand()
else:
_PvPTurn()
func _PvPStand():
npc.ownScript.call("FinishHand", own, _HandValue(playerHand), false)
_PvPWait()
func _PvPWait():
var game : Dictionary = npc.ownScript.call("GetGame", own)
if game.has("result"):
_PvPShowResult(game)
else:
Mes("你这边结束了。现在等对手做决定。")
Choice("查看", _PvPWaitCheck)
Choice("认输", _PvPForfeit)
func _PvPWaitCheck():
var game : Dictionary = npc.ownScript.call("GetGame", own)
if game.has("result"):
_PvPShowResult(game)
else:
Mes("还在思考。谨慎的人值得尊重,除非他只是忘了轮到自己。")
Choice("查看", _PvPWaitCheck)
Choice("认输", _PvPForfeit)
func _PvPShowResult(game : Dictionary):
var isP1 : bool = game.get("p1") == own
var myVal : int = game.get("p1Value") if isP1 else game.get("p2Value")
var oppBust : bool = game.get("p2Busted") if isP1 else game.get("p1Busted")
var oppHand : Array = game.get("p2Hand") if isP1 else game.get("p1Hand")
var myBust : bool = game.get("p1Busted") if isP1 else game.get("p2Busted")
Mes("你的点数:%d%s" % [myVal, "(爆牌)" if myBust else ""])
Mes("对手手牌:%s%s" % [_HandStr(oppHand), "(爆牌)" if oppBust else ""])
var result : String = game.get("result", "")
if result == "draw":
Mes("平局!势均力敌,这在我的桌上不常见。")
elif (result == "p1" and isP1) or (result == "p2" and not isP1):
Mes("胜利归你!今天牌也站在你这边。")
else:
Mes("可惜。牌有自己的脾气,但它总会再绕回来。")
npc.ownScript.call("CleanupGame", own)
Choice("再来一局", StartPvP)
Choice("离开", Decline)
func _PvPForfeit():
npc.ownScript.call("ForfeitPvP", own)
Mes("中途离桌?放在我年轻时,这至少要请全桌喝一轮。")
Choice("再来一局", StartPvP)
Choice("离开", Decline)
+160
View File
@@ -0,0 +1,160 @@
extends NpcScript
const PEYOTE_REQUIRED : int = 5
const MAGGOT_REQUIRED : int = 5
const FIELD_POSITION : Vector2 = Vector2(2912, 1312)
#
func OnStart():
var questState : int = GetQuest(ProgressCommons.Quest.TUTORIAL)
if questState < ProgressCommons.TUTORIAL.ELANORE_DONE:
OnInitialRecovery()
elif questState == ProgressCommons.TUTORIAL.ELANORE_DONE:
OnFirstMeeting()
elif questState == ProgressCommons.TUTORIAL.KAEL_MET:
OnCheckProgress()
elif questState == ProgressCommons.TUTORIAL.KAEL_DONE:
OnSendToEkinu()
elif questState == ProgressCommons.TUTORIAL.EKINU_DONE:
OnComplete()
# Opening recovery and starter choice
func OnInitialRecovery():
var waterBottleID : int = DB.GetCellHash("Water Bottle")
var cactusSourCandyID : int = DB.GetCellHash("Cactus Sour Candy")
Mes("站稳。先让我看一下你的脸色。")
Mes("巡逻队在城外沙地边缘发现你时,你已经脱水到说不出话。埃拉诺把你从最危险的状态里拉了回来,现在轮到我确认你还能不能上路。")
Mes("抬手,握拳,深呼吸。很好。你还虚,但不是那种一阵风就能吹倒的虚。")
Mes("先拿着水和仙人掌酸糖。图利姆沙欢迎活人,不欢迎逞强的人。")
AddItem(waterBottleID)
AddItem(cactusSourCandyID)
OnOpeningBackstory()
func OnOpeningBackstory():
Mes("你是被沙漠送到城门口的陌生人。身上没有能说明身份的东西,脚印却从风暴方向断断续续延过来。")
Mes("最近 托诺里 不安稳。卡奥雷 的波动让野外宠物变得暴躁,东边还有一群把 卡奥雷 当信仰的疯子在袭击旅人。")
Mes("所以我不会让你空手出城,也不会让你在城里闲逛到下一场麻烦砸过来。")
OnStarterPetIntro()
func OnStarterPetIntro():
Mes("先选一只初始宠物。不是装饰,也不是玩伴。它会替你挡住第一口毒牙,陪你学会怎么在这里活下去。")
Mes("三只都经过巡逻队训练,性格和战斗方式不一样。")
Mes("露莉娅属水,耐久和恢复节奏稳定,适合稳扎稳打。")
Mes("啾啾鸟属风,动作快,适合先手和灵活周旋。")
Mes("绒绒兽是普通系,脾气稳,招式直接,适合不想把战斗想得太复杂的人。")
Mes("想清楚就选。没有绝对正确的答案,只有你愿意一起走下去的伙伴。")
# Initial encounter
func OnFirstMeeting():
Mes("选好伙伴了?好。能照顾宠物的人,至少比只会照顾自己的人可靠一点。")
Mes("现在说正事。我这边正缺人手。")
Mes("沙虫最近把仙人掌田啃得一塌糊涂。它们个头大,嘴却灵活,能绕开刺直接钻进茎里。")
Mes("一旦钻进去,整株仙人掌就会从里面被吃空。农户看到那种景象,脸色比沙暴天还难看。")
Choice("这些沙虫为什么这么大?", OnKaoreExplanation)
Choice("听起来很麻烦。", OnWildFauna)
Choice("我可以帮忙清理。", OnWildFauna)
func OnKaoreExplanation():
Mes("据说它们很久以前没这么夸张。可那是 卡奥雷时代 之前的事,已经没人亲眼记得。")
Mes("卡奥雷 让很多生物变得异常:有的变大,有的暴躁,有的像死了又没完全死。")
Mes("沙虫算是我们每天都能看见的麻烦版本。更糟的东西通常藏在城外。")
if GetQuest(ProgressCommons.Quest.TUTORIAL) >= ProgressCommons.TUTORIAL.KAEL_DONE:
MainChoices()
else:
Mes("闲话到此为止。我要你进田里把麻烦清掉。")
Choice("好,我开始。", OnFieldCleanUp)
Choice("城外到底发生了什么?", OnDesertExplanation)
func OnWildFauna():
Mes("不管原因是什么,它们都得被清理。")
Mes("城外的 卡奥雷 波动已经影响到城内田地,沙虫不是唯一的问题。")
Mes("有些仙人掌怪也被侵蚀了,会跳来跳去,往旁边作物上甩水,像在故意嘲笑农户。")
Choice("我去清理它们。", OnFieldCleanUp)
Choice("城外到底发生了什么?", OnDesertExplanation)
func OnDesertExplanation():
Mes("卡奥雷 正在影响各种生物。")
Mes("它会把活物扭成更糟的样子:更凶、更饿、更难控制。")
Mes("被侵蚀得太深时,它们甚至会像死物一样行动。那不是普通疾病,是一种很难洗掉的诅咒。")
if GetQuest(ProgressCommons.Quest.TUTORIAL) >= ProgressCommons.TUTORIAL.KAEL_DONE:
MainChoices()
else:
Mes("西边塔里的 玛纳伊尔 法师已经发出警告:托诺里 多处 卡奥雷 正在异常聚集。")
Choice("明白了,先处理田地。", OnFieldCleanUp)
Choice("玛纳伊尔 是谁?", OnManayir)
func OnManayir():
Mes("玛纳伊尔 是研究 玛纳 的古老组织,就在城西那座塔里。")
Mes("他们会监测 玛纳 和 卡奥雷 的流向,像预报天气一样发布警告。")
Mes("二十七年前,也是他们宣布 卡奥雷时代 结束。可结束不代表消失,只是没以前那么压得人喘不过气。")
Choice("先处理田地。", OnFieldCleanUp)
Choice("卡奥雷时代 是什么?", OnAgeOfKaore)
func OnAgeOfKaore():
Mes("我没亲眼经历过,但父辈都记得。那几百年里,卡奥雷 比 玛纳 更常见,变异生物和不死怪物到处都是。")
Mes("我们现在抱怨沙虫和仙人掌怪,其实已经比那时候幸运多了。")
Choice("先处理眼前的麻烦。", OnFieldCleanUp)
# Task assignment
func OnFieldCleanUp():
OnFightTutorial()
Mes("先从北边田里的仙人掌怪开始。")
LookAtPosition(FIELD_POSITION)
Mes("它们是被 卡奥雷 侵蚀的仙人掌,对人不算致命,但已经没法种植。")
Mes("它们会乱跳、甩水、惊扰宠物,还会把旁边作物弄坏。")
ResetCamera()
Mes("清理 5 只仙人掌怪和 5 只沙虫,今天的田地就能暂时安全。")
HighlightUI(UICommons.UITarget.PROGRESS)
Narrate("任务进度会记录你正在进行的委托和战斗目标。")
HighlightUI(UICommons.UITarget.NONE)
Narrate("图鉴和任务界面会帮助你确认已击败或遇见的宠物与怪物。")
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.KAEL_MET)
# Progress check
func OnCheckProgress():
var peyoteKills : int = GetBestiary("Peyote".hash())
var maggotKills : int = GetBestiary("Maggot".hash())
if peyoteKills >= PEYOTE_REQUIRED and maggotKills >= MAGGOT_REQUIRED:
OnTaskComplete()
elif peyoteKills < PEYOTE_REQUIRED:
Mes("还没完成。田里还需要再清理 %d 只仙人掌怪。" % (PEYOTE_REQUIRED - peyoteKills))
else:
Mes("仙人掌怪处理得不错,但还差 %d 只沙虫。" % (MAGGOT_REQUIRED - maggotKills))
# Task completion
func OnTaskComplete():
Mes("干得漂亮。")
Mes("你处理得比我预想的快,也比很多正式巡逻员稳。")
Mes("去向看守艾基努汇报吧。告诉他,我认可你今天的表现。")
Mes("我们很快要组织一支小队去沙漠侦察,你可能正适合帮忙。")
SetQuest(ProgressCommons.Quest.TUTORIAL, ProgressCommons.TUTORIAL.KAEL_DONE)
AddExp(50)
#
func OnSendToEkinu():
Mes("准备好后去找看守艾基努。")
Mes("他会告诉你下一步怎么做。")
MainChoices()
#
func OnComplete():
Mes("很高兴又见到你。你看,沙虫还是不肯给我放假。")
MainChoices()
#
func MainChoices():
Choice("这些沙虫为什么这么异常?", OnKaoreExplanation)
Choice("我该怎么战斗?", OnFightTutorial)
Choice("城外到底发生了什么?", OnDesertExplanation)
Choice("我先走了。", Farewell)
func Farewell():
Chat("出城后别只看前面,背后也可能有东西靠近。")
func OnFightTutorial():
Mes("这不是让你亲手去砍仙人掌怪。让你的宠物上。")
Mes("靠近野外宠物会进入宠物对战。进入战斗后,用战斗指令释放技能,用宠物指令切换队伍成员。")
Mes("把对手削弱后,可以用捕捉符尝试收服。别一上来就丢,满体力的野外宠物通常不会乖乖进符。")
DisplayActions(["gp_interact", "gp_target"])
Narrate("接触野外宠物会进入对战。战斗菜单中可以使用技能、捕捉符、切换宠物或逃跑。")
@@ -0,0 +1,21 @@
extends NpcScript
#
const QUEST_ID : int = ProgressCommons.Quest.TULIMSHAR_OLD_FRIENDSHIP
var sealedLettersID : int = DB.GetCellHash("Sealed Letters")
var heavyEnvelopeID : int = DB.GetCellHash("Heavy Envelope")
#
func OnStart():
var questState : int = GetQuest(QUEST_ID)
if questState != ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.STARTED:
return
if HasItemsSpace([[sealedLettersID, 1], [heavyEnvelopeID, 1]]):
Mes("你在积灰的书本之间找到了两个旧信封。")
Mes("一个封得很仔细,另一个意外地沉。")
SetQuest(QUEST_ID, ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.ENVELOPES_FOUND)
AddItem(sealedLettersID)
AddItem(heavyEnvelopeID)
else:
Mes("你看见了信封,但背包太满,已经装不下它们。")
@@ -0,0 +1,95 @@
extends NpcScript
class_name TulimsharWestWallLightTrigerGlobal
#
const QUEST_ID : int = ProgressCommons.Quest.TULIMSHAR_OLD_FRIENDSHIP
const GUARD_SPEED_BOOST : int = 200
const APPROACH_DISTANCE : float = 48.0
#
static var activeCatches : Dictionary[int, NpcAgent] = {}
#
func OnAreaEnter(player : PlayerAgent):
CallGuard(player)
static func CallGuard(player : PlayerAgent):
if not ActorCommons.IsAlive(player):
return
var playerRID : int = player.get_rid().get_id()
if activeCatches.has(playerRID):
return
var questState : int = player.progress.GetQuest(QUEST_ID)
if questState >= ProgressCommons.TULIMSHAR_OLD_FRIENDSHIP.LETTERS_DELIVERED:
return
if player.SetState(ActorCommons.State.TRIGGER):
var inst : WorldInstance = WorldAgent.GetInstanceFromAgent(player)
if inst:
var guard : BaseAgent = SpawnGuard(inst)
if guard:
activeCatches[playerRID] = guard
NpcCommons.PushNotification(player, "被守卫发现了!")
Callback.AddCallback(player.tree_exiting, Cleanup, [player, guard, playerRID], ConnectFlags.CONNECT_ONE_SHOT)
Callback.OneShotCallback(guard.ready, OnGuardReady.bind(player, guard, playerRID))
static func SpawnGuard(inst : WorldInstance) -> BaseAgent:
var spawn : SpawnObject = SpawnObject.new()
spawn.map = inst.map
spawn.type = "Npc"
spawn.nick = "图利姆沙 Guard" if randi() % 2 == 0 else "图利姆沙 Sbire"
spawn.id = spawn.nick.hash()
spawn.spawn_position = Vector2i(2688, 1472) # tile (84, 46)
spawn.spawn_offset = Vector2i.DOWN
spawn.player_script = "tonori/tulimshar/PatrolGuardCaught.gd"
return WorldAgent.CreateAgent(spawn, inst.id)
static func OnGuardReady(player : PlayerAgent, guard : NpcAgent, playerRID : int):
if not guard or not ActorCommons.IsAlive(player):
Cleanup(player, guard, playerRID)
return
var guardRID : int = guard.get_rid().get_id()
Launcher.World.BulkPreload(guard, guardRID, player.peerID)
player.CheckVisibility(guard)
# Wait one full update cycle for the SetData to be called
StartGuardNavigation.call_deferred(player, guard, playerRID)
static func StartGuardNavigation(player : PlayerAgent, guard : NpcAgent, playerRID : int):
if not guard or not ActorCommons.IsAlive(player):
Cleanup(player, guard, playerRID)
return
NpcCommons.AddModifier(guard, CellCommons.Modifier.WalkSpeed, GUARD_SPEED_BOOST)
AI.Stop(guard)
# Check if in correct distance to start the guard interaction
if (guard.position - player.position).length() < APPROACH_DISTANCE / 2.0:
OnGuardArrived(player, guard, playerRID)
# Stop right before the player position but still within the trigger position
else:
var stopPos : Vector2 = player.position + (guard.position - player.position).normalized() * APPROACH_DISTANCE / 2.0
guard.WalkToward(stopPos)
Callback.OneShotCallback(guard.agent.navigation_finished, OnGuardArrived.bind(player, guard, playerRID))
static func OnGuardArrived(player : PlayerAgent, guard : NpcAgent, playerRID : int):
if not guard or not ActorCommons.IsAlive(player):
Cleanup(player, guard, playerRID)
return
guard.Interact(player)
static func Cleanup(player : PlayerAgent, guard : NpcAgent, playerRID : int):
activeCatches.erase(playerRID)
if player and is_instance_valid(player):
if player.ownScript:
NpcCommons.ToggleContext(player, false)
player.ClearScript()
if player.state == ActorCommons.State.TRIGGER:
player.SetState(ActorCommons.State.TRIGGER)
if guard and is_instance_valid(guard):
WorldAgent.RemoveAgent.call_deferred(guard)
+48
View File
@@ -0,0 +1,48 @@
extends NpcScript
#
func OnStart():
if own.stat and own.stat.level < 5:
Mes("新面孔?欢迎来到图利姆沙。")
Mes("我是玛茜,平时帮外来者认路,也帮本地人把话说清楚。")
Mes("这里风沙大,脾气也容易被吹硬。记住一件事:在城里别随便羞辱别人,我们靠互相照应活下去。")
else:
Mes("想找路、打听城里的事,或者只是想听几句闲话,都可以问我。")
OnMainChoice()
# Main choice loop
func OnMainChoice():
Choice("给我讲讲这座城。", OnCityOverview)
Choice("住在这里是什么感觉?", OnLiveHere)
Choice("最近城里有什么传闻?", OnRumors)
Choice("我该去哪里补给?", OnSupplies)
Choice("我先走了。", Farewell)
# Answers
func OnCityOverview():
Mes("图利姆沙夹在东西两侧的山丘之间,像一只缩在石壳里的沙漠甲虫。")
Mes("城墙挡住了南边的沙暴,也挡住了许多被 卡奥雷 逼疯的野兽。")
Mes("城门附近是巡逻和救治伤员的地方;中央区有市场、面包铺和小游戏;再往北就是港口,外来的货物大多从那里上岸。")
Mes("如果你刚醒来没多久,先熟悉城门、市场和港口这三处就够了。")
OnMainChoice()
func OnLiveHere():
Mes("这里的人嘴上总说自己习惯了沙漠,其实谁都知道,一个人是扛不过 托诺里 的。")
Mes("水井坏了会有人去修,商船晚到会有人分面包,巡逻队缺人时也总有人硬着头皮顶上。")
Mes("当然,王宫里的人不一定这么想。他们更喜欢命令和税单。")
OnMainChoice()
func OnRumors():
Mes("最近传得最多的是两件事。第一,红女王又想重开沙漠风暴矿洞。")
Mes("第二,城外的宠物和怪物越来越不安分,沙虫啃坏了仙人掌田,沙蝎也开始靠近旧矿道。")
Mes("如果你能收服几只可靠的伙伴,巡逻队会更愿意相信你不是来添乱的。")
OnMainChoice()
func OnSupplies():
Mes("找吃的去里斯基姆的面包铺,找水先看井和商贩,想买药就去找埃拉诺。")
Mes("如果要出城,别只带一只宠物。沙地里最糟糕的不是迷路,是你以为自己还能再撑一场战斗。")
OnMainChoice()
# Farewell
func Farewell():
Chat("风向变得太快时,就先回城墙边歇一会儿。")
+135
View File
@@ -0,0 +1,135 @@
extends NpcScript
#
func OnStart():
var questState : int = GetQuest(ProgressCommons.Quest.NINA_HUNGRY)
if questState == ProgressCommons.NINA_HUNGRY.INACTIVE:
OnIntro()
else:
Mes("又见面了。灵魂石碑 今天很安静,这是好事。")
OnPlayerChoice()
# Intro
func OnIntro():
Mes("你好。是埃拉诺让你来的?欢迎。")
Mes("你面前的是图利姆沙古老的 灵魂石碑。我负责守护它,也用它的力量保护城里的人。")
Mes("当然,是在王宫允许的范围内。这里很多事都要加上这句话。")
OnPlayerChoice()
func OnPlayerChoice():
if GetQuest(ProgressCommons.Quest.NINA_HUNGRY) == ProgressCommons.NINA_HUNGRY.STARTED and HasItem(DB.GetCellHash("Croissant")):
Choice("我给你带来了可颂。", OnCroissantTurnIn)
else:
Choice("有人在阻止你使用 石碑?", OnExplainOpposition)
Choice("讲讲 玛纳 和 卡奥雷。", OnExplainMana)
Choice("灵魂石碑 是什么?", OnExplainMenhir)
Choice("我先走了。", Farewell)
# Opposition and faith
func OnExplainOpposition():
Mes("不是直接阻止。卡维,也就是外人口中的德鲁伊,一直被允许留在图利姆沙。没有我们,这座城很难熬过干旱和 卡奥雷 侵袭。")
Mes("但王国官方信奉 萨维安教义。他们认为 玛纳 和 卡奥雷 一样危险,都应该尽量远离。")
Mes("有趣的是,公开反对 玛纳 的人,家人受伤或田地缺雨时,还是会来找我们。")
Mes("我只是希望大家能更诚实地面对古老传统。萨维安 的恐惧已经让世界吃过太多苦,现在还催生了崇拜 卡奥雷 的 瓦鲁尼亚人。")
Choice("卡维 怎么看待 玛纳?", OnKahwePosition)
Choice("我想问别的事。", OnPlayerChoice)
Choice("谢谢你,我先走了。", Farewell)
func OnKahwePosition():
Mes("卡维 传承的是 玛纳 与生命和谐共处的知识。我们想恢复 汉图 被毁后失去的平衡。")
Choice("汉图 是什么?", OnExplainHantu)
Choice("我想问别的事。", OnPlayerChoice)
Choice("谢谢你,我先走了。", Farewell)
func OnExplainHantu():
Mes("汉图 也叫 玛纳树,曾经像世界生命力的心脏,能自然引导 玛纳。")
Mes("古代有许多 汉图,它们稳定 玛纳,防止 玛纳 腐败成 卡奥雷。")
Mes("后来战争和 萨维安教义 的恐惧摧毁了它们。统治者试图创造一个没有 玛纳 的世界。")
Mes("结果你已经看见了:玛纳 没有消失,只是失去平衡,留下了 卡奥雷。")
Choice("现在局势怎样?", OnCurrentSituation)
Choice("我想问别的事。", OnPlayerChoice)
Choice("谢谢你,我先走了。", Farewell)
func OnCurrentSituation():
Mes("看你问谁。红女王会说普通人离魔法越远越好。")
Mes("可她自己在王宫里照样使用魔法。她真正的意思是:力量最好只掌握在少数人手里。")
Mes("我相信魔法是世界的一部分。水也危险,但没人会因为会溺水就封掉所有水井。")
Choice("红女王是什么人?", OnRedQueen)
Choice("有什么我能帮忙的吗?", OnAskForHelp)
func OnRedQueen():
Mes("她统治图利姆沙,也自称统治整个 托诺里。可城墙之外,听她命令的人并不多。")
Mes("祖尼 部族从未承认这个王国。她越控制不了外面,就越想控制城里的人。")
Mes("祖尼 仍保留古老魔法,这让他们很难被征服。红女王不想图利姆沙居民也拥有那种独立性。")
Choice("讲讲 祖尼。", OnZuni)
Choice("有什么我能帮忙的吗?", OnAskForHelp)
func OnZuni():
Mes("祖尼 在 托诺里 生活了很久,久到他们的故事里还记得这片土地不是沙漠时的样子。")
Mes("他们和图利姆沙一直有贸易,也大多友好。前提是我们别把士兵派得太远。")
Mes("一旦王国越界,他们就会反击。我尊重这一点:他们知道边界在哪里,也愿意守住家园。")
Choice("听起来我该更谨慎些。", OnZuniDismissal)
Choice("我想见见他们。", OnZuniMarket)
func OnZuniDismissal():
Mes("谨慎总比傲慢好。")
OnZuniMarket()
func OnZuniMarket():
Mes("北边市场里常有 祖尼 商人。只要你尊重他们,买不买东西都能聊上几句。")
Choice("有什么我能帮忙的吗?", OnAskForHelp)
# 玛纳 and 卡奥雷
func OnExplainMana():
Mes("玛纳 常被称作生命力,是活物之间流动的能量。")
Mes("正确引导 玛纳,可以施展魔法、治疗土地、维持生命。")
Mes("卡奥雷 则是 玛纳 与源头断裂后腐败留下的东西。它不滋养生命,只会侵蚀、扭曲,让生物变得敌对。")
Mes("这座 灵魂石碑 用 泽莱石 制成。泽莱石 能储存和释放 玛纳,所以它才能保护城门附近。")
OnPlayerChoice()
# 灵魂石碑 and 泽莱石
func OnExplainMenhir():
Mes("灵魂石碑 是用巨大 泽莱石 雕成的古代石碑。卡维 会借它引导并扩散 玛纳。")
Mes("它启动时能形成保护性的气场,驱散被 卡奥雷 侵蚀的生物,也能稳定周围土地。")
Mes("在 卡维 协助下,它还能治疗重伤者。某些时候,甚至能把濒死者的灵魂拉回安全处。")
Choice("泽莱石 是什么?", OnExplainZielite)
Choice("我想问别的事。", OnPlayerChoice)
Choice("谢谢你,我先走了。", Farewell)
func OnExplainZielite():
Mes("泽莱石 是一种天然亲近 玛纳 的稀有矿物,能吸收、储存并释放 玛纳。")
Mes("过去它并不少见,但 萨维安教义 掀起反 玛纳 清洗后,很多矿脉和工艺都被毁了。")
Mes("现在 泽莱石 多藏在护符、遗迹和少数仍然站立的 灵魂石碑 里。")
OnPlayerChoice()
# Hungry quest
func OnAskForHelp():
var questState : int = GetQuest(ProgressCommons.Quest.NINA_HUNGRY)
if questState == ProgressCommons.NINA_HUNGRY.STARTED:
Mes("暂时没有。只是,如果你真的路过面包铺,我还在惦记那份点心。")
OnPlayerChoice()
elif questState == ProgressCommons.NINA_HUNGRY.REWARDS_WITHDREW:
Mes("暂时没有,谢谢你还记得问。那份可颂让我今天好多了。")
OnPlayerChoice()
else:
Mes("暂时没有。")
Mes("不过我有点饿。不是要你帮我买吃的!我只是从早上开始就没离开过 石碑。")
Choice("我去市场时帮你看看点心。", OnStartHungryQuest)
Choice("那你记得休息。", Farewell)
func OnStartHungryQuest():
Mes("你真好。我平时不会开口,但现在如果有一份可颂,确实会像小小的奇迹。")
Mes("市场的面包铺通常能买到。我要是离开太久,石碑 这边没人照看。")
SetQuest(ProgressCommons.Quest.NINA_HUNGRY, ProgressCommons.NINA_HUNGRY.STARTED)
func OnCroissantTurnIn():
Mes("天啊。")
Mes("真的是可颂!我没想到你会记得。")
RemoveItem(DB.GetCellHash("Croissant"))
SetQuest(ProgressCommons.Quest.NINA_HUNGRY, ProgressCommons.NINA_HUNGRY.REWARDS_WITHDREW)
AddItem(DB.GetCellHash("Cactus Potion"), 10)
AddGP(100)
Mes("请收下这些仙人掌药剂,还有一点钱,至少补上你花掉的费用。")
func Farewell():
Chat("愿 灵魂石碑 保佑你一路平安。")
@@ -0,0 +1,18 @@
extends NpcScript
#
const ENTRANCE_POS : Vector2 = Vector2(1536, 1600) # tile (48, 50)
#
func OnStart():
Mes("站住。")
Mes("议员博恩斯的命令:没有许可,任何人不得进入这些走廊。")
Mes("我不管你为什么来。转身,出去。")
Action(Escort)
func Escort():
if own.state == ActorCommons.State.TRIGGER:
own.SetState(ActorCommons.State.TRIGGER)
var entranceMapID : int = "图利姆沙 Center".hash()
Action(NpcCommons.Warp.bind(own, entranceMapID, ENTRANCE_POS))
+111
View File
@@ -0,0 +1,111 @@
extends NpcScript
# Reward items
var croissantID : int = DB.GetCellHash("Croissant")
var cactusSourCandyID : int = DB.GetCellHash("Cactus Sour Candy")
#
func OnStart():
var questState : int = GetQuest(ProgressCommons.Quest.GRAIN_IN_THE_SAND)
match questState:
ProgressCommons.GRAIN_IN_THE_SAND.INACTIVE:
OnInactive()
ProgressCommons.GRAIN_IN_THE_SAND.SEARCHED_CRATES:
OnReward()
ProgressCommons.GRAIN_IN_THE_SAND.REWARDS_WITHDREW:
OnComplete()
_:
OnKeepLooking()
# Quest states
func OnInactive():
Mes("欢迎,旅人。你来得正巧,也来得不太巧:架子上的面包刚刚卖空。")
Mes("图利姆沙 没有像样的农田,城外除了沙、风和仙人掌,几乎什么都长不稳。")
Mes("我的面粉都从 阿尔蒂斯 坐船运来。靠它做出的 沙暴面包,已经养活这座城很多年了。")
Mes("今天清晨有一批货到了码头,可这天气来回跑一趟,回来就没力气揉面了。")
Mes("阿尔蒂斯 面包坊的货桶上有深蓝色蜡封。你能去码头帮我把里面的面粉袋带回来吗?")
QuestChoice()
func QuestChoice(previousChoice : int = -1):
Choice("我去码头看看。", OnAccept)
if previousChoice != 1:
Choice("沙暴面包 是什么?", OnAskBread)
if previousChoice != 2:
Choice("沙漠里真的种不出粮食吗?", OnAskDesert)
Choice("晚点再说。", OnDecline)
func OnKeepLooking():
Mes("码头那边有线索吗?记住,找带深蓝色蜡封的货桶。")
Mes("我会把炉火留着,面粉一到就能开工。")
func OnComplete():
Mes("就算热得像站在炉膛旁边,刚出炉的面包也没人舍得错过。")
CompleteChoice()
func CompleteChoice(previousChoice : int = -1):
if previousChoice != 0:
Choice("给我讲讲 阿尔蒂斯。", OnAskArtis)
if previousChoice != 1:
Choice("图利姆沙 靠什么活下来?", OnAskCity)
if previousChoice != 2:
Choice("沙暴面包 是什么?", OnAskBread)
if previousChoice != 3:
Choice("沙漠里真的种不出粮食吗?", OnAskDesert)
if previousChoice != -1:
Choice("你忙吧。", OnFarewell)
# Optional dialogue
func OnAskBread():
Mes("我的招牌配方:阿尔蒂斯 的细面粉、仙人掌汁代替清水,再加一撮晒干的沙盐。")
Mes("这种面包外壳结实,穿过沙暴也不容易碎,第二天早上还带着香气,所以才叫 沙暴面包。")
if IsQuestCompleted(ProgressCommons.Quest.GRAIN_IN_THE_SAND):
CompleteChoice(2)
else:
QuestChoice(1)
func OnAskDesert():
Mes("能长的主要是仙人掌。它们很硬气,我们也很会利用:糖果、饮料、药膏,什么都能做。")
Mes("可粮食不一样。托诺里 从来不是产麦子的地方,没有商船,城里的炉火会先灭,人的肚子也会跟着空。")
if IsQuestCompleted(ProgressCommons.Quest.GRAIN_IN_THE_SAND):
CompleteChoice(3)
else:
QuestChoice(2)
func OnAskArtis():
Mes("阿尔蒂斯 是海对岸 奥罗拉 海岸上的大港,农田肥沃,工匠也多。")
Mes("我妹妹就住在那里。她烤的小饼干特别香,我这里用的面粉也来自她的面包坊。")
Mes("图利姆沙 市场上一半的货都靠 阿尔蒂斯 商船补给。少了它们,我们能吃的就只剩仙人掌和耐心了。")
CompleteChoice(0)
func OnAskCity():
Mes("贸易。图利姆沙 最擅长的不是种地,而是让来自三片大陆的货物在这里停脚。")
Mes("港口、城墙、商队、守卫,还有像我这样盯着下一船面粉的人,拼在一起才撑起这座沙漠城市。")
Mes("这也说明了一件事:码头上每一个迟到的货桶,都会让城里某个炉子安静下来。")
CompleteChoice(1)
func OnFarewell():
Mes("有空再来。只要炉火还亮,就会有一块新面包等着你。")
# Transitions to next states
func OnAccept():
SetQuest(ProgressCommons.Quest.GRAIN_IN_THE_SAND, ProgressCommons.GRAIN_IN_THE_SAND.STARTED)
Mes("太感谢了,朋友。")
Mes("找深蓝色蜡封。看到 阿尔蒂斯 的印记,就说明货没错。")
func OnDecline():
Mes("没关系。炉火会等一会儿,只是别让它等到天黑。")
func OnReward():
Mes("你找到了!太好了。")
Mes("有了这些面粉,日落前 图利姆沙 就能闻到 沙暴面包 的香味。")
SetQuest(ProgressCommons.Quest.GRAIN_IN_THE_SAND, ProgressCommons.GRAIN_IN_THE_SAND.REWARDS_WITHDREW)
AddItem(cactusSourCandyID, 5)
AddItem(croissantID, 5)
AddKarma(1)
AddExp(20)
Mes("这些给你:可颂 和 仙人掌酸糖。一个是家传手艺,一个是我在沙暴天里琢磨出来的小发明。")
@@ -0,0 +1,41 @@
extends NpcScript
#
func OnStart():
Mes("需要指路?别在这片城墙下乱转,图利姆沙的路看起来直,走起来很会骗人。")
OnMainChoice()
# Main choice loop
func OnMainChoice():
Choice("港口在哪里?", OnPort)
Choice("东边海岸有什么?", OnEast)
Choice("沙漠风暴矿洞怎么走?", OnMines)
Choice("城里哪里比较安全?", OnSafe)
Choice("我先走了。", Farewell)
# Answers
func OnPort():
Mes("往北走。你会先闻到海味,再听见水手吵架,那就是港口。")
Mes("托诺里 自己产不了多少谷物,外地来的布料、面粉、药材也多靠那里。")
Mes("水手工作时别挡路。他们骂人比沙暴还快,而且通常骂得很准。")
OnMainChoice()
func OnEast():
Mes("东边是一长段海滩。喜欢沙子的话,那里多到能让你重新讨厌它。")
Mes("沿路继续走能靠近 玛纳伊尔 的塔。那些研究 玛纳 的人很少下城,但他们看见的东西比我们多。")
Mes("再往东北有座灯塔,天晴时从那里能望见整片海。迷路时认灯塔,比认自己的影子可靠。")
OnMainChoice()
func OnMines():
Mes("要去矿洞,就先出城往南,穿过沙漠风暴谷地。")
Mes("旧井旁边有入口,内森常被派在那里站岗。如果你看见一个快被晒干的守卫,多半就是他。")
Mes("别低估那条路。风会遮住怪物,怪物也会利用风。")
OnMainChoice()
func OnSafe():
Mes("城门附近有埃拉诺,灵魂石碑 那边有妮娜,市场人多,通常也安全。")
Mes("真正别乱闯的是城堡走廊。巡逻队对陌生人没耐心,尤其是灯光照得到的地方。")
OnMainChoice()
func Farewell():
Chat("看路,也看风。风有时候比人诚实。")
@@ -0,0 +1,36 @@
extends NpcScript
class_name SoulMenhirGlobal
#
const REGEN_HEALTH_BONUS : int = 3
const REGEN_MANA_BONUS : int = 3
const REGEN_STAMINA_BONUS : int = 3
var playerModifiers : Dictionary[int, Array] = {}
#
func OnAreaEnter(player : PlayerAgent):
if not player or not ActorCommons.IsAlive(player):
return
var playerRID : int = player.get_rid().get_id()
if playerModifiers.has(playerRID):
return
var mods : Array[StatModifier] = []
mods.append(AddModifier(CellCommons.Modifier.RegenHealth, REGEN_HEALTH_BONUS, player))
mods.append(AddModifier(CellCommons.Modifier.RegenMana, REGEN_MANA_BONUS, player))
mods.append(AddModifier(CellCommons.Modifier.RegenStamina, REGEN_STAMINA_BONUS, player))
playerModifiers[playerRID] = mods
func OnAreaExit(player : PlayerAgent):
if not player:
return
var playerRID : int = player.get_rid().get_id()
if not playerModifiers.has(playerRID):
return
for mod in playerModifiers[playerRID]:
RemoveModifier(mod, player)
playerModifiers.erase(playerRID)
@@ -0,0 +1,9 @@
extends WarpGlobal
#
func OnAreaEnter(player : PlayerAgent):
if player and player.progress:
if player.progress.GetQuest(ProgressCommons.Quest.TUTORIAL) < ProgressCommons.CompletedProgress:
Network.PushNotification("出城前先去找埃拉诺。", player.peerID)
return
super.OnAreaEnter(player)
+88
View File
@@ -0,0 +1,88 @@
extends NpcScript
#
func OnStart():
var globalScript : TicTacToeGlobal = (npc.ownScript as TicTacToeGlobal)
match globalScript.startStep:
TicTacToeGlobal.State.NONE:
if steps.is_empty():
match randi_range(0, 2):
0: Mes("要玩井字棋吗?棋盘是我自己画的!")
1:
Mes("我画不出圆圆的圈,所以我用小方块代替。安迪总笑我,但方块也可以很厉害。")
Mes("要玩井字棋吗?")
2:
Mes("前几天我看到一伙海盗和守卫玩井字棋。守卫输了,但他们说那叫战略撤退。")
Mes("你也要玩吗?")
else:
Mes("要玩井字棋吗?")
Choice("和你玩", StartPvE)
Choice("找别人玩", StartPvP)
Choice("怎么玩?", ShowRules)
Choice("晚点再玩", Decline)
TicTacToeGlobal.State.X:
if globalScript.playerX != own:
Mes("嘿,已经有人在等对手了!你可以去挑战他们!")
Choice("我来和他们玩!", StartPvP)
Choice("怎么玩?", ShowRules)
Choice("晚点再玩", Decline)
else:
Mes("还没人来。你要继续等吗?")
Choice("再等一会儿", WaitPvP)
Choice("怎么玩?", ShowRules)
Choice("我得走了", CancelPvP)
TicTacToeGlobal.State.O:
var isPlaying : bool = globalScript.playerX == own or globalScript.playerO == own
if isPlaying:
Mes("嘿,不许偷看提示!快回去下你的棋!")
else:
Mes("嘘,他们正在下棋!你可以看,但不能乱指。")
Choice("好啦好啦", OnQuit)
Choice("怎么玩?", ShowRules)
if isPlaying:
Choice("我得走了", CancelPvP)
func ShowRules():
Mes("你选一个格子,放上自己的记号,然后换另一个人。")
Mes("横着、竖着、斜着,只要三个连成一条线就赢。")
Mes("如果九个格子都填满还没人连成线,就是平局。平局也不错,只是没有胜利时那么好跳起来。")
OnStart()
func Decline():
Chat("好吧,改变主意就来找我,棋盘不会跑掉。")
# Player vs 角色
func StartPvE():
var globalScript : TicTacToeGlobal = (npc.ownScript as TicTacToeGlobal)
if globalScript.startStep == TicTacToeGlobal.State.NONE and globalScript.StartPvE(own):
DisplayActions(["gp_target", "gp_untarget"])
Mes("你执叉号!选一个格子,快快快!")
else:
Mes("等一下,已经有人在玩了!要排队。")
# Player vs Player
func StartPvP():
var globalScript : TicTacToeGlobal = (npc.ownScript as TicTacToeGlobal)
match globalScript.StartPvP(own):
TicTacToeGlobal.State.X:
Mes("现在等另一个人来挑战你。")
return
TicTacToeGlobal.State.O:
DisplayActions(["gp_target", "gp_untarget"])
Mes("太好了,你们都到齐了!好好玩!")
return
TicTacToeGlobal.State.NONE:
Mes("不行不行,棋盘现在被占用了!")
func WaitPvP():
if (npc.ownScript as TicTacToeGlobal).playerX == own:
Chat("一定很快就有人来,大概吧!")
func CancelPvP():
var globalScript : TicTacToeGlobal = (npc.ownScript as TicTacToeGlobal)
if globalScript.playerX == own:
globalScript.LeaveQueue(own)
Chat("啊,这么快就不等了?好吧。")
func OnQuit():
Chat("嘘,别打扰他们下棋。")