MC击杀者判定系统

联机侠
1-140422000153
※警告:此文内容为18w03b实现,其他版本不生效作者概不负责
  有人看到这个标题可能会问我,击杀判定不是已经被MC内部代码实现了吗?确实,但是那是玩家死亡的击杀判定。有些情况我们不希望玩家死亡,但是在特定条件下判定击杀。就比如我们为了节省玩家掉进虚空死亡的时间,在玩家坐标小于1的时候就判定击杀,那么我们就需要知道谁击杀了玩家。
我们这次只考虑玩家击杀的情况。
一个很简单的思路就是,我们只要知道谁最后击中了玩家,就判定谁是击杀者(当然可以根据自己的游戏规则更改)
为了记录攻击者,我们创建一个lastHitter记分板
init.mcfunction
  1. scoreboard objectives add lastHitter dummy

复制代码

所以我们先考虑近战击中的情况:
很容易就能想到,只要知道谁是攻击者,谁是被攻击者,就能给被攻击者的最后攻击者记分板赋予一个值...
等等,玩家还能作为记分板的值?

 


为了解决上述问题我们插入一个略显无关的教程。为了解决记录玩家的问题我们需要赋予每个玩家一个特有的id以方便在记分板中记录。
我们创建一个id记分板
  1. scoreboard objectives add id dummy

复制代码

那么怎么赋予玩家id呢?

很显然,我们只要给没有id的玩家赋予最高id+1的分数就行了
但是考虑到最高id分数玩家可能正好不在,所以我们需要一个东西来记录最高id
那为了方便我们干脆召唤一个盔甲架来持有一些特有量吧
  1. execute unless entity @e[name=constants] run summon minecraft:armor_stand ~ ~ ~ {CustomName:"[\"constants\"]",Invisible:1,NoGravity:1,Marker:1}
  2. scoreboard players add @e[name=constants] id 0
  3. scoreboard players add @e[name=constants,scores={id=0}] id 1

复制代码

从上述命令我们可以看到我们召唤了一个名为costants的盔甲架。给它加上分数0以让他拥有id记分板,随后如果他的id是0那么就加上1

所以如果这个盔甲架不存在的情况就会被召唤一个并且赋予id=1
我之前说这是最高值其实不准确,用"next"可能更能贴切地表达这个盔甲架的用途。它承载着会被赋予下一个玩家的id分数
我们把这几个命令写进init.mcfunction里并且放到#minecraft:load这个function tag内,以便在datapack加载时运行
这时候我们写一个allocid.mcfunction
  1. scoreboard players operation @s id = @e[name=constants,limit=1] id
  2. scoreboard players add @e[name=constants] id 1

复制代码

这两行命令的意图显而易见我就不解释了

然后在ticks.mcfunction内
  1. scoreboard players add @a id 0
  2. execute as @a[scores={id=0},limit=1] run function namespace:allocid

复制代码

※ 命名空间什么的你们就随意啦

同样我们让玩家拥有id记分板然后让所有id=0的玩家执行allocid

至于为什么要limit=1,是防止多个玩家同时没有id的情况,同时执行会出现多个玩家分得同一个id的尴尬情况,所以一个一个来
把ticks.mcfunction放进#minecraft:ticks这个function tag内,以便每tick执行一次
至此,id分配内容结束

好了,既然id已经分配到了我们可以继续考虑攻击者的问题了。

我们知道advancement(以下简称adv)可以触发攻击和被攻击事件,但是……他们只能把触发者(攻击者或被攻击者)传入reward function里面可是我们两个都需要啊???不过这个问题我们稍后再解决,我们先把adv写好
stickhit.json
  1. {
  2.     "display": {
  3.         "announce_to_chat": false,
  4.         "hidden": true,
  5.         "show_toast": false,
  6.         "icon": {
  7.             "item":"minecraft:stick"
  8.         },
  9.         "description":"",
  10.         "title":""
  11.     },
  12.     "criteria": {
  13.         "bow_hit": {
  14.             "trigger": "minecraft:player_hurt_entity",
  15.             "conditions": {
  16.                 "damage": {
  17.                     "direct_entity": {
  18.                         "type":"player"
  19.                     }
  20.                 }
  21.             }
  22.         }
  23.     },
  24.     "rewards": {
  25.         "function":"namespace:stickhit"
  26.     }
  27. }

复制代码

bestickhit.json
  1. {
  2.     "display": {
  3.         "announce_to_chat": false,
  4.         "hidden": true,
  5.         "show_toast": false,
  6.         "icon": {
  7.             "item":"minecraft:stick"
  8.         },
  9.         "description":"",
  10.         "title":""
  11.     },
  12.     "criteria": {
  13.         "bow_hit": {
  14.             "trigger": "minecraft:entity_hurt_player",
  15.             "conditions": {
  16.                 "damage": {
  17.                     "direct_entity": {
  18.                         "type":"player"
  19.                     }
  20.                 }
  21.             }
  22.         }
  23.     },
  24.     "rewards": {
  25.         "function":"namespace:bestickhit"
  26.     }
  27. }

复制代码

※ 好像对齐有点怪……不管了忍一下吧

前者是击中判定,后者是被击中判定。因为我的小游戏里的武器是stick所以就叫bestickhit之类的了
这两个adv也没什么稀奇的我就不解释了
然后我们正式把目光移向重点——reward function
stickhit.mcfunction
  1. tag @s add stickhit
  2. advancement revoke @s only namespace:stickhit

复制代码

※ 超短的

没错我们干的事很简单,就是tag了一下这个玩家并且移除这个adv以重复触发而已
同样的,bestickhit.mcfunction
  1. tag @s add bestickhit
  2. scoreboard players set @s fromDamaged 0 #请先忽视它
  3. advancement revoke @s only namespace:bestickhit

复制代码

干的事也一样,至于第二行后面会提到的,先忽视就好

现在我们拥有了两个玩家的标记……但是还是没法配对啊?
不要怕,先看命令:
stickhitrecord.mcfunction
  1. scoreboard players operation @a[sort=nearest,tag=bestickhit,limit=1,distance=0.3..] lastHitter = @s id
  2. tag @s remove stickhit
  3. tag @a[sort=nearest,tag=bestickhit,limit=1,distance=0.3..] remove bestickhit

复制代码

ticks.mcfunction
  1. # ...Previous Commands...
  2. execute as @a[tag=stickhit] run function namespace:stickhitrecord

复制代码

我们现在看看都干了些什么
我们在每tick内以所有stickhit tag拥有着的名义执行stickhitrecord,在其中我们先把离这位执行者最近的bestickhit拥有者的lastHitter记分板标为自己的id,并且移除这两人的tag。distance=0.3...意为选中玩家的距离离执行者得≥3,防止选到自己,因为自己也有可能是bestickhit拥有者啊2333
至于选择最近的玩家,是因为我们只能认为通常被击中者是离攻击玩家最近的了
尽管这个判定可能会造成误差,但是至少能基本确定近战的击中者了

好的,我们进阶,来考虑一下如何判断远距离武器击杀吧
距离肯定是行不通了,不然怎么叫远距离武器呢?
所以,我们来转换一下思维
如果箭拥有其主人的id,那不就好判断了吗?
循着这种思路,我们先创建两个记分板
init.mcfunction
  1. scoreboard objectives add owner dummy
  2. scoreboard objectives add lifeTime dummy

复制代码

为什么是两个?过会就知道了

我们总得赋予这两个记分板拥有着啊
ticks.mcfunction
  1. scoreboard players add @e[type=arrow] lifeTime 1
  2. execute as @e[type=arrow,scores={lifeTime=1}] at @s run scoreboard players operation @s owner = @a[sort=nearest,limit=1] id

复制代码

我们每tick所有箭的lifeTime+1,然后让刚出生(?)的箭把离自己的owner附上最近的人的id,很好理解吧

※ 其实这个lifeTime本来是其他作用的正好能用上我就用了
然后我们就可以adv触发被箭攻击事件,然后开开心心地赋予owner……等等,等adv被触发结束箭已经击中玩家消失了啊?
你的笑容是不是凝固在脸上?没关系,我们还有解决的办法
init.mcfunction
  1. scoreboard objectives add possiblyArrowHit dummy

复制代码

※ 啊不要吐槽这个命名

我们创建一个新记分板(以下简称poss),板如其名,可能被箭击中。文字表述比较难表达它的作用,我们直接贴一下调用
ticks.mcfunction
  1. execute as @e[type=arrow,scores={lifeTime=2..},nbt={inGround:0b}] at @s run scoreboard players operation @a[distance=0.1..3] possiblyArrowHit = @s owner

复制代码

我们以所有还在飞行并且出生过了一段时间(?)的箭的位置,把周围3格内的玩家的poss标记成自己的owner
这有什么用?
先继续看
bearrowhit.json
  1. {
  2.     "display": {
  3.         "announce_to_chat": false,
  4.         "hidden": true,
  5.         "show_toast": false,
  6.         "icon": {
  7.             "item":"minecraft:bow"
  8.         },
  9.         "description":"",
  10.         "title":""
  11.     },
  12.     "criteria": {
  13.         "bow_hit": {
  14.             "trigger": "minecraft:player_hurt_entity",
  15.             "conditions": {
  16.                 "damage": {
  17.                     "direct_entity": {
  18.                         "type":"arrow"
  19.                     }
  20.                 }
  21.             }
  22.         }
  23.     },
  24.     "rewards": {
  25.         "function":"namespace:bearrowhit"
  26.     }
  27. }

复制代码

※ 直接vscode复制过来竟然带特效 duangduangduang 前面的懒得重新复制了凑合着吧
bearrowhit.mcfunction

  1. scoreboard players operation @s lastHitter = @s possiblyArrowHit
  2. scoreboard players set @s fromDamaged 0 # 忽视

复制代码

是不是有一种茅塞顿开的感觉?
没错我们把沿途所有可能击中的玩家全部标上,但是只有最终击中的那个才会被正式计入击中者
然后远程攻击判定也就到这里了
不过最后我们解决几个无关紧要的问题
1.玩家自杀怎么判断?
这个我们真没办法,但是你可以用上那个fromDamaged,每tick增加1,到一定值归位并且把满值的玩家的lastHitter清零
2.偶尔判定lastHitter就是死亡者自身,怎么办?

  1. execute as @a run scoreboard players operation @s lastHitter -= @s id
  2. execute as @a[scores={lastHitter=1..}] run scoreboard players operation @s lastHitter += @s id
  3. execute as @a[scores={lastHitter=..-1}] run scoreboard players operation @s lastHitter += @s id

复制代码

这样解决……至于是什么意思,请自己揣摩吧。实在不懂的话我会在评论区解答,算一个小小的作业
3.怎么选择器选择击杀者?分数没法动态导入选择器里啊?
那就把击杀玩家分数特化(快记笔记)

  1. scoreboard players operation @a[distance=0.3..] id -= @s lastHitter
  2. tellraw @a [{"color":"red","selector":"@s"},{"color":"grey","text":" 被 "},{"color":"gold","selector":"@a[scores={id=0}]"},{"color":"grey","text":" 击杀了"}]
  3. scoreboard players operation @a[distance=0.3..] id += @s lastHitter

复制代码

像这样……我把id特化成0了。具体自己研究吧

联机侠

发表评论

您必须 登录 才能发表留言!