nyacronomicon/js/plugins/KN_StatusPoint.js

543 lines
No EOL
20 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*:ja
* @target MZ
* @base PluginCommonBase
* @author kurogoma knights
* @plugindesc KN: ステータスポイント管理プラグイン(単一アクター向け)
*
* @param pointsPerLevel
* @text レベルごとの付与ポイント
* @type number
* @default 1
* @desc レベルが上がったときに付与するポイント数暫定で1
*
* @help
* KN_StatusPoint
* ・目的
* レベルアップ時にステータスポイントを付与し、変数33〜37の値を
* ステータスへ反映しますHP/MPは1ポイント=+5、それ以外は1ポイント=+1
*
* ・仕様(現状)
* - 変数33: HPに対応MHPパラメータ。1ポイント=+5
* - 変数34: MPに対応MMPパラメータ。1ポイント=+5
* - 変数35: 攻撃ATK、1ポイント=+1
* - 変数36: 防御DEF、1ポイント=+1
* - 変数37: 素早さAGI、1ポイント=+1
* - レベルアップで付与されるポイントはプラグインパラメータで設定
* された値デフォルト1。付与先は変数番号22に加算されます。UIは別途作成予定です。
*
* ・備考
* - 本プラグインは単一プレイヤー用対象アクターはID=1に固定
* - 変数に値を入れれば即座にステータスに反映されます(メニュー等はパラメータの再取得で自動更新されます)
*
*/
(function() {
'use strict';
const script = document.currentScript;
const param = PluginManagerEx.createParameter(script);
// エリア
const STAND_AREA_WIDTH = Kurogoma.Map.getStandAreaWidth();
// レベルごとの付与ポイント
const POINTS_PER_LEVEL = Number(param.pointsPerLevel) || 1;
// 変数
const AUTO_TARGET_VAR_ID = Kurogoma.VariableConst.AutoTarget; // 自動割当対象パラメータ保存変数ID 変数の中は("mhp"/"mmp"/"atk"/"def"/"agi"/"")
const UNASSIGNED_VAR_ID = Kurogoma.VariableConst.UnassignedPoints; // 未割当ポイント保存変数ID
// パラメーターID
const STATUS_PARAM_IDS = {
mhp: 0,
mmp: 1,
atk: 2,
def: 3,
agi: 6
};
// パラメーター上限値
const STATUS_LIMITS = {
mhp: 999,
mmp: 999,
atk: 255,
def: 255,
agi: 255
};
// ステータスポイント関連定数
const STATUS_POINT_SYMBOLS = ["mhp", "mmp", "atk", "def", "agi"];
// ステータスポイント表示用ラベル
const STATUS_POINT_LABELS = {
mhp: "HP",
mmp: "MP",
atk: "ATK",
def: "DEF",
agi: "SPD"
};
// ステータスポイント変数ID
const STATUS_POINT_VARIABLE_IDS = {
mhp: Kurogoma.VariableConst.StatusHP,
mmp: Kurogoma.VariableConst.StatusMP,
atk: Kurogoma.VariableConst.StatusATK,
def: Kurogoma.VariableConst.StatusDEF,
agi: Kurogoma.VariableConst.StatusAGI
};
// 上昇量: 1ポイントあたりの増分例: HP/MPは1ポイント=+5
const STATUS_POINT_INCREMENTS = {
mhp: 5,
mmp: 5,
atk: 1,
def: 1,
agi: 1
};
const AUTO_TARGET_OPTIONS = [
{ symbol: "mhp", label: STATUS_POINT_LABELS.mhp, variableValue: "mhp" },
{ symbol: "mmp", label: STATUS_POINT_LABELS.mmp, variableValue: "mmp" },
{ symbol: "atk", label: STATUS_POINT_LABELS.atk, variableValue: "atk" },
{ symbol: "def", label: STATUS_POINT_LABELS.def, variableValue: "def" },
{ symbol: "agi", label: STATUS_POINT_LABELS.agi, variableValue: "agi" },
{ symbol: "none", label: "None", variableValue: "" }
];
// --- レベルアップ時の付与 ---
// 変数20には 'mhp'|'mmp'|'atk'|'def'|'agi' のいずれか(大文字/小文字区別なし)を入れてください。
const _Game_Actor_prototype_changeExp = Game_Actor.prototype.changeExp;
Game_Actor.prototype.changeExp = function(exp, show) {
const oldLevel = this.level;
_Game_Actor_prototype_changeExp.call(this, exp, show);
const newLevel = this.level;
if (newLevel > oldLevel) {
const autoTargetKey = $gameVariables.value(AUTO_TARGET_VAR_ID);
const autoTargetVariableId = STATUS_POINT_VARIABLE_IDS[autoTargetKey]; // なくてもundefinedになるだけ
const gained = (newLevel - oldLevel) * POINTS_PER_LEVEL;
const canAutoGain = checkAutoGain();
if (autoTargetVariableId && canAutoGain) { // mhp/mmp/atk/def/agi
const prev = $gameVariables.value(autoTargetVariableId);
$gameVariables.setValue(autoTargetVariableId, prev + gained);
const label = STATUS_POINT_LABELS[autoTargetKey];
const msg = `${label} has been enhanced!`;
Torigoya.NotifyMessage.Manager.notify(new Torigoya.NotifyMessage.NotifyItem({ message: msg }));
} else if (UNASSIGNED_VAR_ID > 0) {
const prev = Number($gameVariables.value(UNASSIGNED_VAR_ID) || 0);
$gameVariables.setValue(UNASSIGNED_VAR_ID, prev + gained);
}
}
};
// 強化値が最大値に達している場合は自動割当しない機能
function checkAutoGain() {
// - AUTO_TARGET_VAR_ID が空または無効な値なら自動割当しない
// - 対象パラメータが STATUS_LIMITS で定義された最大に達している(>=)なら自動割当しない
// - 増分値で限界を超える場合でも "部分的に" 効果があるなら実行して良いとする。
const key = $gameVariables.value(AUTO_TARGET_VAR_ID) || "";
if (!key) return false;
if (!STATUS_POINT_SYMBOLS.includes(key)) return false;
const paramId = STATUS_PARAM_IDS[key];
if (typeof paramId === "undefined") return false;
// 現在の実際のパラメータ値を確認する(通常は base + 加算分)
const actor = $gameParty.leader();
const current = actor.param(paramId);
const limit = STATUS_LIMITS[key] || actor.paramMax(paramId);
// 既に上限に達している場合は自動割当を行わない
return current < limit;
}
// --- 変数に保存した強化値をパラメータに反映 ---
const _KN_Game_Actor_paramPlus = Game_Actor.prototype.paramPlus;
Game_Actor.prototype.paramPlus = function(paramId) {
let value = _KN_Game_Actor_paramPlus.call(this, paramId);
switch (paramId) {
case STATUS_PARAM_IDS.mhp: // MHP
value += Number($gameVariables.value(STATUS_POINT_VARIABLE_IDS.mhp) || 0) * STATUS_POINT_INCREMENTS.mhp;
break;
case STATUS_PARAM_IDS.mmp: // MMP
value += Number($gameVariables.value(STATUS_POINT_VARIABLE_IDS.mmp) || 0) * STATUS_POINT_INCREMENTS.mmp;
break;
case STATUS_PARAM_IDS.atk: // ATK
value += Number($gameVariables.value(STATUS_POINT_VARIABLE_IDS.atk) || 0) * STATUS_POINT_INCREMENTS.atk;
break;
case STATUS_PARAM_IDS.def: // DEF
value += Number($gameVariables.value(STATUS_POINT_VARIABLE_IDS.def) || 0) * STATUS_POINT_INCREMENTS.def;
break;
case STATUS_PARAM_IDS.agi: // AGI
value += Number($gameVariables.value(STATUS_POINT_VARIABLE_IDS.agi) || 0) * STATUS_POINT_INCREMENTS.agi;
break;
default:
break;
}
return value;
};
// アクターだけ最大値設定 エネミーも含めるならGame_BattlerBaseで上書き
const _Game_Actor_paramMax = Game_Actor.prototype.paramMax;
Game_Actor.prototype.paramMax = function(paramId) {
switch (paramId) {
case STATUS_PARAM_IDS.mhp:
return STATUS_LIMITS.mhp;
case STATUS_PARAM_IDS.mmp:
return STATUS_LIMITS.mmp;
case STATUS_PARAM_IDS.atk:
return STATUS_LIMITS.atk;
case STATUS_PARAM_IDS.def:
return STATUS_LIMITS.def;
case STATUS_PARAM_IDS.agi:
return STATUS_LIMITS.agi;
default:
return _Game_Actor_paramMax.call(this, paramId);
}
};
// ステータスポイント手動割り振り
class Scene_StatusPointAllocate extends Scene_MenuBase {
create() {
super.create();
this._totalAvailable = this.initialAvailablePoints();
this.createAllWindows();
}
createAllWindows() {
this.createBoardWindow();
}
start() {
super.start();
this._boardWindow.activate();
this._boardWindow.select(0);
}
initialAvailablePoints() {
const raw = Number($gameVariables.value(UNASSIGNED_VAR_ID) || 0);
return Math.max(0, Math.floor(raw));
}
boardWindowRect() {
const n = STATUS_POINT_SYMBOLS.length + 3;
const ww = 360;
const wh = this.calcWindowHeight(n, true);
const wx = (Graphics.boxWidth - STAND_AREA_WIDTH - ww) / 2;
const wy = (Graphics.boxHeight - wh) / 2;
return new Rectangle(wx, wy, ww, wh);
}
createBoardWindow() {
const rect = this.boardWindowRect();
const initialAutoSymbol = this.resolveInitialAutoSymbol();
this._boardWindow = new Window_StatusPointBoard(rect, {
totalAvailable: this._totalAvailable,
labels: STATUS_POINT_LABELS,
autoOptions: AUTO_TARGET_OPTIONS,
initialAutoSymbol
});
this._boardWindow.setHandler("ok", this.onBoardOk.bind(this));
this._boardWindow.setHandler("cancel", this.onBoardCancel.bind(this));
this.addWindow(this._boardWindow);
}
onBoardOk() {
const total = this._boardWindow.totalAllocated();
const autoDirty = this._boardWindow.isAutoDirty();
if (total <= 0 && !autoDirty) {
SoundManager.playBuzzer();
this._boardWindow.activate();
return;
}
this.applyAutoTargetSelection();
if (total > 0) {
this.applyAllocations(this._boardWindow.allocations(), total);
}
SoundManager.playOk();
this.popScene();
}
onBoardCancel() {
this.popScene();
}
resolveInitialAutoSymbol() {
const raw = $gameVariables.value(AUTO_TARGET_VAR_ID) || ""; // mhp/mmp/atk/def/agi
const found = AUTO_TARGET_OPTIONS.find(opt => opt.variableValue === raw);
return found ? found.symbol : "none";
}
applyAutoTargetSelection() {
const value = this._boardWindow.currentAutoOption().variableValue;
$gameVariables.setValue(AUTO_TARGET_VAR_ID, value);
}
applyAllocations(allocations, total) {
STATUS_POINT_SYMBOLS.forEach(symbol => {
const add = allocations[symbol];
if (add <= 0) return;
const varId = STATUS_POINT_VARIABLE_IDS[symbol];
if (!varId) return;
const current = Number($gameVariables.value(varId) || 0);
$gameVariables.setValue(varId, current + add);
});
const remain = Math.max(0, Number($gameVariables.value(UNASSIGNED_VAR_ID) || 0) - total);
$gameVariables.setValue(UNASSIGNED_VAR_ID, remain);
$gameParty.leader().refresh();
}
}
// ステータスポイントウィンドウ
class Window_StatusPointBoard extends Window_Selectable {
initialize(rect, options) {
this._labels = options.labels;
this._totalAvailable = Math.max(0, Math.floor(options.totalAvailable));
this._baseValues = this.createBaseValues();
this.initAllocations();
this._autoOptions = options.autoOptions;
this._autoIndex = this.indexOfAutoSymbol(options.initialAutoSymbol);
if (this._autoIndex < 0) this._autoIndex = 0;
this._autoInitialIndex = this._autoIndex;
super.initialize(rect);
this.refresh();
this.select(0);
}
initAllocations() {
this._allocations = {};
STATUS_POINT_SYMBOLS.forEach(symbol => this._allocations[symbol] = 0);
}
createBaseValues() {
const actor = $gameParty.leader();
const values = {};
STATUS_POINT_SYMBOLS.forEach(symbol => {
values[symbol] = actor.param(STATUS_PARAM_IDS[symbol]);
});
return values;
}
maxCols() {
return 1;
}
maxItems() {
return STATUS_POINT_SYMBOLS.length + 2;
}
rowSpacing() {
return 4;
}
headerHeight() {
return this.lineHeight();
}
separatorThickness() {
return 2;
}
headerOffset() {
return this.headerHeight() + this.separatorThickness() + 8;
}
contentsHeight() {
const base = super.contentsHeight.call(this);
return base + this.headerOffset();
}
autoIndex() {
return STATUS_POINT_SYMBOLS.length;
}
confirmIndex() {
return STATUS_POINT_SYMBOLS.length + 1;
}
totalAllocated() {
return STATUS_POINT_SYMBOLS.reduce((sum, key) => sum + this._allocations[key], 0);
}
remainingPoints() {
return Math.max(0, this._totalAvailable - this.totalAllocated());
}
allocations() {
return Object.assign({}, this._allocations);
}
isAutoDirty() {
return this._autoIndex !== this._autoInitialIndex;
}
currentAutoOption() {
return this._autoOptions[this._autoIndex];
}
itemRect(index) {
const rect = super.itemRect(index);
rect.y += this.headerOffset();
return rect;
}
refresh() {
this.contents.clear();
this.drawHeader();
this.drawItems();
this.refreshCursor();
}
drawItems() {
for (let i = 0; i < this.maxItems(); i++) {
this.drawItemBackground(i);
this.drawItem(i);
}
}
drawHeader() {
const label = "Available Points: ";
const value = `${this.remainingPoints()}P`;
const contentWidth = this.contentsWidth();
const totalText = label + value;
const totalWidth = this.textWidth(totalText);
const startX = Math.max(0, (contentWidth - totalWidth) / 2);
this.drawText(label, startX, 0, totalWidth, "left");
const labelWidth = this.textWidth(label);
this.drawText(value, startX + labelWidth, 0, totalWidth - labelWidth, "left");
this.resetTextColor();
}
drawItem(index) {
const rect = this.itemLineRect(index);
this.resetTextColor();
if (index < STATUS_POINT_SYMBOLS.length) {
const symbol = STATUS_POINT_SYMBOLS[index];
const label = this._labels[symbol];
const value = this.displayStatValue(symbol);
const increment = this._allocations[symbol];
const valueWidth = 80;
const labelWidth = Math.max(0, rect.width - valueWidth);
this.changeTextColor(ColorManager.systemColor());
this.drawText(label, rect.x, rect.y, labelWidth, "left");
this.resetTextColor();
let displayText = String(value);
if (increment > 0) {
displayText += ` (+${increment})`;
}
this.drawText(displayText, rect.x, rect.y, rect.width, "right");
} else if (index === this.autoIndex()) {
const option = this.currentAutoOption();
const label = option ? option.label : "";
this.drawText(`Auto-Assign <${label}>`, rect.x, rect.y, rect.width, "center");
} else {
const enabled = this.isConfirmEnabled();
this.changePaintOpacity(enabled);
this.drawText("Confirm", rect.x, rect.y, rect.width, "center");
this.changePaintOpacity(true);
}
}
isCurrentItemEnabled() {
if (this.index() === this.confirmIndex()) {
return this.isConfirmEnabled();
}
return true;
}
isConfirmEnabled() {
return this.totalAllocated() > 0 || this.isAutoDirty();
}
cursorRight(wrap) {
const index = this.index();
if (index < STATUS_POINT_SYMBOLS.length) {
this.adjustStatValue(1);
} else if (index === this.autoIndex()) {
this.changeAutoSelection(1);
} else {
super.cursorRight(wrap);
}
}
cursorLeft(wrap) {
const index = this.index();
if (index < STATUS_POINT_SYMBOLS.length) {
this.adjustStatValue(-1);
} else if (index === this.autoIndex()) {
this.changeAutoSelection(-1);
} else {
super.cursorLeft(wrap);
}
}
processOk() {
const idx = this.index();
if (idx < STATUS_POINT_SYMBOLS.length) {
this.adjustStatValue(1);
return;
}
if (idx === this.autoIndex()) {
this.changeAutoSelection(1);
return;
}
if (idx === this.confirmIndex()) {
if (this.isConfirmEnabled()) {
if (this.totalAllocated() > 0) {
AudioManager.playSe({ name: "Absorb2", pan: 0, pitch: 100, volume: 90 });
}
super.processOk();
} else {
SoundManager.playBuzzer();
}
} else {
SoundManager.playBuzzer();
}
}
adjustStatValue(step) {
const symbol = STATUS_POINT_SYMBOLS[this.index()];
const current = this._allocations[symbol];
if (step > 0) {
const pointValue = STATUS_POINT_INCREMENTS[symbol];
const limit = STATUS_LIMITS[symbol];
const base = this._baseValues[symbol];
const statAfter = base + (current + 1) * pointValue;
if (this.remainingPoints() <= 0 || statAfter > limit) {
SoundManager.playBuzzer();
return;
}
this._allocations[symbol] = current + 1;
SoundManager.playCursor();
} else if (step < 0) {
if (current <= 0) {
SoundManager.playBuzzer();
return;
}
this._allocations[symbol] = current - 1;
SoundManager.playCursor();
}
this.refresh();
}
changeAutoSelection(delta) {
if (!this._autoOptions.length) return;
const max = this._autoOptions.length;
this._autoIndex = (this._autoIndex + delta + max) % max;
SoundManager.playCursor();
this.refresh();
}
displayStatValue(symbol) {
const base = this._baseValues[symbol];
const allocation = this._allocations[symbol];
const pointValue = STATUS_POINT_INCREMENTS[symbol];
return base + allocation * pointValue;
}
indexOfAutoSymbol(symbol) {
if (!symbol) return -1;
return this._autoOptions.findIndex(opt => opt.symbol === symbol);
}
}
window.Kurogoma = window.Kurogoma || {};
window.Kurogoma.Scene_StatusPointAllocate = Scene_StatusPointAllocate;
})();