前言

首先算不上什么高技术力教程,只是一点自己的整理

然后开始之前先说明一下这个插件能干什么:根据你游戏里的某个数值(比如金币数,变量值之类的)画一个条,并且给这个条取一个名字。但它并不能直接控制你的这个数值,换句话说只能画个条给你看,想控制这个数值还是得你自己解决

准备工作

VisuStella 的 Core Engine 和 Skill and State Engine(前者为前置)

我打算写一个新的技能消耗条,并写一个消耗特定变量的技能

解读

设置自定义cost的地方就在这里了,这里自带的消耗道具的为例

那么要自定义消耗就要靠下面这一排JS了,大致分为三个部分

  • Cost Processing:技能消耗的流程,分为计算消耗、判断是否可进行消耗、进行消耗
  • Window Display:设置在窗口中的显示
  • Gauge Display:显示值槽的方式,包括最大值、当前值和值的具体绘制

接下来试着对这些代码进行解读。

首先是计算部分:

其实就是通过特性和注释标签的影响和修正,来得出实际消耗的值

1
2
3
4
// Declare Variables
const user = this;
const skill = arguments[0];
let cost = 0;

首先对使用者和技能和消耗值进行声明,使用者就是该函数的调用者,技能编号和消耗都先设置为0
然后是进行计算:

1
2
3
4
5
6
7
8
9
// Calculations
const note = skill.note;
if (note.match(/<POTION COST:[ ](\d+)>/i)) {
cost += Number(RegExp.$1); // cost值为具体的数字时
}
if (note.match(/<JS POTION COST>\s*([\s\S]*)\s*<\/JS POTION COST>/i)) {
const code = String(RegExp.$1); //使用JS代码来设置cost值时
eval(code); //执行这里自定义的JS代码
}

这里是读取技能注释中的设定,如果要自己写的话,就要注意把这里的注释标签给改了

1
2
3
4
5
6
7
8
9
10
11
12
// Apply Trait Cost Alterations
if (cost > 0) {
const rateNote = /<POTION COST:[ ](\d+\.?\d*)([%%])>/i;
const rates = user.traitObjects().map((obj) => (obj && obj.note.match(rateNote) ? Number(RegExp.$1) / 100 : 1));
// 百分比修正
const flatNote = /<POTION COST:[ ]([\+\-]\d+)>/i;
const flats = user.traitObjects().map((obj) => (obj && obj.note.match(flatNote) ? Number(RegExp.$1) : 0));
// 固定值修正
cost = rates.reduce((r, rate) => r * rate, cost);
cost = flats.reduce((r, flat) => r + flat, cost);
cost = Math.max(1, cost);
}

这里应用特性(就是数据库里右上角那一大块)对技能消耗的影响

1
2
3
4
5
6
7
8
9
10
// Set Cost Limits
if (note.match(/<POTION COST MAX:[ ](\d+)>/i)) {
cost = Math.min(cost, Number(RegExp.$1));
}
if (note.match(/<POTION COST MIN:[ ](\d+)>/i)) {
cost = Math.max(cost, Number(RegExp.$1));
}

// Return cost value
return Math.round(Math.max(0, cost));

这里就是应用消耗的最大值和最小值,同样是应用注释标签的内容

接下来是判断部分:

就是判断消耗够不够支付

1
2
3
4
5
6
7
8
9
10
11
12
// Declare Variables
const user = this;
const skill = arguments[0];
const cost = arguments[1];
const item = $dataItems[7]; // 要消耗的道具编号,从0开始

// Return Boolean
if (user.isActor() && cost > 0) {
return $gameParty.numItems(item) >= cost; //检查队伍中的道具数量
} else {
return true; //消耗为0时或使用者非角色时,返回true
}

将道具数量与消耗数进行比较,根据结果返回 t / f ,如果消耗为0或使用者非角色,则返回true

进行实际的消耗

1
2
3
4
5
6
7
8
9
10
// Declare Variables
const user = this;
const skill = arguments[0];
const cost = arguments[1];
const item = $dataItems[7];

// Process Payment
if (user.isActor()) {
$gameParty.loseItem(item, cost); //扣除相应的道具和数量
}

接下来开始就是视觉部分

是否显示消耗:

1
2
3
4
5
6
7
// Declare Variables
const user = this;
const skill = arguments[0];
const cost = arguments[1];

// Return Boolean
return cost > 0; // 只在消耗大于0时显示

显示消耗的文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Declare Variables
const user = this;
const item = $dataItems[7];
const skill = arguments[0];
const cost = arguments[1];
const settings = arguments[2];
const fontSize = settings.FontSize;
const color = settings.FontColor;
const name = settings.Name;
const icon = settings.Icon;
let text = '';

// Text: Change Font Size
text += '\\FS[%1]'.format(fontSize);

// Text: Add Color
if (color.match(/#(.*)/i) && Imported.VisuMZ_1_MessageCore) {
text += '\\HexColor<#%1>'.format(String(RegExp.$1));
} else {
text += '\\C[%1]'.format(color);
}

// Text: Add Cost
text += '×%1'.format(cost);

// Text: Add Icon
text += '\\I[%1]'.format(item.iconIndex);

// Return text
return text;

就是显示字体的尺寸颜色,以及显示消耗量和对应图标,应该不用解释太多了

接下来就是绘制值槽的部分

首先是最大值

1
2
3
4
5
6
7
// JS: Maximum value 
// Declare Variables
const user = this;
const item = $dataItems[7];

// Return value
return $gameParty.maxItems(item);

这里是通过获取道具的最大值来判断该消耗条的上限,如果打算用变量作为消耗,就直接填一个值就行了

然后是获取当前值

1
2
3
4
5
6
// Declare Variables
const user = this;
const item = $dataItems[7];

// Return value
return $gameParty.numItems(item);

直接获取当前道具数就行了,没什么可说的

然后就是绘制消耗条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Declare Settings
const color1 = ColorManager.textColor(30);
const color2 = ColorManager.textColor(31);

// Declare Variables
const sprite = this;
const settings = sprite._costSettings;
const bitmap = sprite.bitmap;
const user = sprite._battler;
const item = $dataItems[7];
const currentValue = sprite.currentDisplayedValue();
const bitmapWidth = sprite.bitmapWidth();
const bitmapHeight = sprite.textHeight ? sprite.textHeight() : sprite.bitmapHeight();
const gaugeHeight = sprite.gaugeHeight();

// Draw Gauge
const gx = 0;
const gy = bitmapHeight - gaugeHeight;
const gw = bitmapWidth - gx;
const gh = gaugeHeight;
this.drawFullGauge(color1, color2, gx, gy, gw, gh);

// Draw Icon
const iconIndex = item.iconIndex;
const iconBitmap = ImageManager.loadSystem("IconSet");
const pw = ImageManager.iconWidth;
const ph = ImageManager.iconHeight;
const sx = (iconIndex % 16) * pw;
const sy = Math.floor(iconIndex / 16) * ph;
bitmap.blt(iconBitmap, sx, sy, pw, ph, 0, 0, 24, 24);

// Draw Value
const vw = bitmapWidth - 2;
const vh = bitmapHeight;
sprite.setupValueFont();
bitmap.textColor = ColorManager.normalColor();
bitmap.drawText(currentValue, 0, 0, vw, vh, "right");

也没什么特别需要强调的看就完了

尝试实操

那么直接复制一份potion为样例,我们自己来改

这里我们给消耗命名为“日光”,取5号变量

首先是计算部分,将涉及到注释标签的地方给改了,其他地方不用动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Declare Variables
const user = this;
const skill = arguments[0];
let cost = 0;

// Calculations
const note = skill.note;
if (note.match(/<SOLAR COST:[ ](\d+)>/i)) {
cost += Number(RegExp.$1);
}
if (note.match(/<JS SOLAR COST>\s*([\s\S]*)\s*<\/JS SOLAR COST>/i)) {
const code = String(RegExp.$1);
eval(code);
}

// Apply Trait Cost Alterations
if (cost > 0) {
const rateNote = /<SOLAR COST:[ ](\d+\.?\d*)([%%])>/i;
const rates = user.traitObjects().map((obj) => (obj && obj.note.match(rateNote) ? Number(RegExp.$1) / 100 : 1));
const flatNote = /<SOLAR COST:[ ]([\+\-]\d+)>/i;
const flats = user.traitObjects().map((obj) => (obj && obj.note.match(flatNote) ? Number(RegExp.$1) : 0));
cost = rates.reduce((r, rate) => r * rate, cost);
cost = flats.reduce((r, flat) => r + flat, cost);
cost = Math.max(1, cost);
}

// Set Cost Limits
if (note.match(/<SOLAR COST MAX:[ ](\d+)>/i)) {
cost = Math.min(cost, Number(RegExp.$1));
}
if (note.match(/<SOLAR COST MIN:[ ](\d+)>/i)) {
cost = Math.max(cost, Number(RegExp.$1));
}

// Return cost value
return Math.round(Math.max(0, cost));

然后是判断是否可以支付消耗:

1
2
3
4
5
6
7
8
9
10
11
// Declare Variables
const user = this;
const skill = arguments[0];
const cost = arguments[1];

// Return Boolean
if (user.isActor() && cost > 0) {
return $gameVariables.value(5) >= cost; // !
} else {
return true;
}

接下来是支付消耗:

1
2
3
4
5
6
7
8
9
10
// Declare Variables
const user = this;
const skill = arguments[0];
const cost = arguments[1];

// Process Payment
if (user.isActor()) {
const currentValue = $gameVariables.value(5);
$gameVariables.setValue(5, currentValue - cost);
}

将5号变量减去对应的消耗值

窗口显示可以先暂时不用改,

接下来是获取最大值,直接定位固定的100就行了

1
2
3
4
5
6
// JS: Maximum value 
// Declare Variables
const user = this;

// Return value - 固定为100
return 100;

获取当前值:

1
2
3
4
5
6
// JS: current value
// Declare Variables
const user = this;

// Return value - 获取5号变量的值
return $gameVariables.value(5);

绘制值槽,直接贴过来就行了

测试

测试之前先在数据库测试一下

先把主角的MP条给换了,在数据库中的职业备注加上一条

image-20260121171821499

(我这里弄错了哈。。是放在职业不是角色)

新建一个技能,进行如下设置:

image-20260121175851677

主要是注意备注栏的部分

<JS Skill Enable>是判断技能是否可用,这里是判断5号变量是否大于等于10

<solar cost: 10>就是在技能后面显示消耗多少而已,而它并不会实际帮你扣掉的(也就是说它只是帮你在游戏中显示出来而无法真正的控制数值)

所以要加一个公共事件,在公共事件里面扣除10的变量值就行了

别忘了设置好5号变量(图里弄错了)

image-20260121172159244

进游戏看到我们主角的MP条已经变了(这里显示比较怪,等会再重新改一下)

image-20260121172802656

在旁边放了条狗,设置对话一次加50点变量,对话两次看到数值也的确变了

image-20260121173059315

进战斗测试,使用技能时自动扣掉10点日光,日光被扣到10点一下后无法使用技能,ok没问题(这里就不截图了)

等待解决的问题

值槽绘制的问题

刚才的测试中值槽没显示出来,是因为我没改默认的值槽颜色,在这里

1
2
const color1 = ColorManager.textColor(6);
const color2 = ColorManager.textColor(31);

把颜色索引给改了

但是条的位置有点怪怪的,这个暂时不会改,搁置(其实干脆别画值槽了也行)

数值上限

这个日光值虽然设置了100的maximum,但是在通过事件获取变量时仍然会超出100

我直接用并行处理的公共事件来检查了,超过100就直接强行重置为100,但是并行处理事件可能会影响游戏效率,所以仍然需要想办法改善

后记

好麻烦,要不还是别用了吧。。。