|
本帖最后由 P.A.I.M.O.N 于 2023-6-23 22:20 编辑
大家好我是派蒙,今天又来给各位星辰旅行者讲课了,上次有人说我只说加能接任务的NPC和Rules,没说怎么写任务,那不是只给开瓶器不给啤酒么?
嘿嘿,小派蒙是个诚实的孩子,说加NPC就加NPC,一点不多,一点也不少,Intel放在这期讲。
同时,因为议长向我提出建议,说教程最好一步一步写,否则大量信息直接贴出来会让读者无所适从,尤其是那个Rules教程可能会卡住大部分读者,所以写intel的计划刻不容缓了!
那么废话说够了,开始!
1:建立一个新的*.Java文件
打开Starsector\starsector-core\starfarer.api\com\fs\starfarer\api\impl\campaign\missions,这是原版任务文件的储存地点,我们以原版为蓝本,复制粘贴一个文件到我们的MOD之中。
这里我选择以SpySatDeployment为原型,重新制作一次我X入侵中watchmen_Scout玩家去异械老家回收侦查信标的任务。
将文件复制到XInvade(你的MOD文件夹)\com\fs\starfarer\api\impl\campaign\missions中,改名并把文件中无关内容删除,变成以下模样
- package com.fs.starfarer.api.impl.campaign.missions;
- import java.awt.Color;
- import com.fs.starfarer.api.campaign.SectorEntityToken;
- import com.fs.starfarer.api.campaign.econ.MarketAPI;
- import com.fs.starfarer.api.characters.PersonAPI;
- import com.fs.starfarer.api.impl.campaign.ids.Factions;
- import com.fs.starfarer.api.impl.campaign.ids.Ranks;
- import com.fs.starfarer.api.impl.campaign.ids.Tags;
- import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithBarEvent;
- import com.fs.starfarer.api.ui.TooltipMakerAPI;
- import com.fs.starfarer.api.util.Misc;
- //上方的import是导入类,只有导入了某个类才能使用它的方法,一般来说如果你技术不精湛,不要随意删除上面的内容
- public class watchmen_Scout extends HubMissionWithBarEvent {
- //类名与继承,注意类名应与文件名一致,以你MOD的特殊标识符为开头。因为这里是守望者联盟而非异械的任务,所以我用了watchmen
- public enum Stage {//任务的阶段
- }
- @Override
- protected boolean create(MarketAPI createdAt, boolean barEvent) {//当任务被创建时自动调用
- return true;
- }
- protected void updateInteractionDataImpl() {//当任务被手动刷新时(例如在rules里使用Call XXXXX updateData)时调用
- }
- @Override
- public void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height) {//在intel界面里,提示玩家任务的下一步该做什么
- }
- @Override
- public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) {//任务刷新至下阶段时,弹出的“子弹信息”内容
- }
- @Override
- public String getBaseName() {//在各处显示给玩家看的任务名称,可自定义
- return "侦察计划";
- }
- }
复制代码
2:设计任务
在编写Intel前,你要在脑海中大致规划一下该任务讲述的是什么故事,流程如何。此处与代码设计没有什么关系,更多是考验作者的策划能力。
在XInvade中,watchmen_Scout侦察计划任务是守望者联盟的导师Marx,因为最近异械没有动静怀疑这些邪恶机械是不是在谋划什么,于是发射了探测信标。可能由于距离太远,信标发生了故障,不再向他发送信号,需要探险者手动去把信标回收,遂派玩家不远千里前往Xenon Sector 101星区。当玩家到达信标附近时才发现,信标早就被异械捕获,敌人舰队在某处守株待兔等待傻白甜玩家自投罗网,怎想玩家战斗力爆表,最终成功回收信标,结局皆大欢喜。
3:设置任务阶段与初始化
根据上个阶段的规划,任务总计分为4个阶段,可以用枚举来表示它们。关于枚举的本质,感兴趣的读者可以自行寻找Java基础教程深入了解。
- public enum Stage {
- GO_TO_PROBE,//从任务开始,到玩家回收信标
- RETURN_TO_CHALDEA,//从玩家回收信标,到玩家回到Marx身边交付任务
- COMPLETED,//任务完成
- FAILED,//任务失败
- }
复制代码 原则上,一个任务至少需要有三个阶段:开始阶段,成功阶段,失败阶段。这三个阶段可以在create函数中用以下方法设置:
- @Override
- protected boolean create(MarketAPI createdAt, boolean barEvent) {//当任务被创建时自动调用
- setStartingStage(Stage.GO_TO_PROBE);
- addSuccessStages(Stage.COMPLETED);
- addFailureStages(Stage.FAILED);
- return true;
- }
复制代码 在正式编写create函数之前,我们要首先做一些准备工作,除了设置静态与全局变量外,还要设置任务ref:
- public class watchmen_Scout extends HubMissionWithSearch {
- public static float MISSION_DAYS = 60f;
- public enum Stage {
- GO_TO_PROBE,
- RETURN_TO_CHALDEA,
- COMPLETED,
- FAILED,
- }
- protected StarSystemAPI system;
- protected SectorEntityToken object;
- @Override
- protected boolean create(MarketAPI createdAt, boolean barEvent) {
- if (!setGlobalReference("$watchmenScout_ref")) {
- //上面这句话含义是,初始化一个Global索引,在rules中可以根据该索引找到此实例化的Intel,该索引名字为"$watchmenScout_ref"。
- //如果创建失败,则返回false,也就是说如果创建失败,那么if里面的值应该为true,如果为true,则执行下面的return false,创建失败,直接结束,避免出BUG
- return false;
- }
- object = Global.getSector().getEntityById("xenon_sector101debris");
- //从Global中根据ID"xenon_sector101debris"找到一个物体,其实是我提前在xenonsector101星系文件中设置好的残骸场
- if (object == null) {//如果找不到该物体,则说明出了BUG,直接返回,避免游戏崩溃
- return false;
- }
- system = object.getStarSystem();//通过残骸场,找到残骸场所处的星系,也就是“xenonsector101”
- setStartingStage(Stage.GO_TO_PROBE);
- addSuccessStages(Stage.COMPLETED);
- addFailureStages(Stage.FAILED);
- return true;
- }
复制代码 4:设置任务细节
将以下代码段加到return true的前面,addFailureStages后面
这些是对任务一些琐碎细节的设置
- SectorEntityToken probe = spawnEntity(Entities.GENERIC_PROBE, new LocData(object));
- //这句的含义是创建一个物体,这个物体的种类是Entities.GENERIC_PROBE(探测器)
- //类Entities里有各种类型的物体,例如Entities.COMM_RELAY是通讯阵列
- //物体的位置是new LocData(object),之所以要创建一个LocData是因为这个函数的参数如此,道理和设置颜色时用new Color(255,255,255,255)而不直接用(255,255,255,255)这种RGB参数一样。
- //这个位置内容就是object的位置,也就是说探测器会出现在残骸场的正中央。
- if (probe == null) return false;//如果创建失败,立刻结束以免出现更大的BUG导致游戏崩溃
- probe.setId("watchmenScout_probe");//设置该物体的ID为括号中内容,该步骤不是强制的,可以省略。
- makeImportant(probe, "$watchmenScout_probeFlag", Stage.GO_TO_PROBE);
- //设置该物体为重要物体,直观来看就是在右上角添加一个黄色感叹号,三个参数分别为需要添加感叹号的物体,以什么索引添加(索引可能在其他地方用到,此处为null亦可),在什么阶段添加。
- //如果阶段为Stage.GO_TO_PROBE,那么在阶段Stage.RETURN_TO_CHALDEA则感叹号不会出现。
- makeImportant(getPerson(), "$watchmenScout_returnHere", Stage.RETURN_TO_CHALDEA);
- //设置人物为重要人物。默认状态下,getPerson()方法返回的人物是派发给玩家任务的人物,这里即Marx
- setStageOnGlobalFlag(Stage.RETURN_TO_CHALDEA, "$watchmenScout_canReturn");
- //设置一个GlobalFlag变量,当该变量为true时更新任务会直接跳转到某阶段,例如在rules中使用"Call xxxxx updateStage"时。
- connectWithGlobalFlag(Stage.RETURN_TO_CHALDEA, Stage.COMPLETED, "$watchmenScout_finished");
- //设置一个GlobalFlag变量,当该变量为true且当前阶段处于A阶段时更新任务会跳转到B阶段。也就是说,如果阶段不为Stage.RETURN_TO_CHALDEA,即使$watchmenScout_finished值为true时使用Call,也不会刷新阶段。
- setStageOnGlobalFlag(Stage.FAILED, "$watchmenScout_failed");
- //同上,一般来说,setStageOnGlobalFlag可以满足绝大部分需求。
- setTimeLimit(Stage.FAILED, MISSION_DAYS, system, Stage.RETURN_TO_CHALDEA);
- //设置时间限制,当过了MISSION_DAYS(120)天后,会自动跳转到FAILED阶段,即任务失败。
- //当玩家处于system星系中时,不会因时间问题被宣告任务失败。当任务阶段为RETURN_TO_CHALDEA时,即使超过了120天,也不会被强制任务失败。
- setCreditReward(CreditReward.AVERAGE);//设置任务完成后获得的星币量大小
- setRepRewardPerson(RepRewards.VERY_HIGH);//设置任务完成后,派发任务的NPC关系增加量
- setRepRewardFaction(RepRewards.EXTREME);//设置任务完成后,NPC所属阵营的关系增加量
复制代码
5:设置Trigger
现在,我们要进行重头戏——任务中,埋伏玩家的舰队的设置
我们可以用Alex准备好的接口——Trigger来进行设置,这些Trigger详情可以在类HubMissionWithTrigger中看到。
除此之外,我们也可以使用更加基础的FleetParamV3之类接口来设置舰队,但是此处我推荐使用更加方便的Trigger
- beginInRangeOfEntityTrigger(object, 100f,Stage.GO_TO_PROBE);
- //设置触发条件,这里是当玩家舰队距离object(残骸场)距离小于100f,且阶段处于GO_TO_PROBE时触发
- triggerCreateFleet(FleetSize.MEDIUM, FleetQuality.VERY_HIGH, "xenon", FleetTypes.PATROL_MEDIUM, object);
- //创建舰队,参数分别从左到右为舰队规模,舰队质量,阵营,舰队类型,大致位置
- triggerFleetNoJump();
- //令trigger创造出的舰队无法超空间跳跃
- triggerSetFleetMissionRef("$watchmenScout_ref");
- //设置舰队所属任务,一般用不上
- triggerFleetSetPatrolActionText("等待");
- //设置舰队在巡逻时,玩家鼠标移到舰队上显示出的介绍文字
- triggerOrderFleetPatrol(probe);
- //令舰队在probe(探测器)附近巡逻
- triggerOrderFleetInterceptPlayer();
- //令舰队在探测器扫到玩家时追击玩家
- triggerPickLocationAroundEntity(probe, 500f);
- //挑选一个位置,为后续生成做准备,这里是挑选距离probe探测器半径500f的圆弧上的位置
- triggerSpawnFleetAtPickedLocation("$watchmenScout_EnemyFlag", "watchmenScout_ref");
- //生成舰队在挑选的位置,并赋予flag和任务所属。注意,如果只有上文的CreateFleet舰队只会被创建在内存中,如果不生成玩家是无法在生涯模式宇宙中与其遭遇的。
- triggerFleetMakeImportant("$watchmenScout_Enemy",Stage.RETURN_TO_CHALDEA);
- //设置该舰队为重要舰队
- endTrigger();
- //结束Trigger
复制代码
如果之前按部就班,那么现在您的create函数内应该是如下景象:
- @Override
- protected boolean create(MarketAPI createdAt, boolean barEvent) {
- if (!setGlobalReference("$watchmenScout_ref")) {
- return false;
- }
- object = Global.getSector().getEntityById("xenon_sector101debris");
- if (object == null) {
- return false;
- }
- system = object.getStarSystem();
-
- setStartingStage(Stage.GO_TO_PROBE);
- addSuccessStages(Stage.COMPLETED);
- addFailureStages(Stage.FAILED);
- SectorEntityToken probe = spawnEntity(Entities.GENERIC_PROBE, new LocData(object));
- if (probe == null) return false;
-
- probe.setId("watchmenScout_probe");
- makeImportant(probe, "$watchmenScout_probe", Stage.GO_TO_PROBE);
- makeImportant(getPerson(), "$watchmenScout_returnHere", Stage.RETURN_TO_CHALDEA);
- setStageOnGlobalFlag(Stage.RETURN_TO_CHALDEA, "$watchmenScout_canReturn");
- connectWithGlobalFlag(Stage.RETURN_TO_CHALDEA, Stage.COMPLETED, "$watchmenScout_finished");
- setStageOnGlobalFlag(Stage.FAILED, "$watchmenScout_failed");
- setTimeLimit(Stage.FAILED, MISSION_DAYS, system, Stage.RETURN_TO_CHALDEA);
- setCreditReward(CreditReward.AVERAGE);
- setRepRewardPerson(RepRewards.VERY_HIGH);
- setRepRewardFaction(RepRewards.EXTREME);
- beginInRangeOfEntityTrigger(object, 100f, Stage.GO_TO_PROBE);
- triggerCreateFleet(FleetSize.MEDIUM, FleetQuality.VERY_HIGH, "xenon", FleetTypes.PATROL_MEDIUM, object);
- triggerFleetNoJump();
- triggerSetFleetMissionRef("$watchmenScout_ref");
- triggerFleetSetPatrolActionText("等待");
- triggerOrderFleetPatrol(probe);
- triggerOrderFleetInterceptPlayer();
- triggerPickLocationAroundEntity(probe, 500f);
- triggerSpawnFleetAtPickedLocation("$watchmenScout_EnemyFlag", "watchmenScout_ref");
- triggerFleetMakeImportant("$watchmenScout_Enemy", Stage.RETURN_TO_CHALDEA);
- endTrigger();
- return true;
- }
复制代码
5:设置Update行为及面向玩家的描述
函数“updateInteractionDataImpl()”将在玩家使用Call XXXXX updateData时被调用一次
- protected void updateInteractionDataImpl() {
- if (getCurrentStage() != null) {
- set("$watchmenScout_stage", ((Enum)getCurrentStage()).name());//这个是防止出BUG用的
- }
- set("$watchmenScout_starName", system.getNameWithNoType());//星系名字
- set("$watchmenScout_systemName", system.getNameWithLowercaseTypeShort());//星系名字的小写
- set("$watchmenScout_dist", getDistanceLY(object));//星系距离玩家当前位置的距离
- set("$watchmenScout_reward", Misc.getWithDGS(getCreditsReward()));//获得报酬的量(星币)
- }
复制代码 这些一般用于rules中获取任务信息(报酬量,距离,关键物体所在星系之类)
最后,我们在addDescriptionForNonEndStage和addNextStepText中设置任务在intel和左下角弹出的简报中的描述文字
- @Override
- public void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height) {
- float opad = 10f;
- Color h = Misc.getHighlightColor();
- if (currentStage == Stage.GO_TO_PROBE) {
- info.addPara("恢复在 " +
- system.getNameWithLowercaseTypeShort() + " 星系中探测器里的数据包.", opad);
- } else if (currentStage == Stage.RETURN_TO_CHALDEA) {
- info.addPara("将数据包送回Chaldea并与" +
- getPerson().getNameString() + " 谈话获取你的报酬.", opad);
- }
- }
- @Override
- public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) {
- Color h = Misc.getHighlightColor();
- if (currentStage == Stage.GO_TO_PROBE) {
- if (system.isCurrentLocation()) {
- info.addPara("恢复探测器里的数据包", tc, pad);
- } else {
- info.addPara(getGoToSystemTextShort(system), tc, pad);
- }
- return true;
- } else if (currentStage == Stage.RETURN_TO_CHALDEA) {
- info.addPara("回到Chaldea并与" + getPerson().getNameString() + " 对话", tc, pad);
- return true;
- }
- return false;
- }
复制代码 这两个函数之中的参数不必考虑太多,只需修改addPara中的内容即可。
最后的工作完成,如果中间步骤没有出错的话,类中内容应该如下所示:
- package com.fs.starfarer.api.impl.campaign.missions;
- import java.awt.Color;
- import java.util.List;
- import java.util.Map;
- import com.fs.starfarer.api.Global;
- import com.fs.starfarer.api.campaign.InteractionDialogAPI;
- import com.fs.starfarer.api.campaign.SectorEntityToken;
- import com.fs.starfarer.api.campaign.StarSystemAPI;
- import com.fs.starfarer.api.campaign.econ.MarketAPI;
- import com.fs.starfarer.api.campaign.rules.MemoryAPI;
- import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepRewards;
- import com.fs.starfarer.api.impl.campaign.ids.Entities;
- import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
- import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithSearch;
- import com.fs.starfarer.api.ui.TooltipMakerAPI;
- import com.fs.starfarer.api.util.Misc;
- import com.fs.starfarer.api.util.Misc.Token;
- public class watchmen_Scout extends HubMissionWithSearch {
- public static float MISSION_DAYS = 60f;
-
- public enum Stage {
- GO_TO_PROBE,
- RETURN_TO_CHALDEA,
- COMPLETED,
- FAILED,
- }
-
- protected StarSystemAPI system;
- protected SectorEntityToken object;
-
- @Override
- protected boolean create(MarketAPI createdAt, boolean barEvent) {
- // if this mission type was already accepted by the player, abort
- if (!setGlobalReference("$watchmenScout_ref")) {
- return false;
- }
- object = Global.getSector().getEntityById("xenon_sector101debris");
- if (object == null) {
- return false;
- }
- system = object.getStarSystem();
-
- setStartingStage(Stage.GO_TO_PROBE);
- addSuccessStages(Stage.COMPLETED);
- addFailureStages(Stage.FAILED);
- SectorEntityToken probe = spawnEntity(Entities.GENERIC_PROBE, new LocData(object));
- if (probe == null) return false;
-
- probe.setId("watchmenScout_probe");
- makeImportant(probe, "$watchmenScout_probe", Stage.GO_TO_PROBE);
- makeImportant(getPerson(), "$watchmenScout_returnHere", Stage.RETURN_TO_CHALDEA);
- setStageOnGlobalFlag(Stage.RETURN_TO_CHALDEA, "$watchmenScout_canReturn");
- connectWithGlobalFlag(Stage.RETURN_TO_CHALDEA, Stage.COMPLETED, "$watchmenScout_finished");
- setStageOnGlobalFlag(Stage.FAILED, "$watchmenScout_failed");
- setTimeLimit(Stage.FAILED, MISSION_DAYS, system, Stage.RETURN_TO_CHALDEA);
- setCreditReward(CreditReward.AVERAGE);
- setRepRewardPerson(RepRewards.VERY_HIGH);
- setRepRewardFaction(RepRewards.EXTREME);
- beginInRangeOfEntityTrigger(object, 100f, Stage.GO_TO_PROBE);
- triggerCreateFleet(FleetSize.MEDIUM, FleetQuality.VERY_HIGH, "xenon", FleetTypes.PATROL_MEDIUM, object);
- triggerFleetNoJump();
- triggerSetFleetMissionRef("$watchmenScout_ref");
- triggerFleetSetPatrolActionText("等待"); // a bit dark, maybe?
- triggerOrderFleetPatrol(probe);
- triggerOrderFleetInterceptPlayer();
- triggerPickLocationAroundEntity(probe, 500f);
- triggerSpawnFleetAtPickedLocation("$watchmenScout_EnemyFlag", "watchmenScout_ref");
- triggerFleetMakeImportant("$watchmenScout_Enemy", Stage.RETURN_TO_CHALDEA);
- endTrigger();
- return true;
- }
-
- @Override
- protected boolean callAction(String action, String ruleId, InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) {
- return false;
- }
- protected void updateInteractionDataImpl() {
- if (getCurrentStage() != null) {
- set("$watchmenScout_stage", ((Enum)getCurrentStage()).name());
- }
- set("$watchmenScout_starName", system.getNameWithNoType());
- set("$watchmenScout_systemName", system.getNameWithLowercaseTypeShort());
- set("$watchmenScout_dist", getDistanceLY(object));
- set("$watchmenScout_reward", Misc.getWithDGS(getCreditsReward()));
- }
- @Override
- public void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height) {
- float opad = 10f;
- Color h = Misc.getHighlightColor();
- if (currentStage == Stage.GO_TO_PROBE) {
- info.addPara("恢复在 " +
- system.getNameWithLowercaseTypeShort() + " 星系中探测器里的数据包.", opad);
- } else if (currentStage == Stage.RETURN_TO_CHALDEA) {
- info.addPara("将数据包送回Chaldea并与" +
- getPerson().getNameString() + " 谈话获取你的报酬.", opad);
- }
- }
- @Override
- public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) {
- Color h = Misc.getHighlightColor();
- if (currentStage == Stage.GO_TO_PROBE) {
- if (system.isCurrentLocation()) {
- info.addPara("恢复探测器里的数据包", tc, pad);
- } else {
- info.addPara(getGoToSystemTextShort(system), tc, pad);
- }
- return true;
- } else if (currentStage == Stage.RETURN_TO_CHALDEA) {
- info.addPara("回到Chaldea并与" + getPerson().getNameString() + " 对话", tc, pad);
- return true;
- }
- return false;
- }
-
- @Override
- public String getBaseName() {
- return "Recover Scouting Datapack";
- }
-
- }
复制代码 6:Q&A
(待施工)
|
评分
-
查看全部评分
|