제대로 된 스크립트를 작성하기 위해서
RPG MV의 시스템 스크립트 일부를
이해할 필요가 있기에 먼저 설명을 좀 하겠습니다.
1. 배틀러가 행동을 시작합니까?
BattleManager.startAction()
을 통해 하게 됩니다.
//일반적으로 배틀러의 턴을 실행하는 함수 BattleManager.processTurn = function() { var subject = this._subject; var action = subject.currentAction(); if (action) { action.prepare(); if (action.isValid()) { this.startAction(); } subject.removeCurrentAction(); } else { subject.onAllActionsEnd(); this.refreshStatus(); this._logWindow.displayAutoAffectedStatus(subject); this._logWindow.displayCurrentState(subject); this._logWindow.displayRegeneration(subject); this._subject = this.getNextSubject(); } }; //--------------------------------------------------------------------------------- //강제적인 행동에 의한 배틀러의 행동을 실행하는 함수 BattleManager.processForcedAction = function() { if (this._actionForcedBattler) { this._subject = this._actionForcedBattler; this._actionForcedBattler = null; this.startAction(); this._subject.removeCurrentAction(); } }; //--------------------------------------------------------------------------------- //배틀러의 실제적인 행동 실행 함수 BattleManager.startAction = function() { var subject = this._subject; var action = subject.currentAction(); var targets = action.makeTargets(); this._phase = 'action'; this._action = action; this._targets = targets; subject.useItem(action.item()); this._action.applyGlobal(); this.refreshStatus(); this._logWindow.startAction(subject, action, targets); };
2. 행동하는 배틀러는 누구입니까?
subject = this._subject;
this._subejct 가 가르키는 actor 입니다.
this._subject = this.getNextSubject();
BattleManager.getNextSubject = function() { for ( ; ; ) { var battler = this._actionBattlers.shift(); if (!battler) { return null; } if (battler.isBattleMember() && battler.isAlive()) { return battler; } } };
또는
this._subject = this._actionForcedBattler;
this._actionForcedBattler 에 대한 데이터의 흐름
1) Game_Interpreter의 강제적 행동 판정
// Force Action Game_Interpreter.prototype.command339 = function() { this.iterateBattler(this._params[0], this._params[1], function(battler) { if (!battler.isDeathStateAffected()) { battler.forceAction(this._params[2], this._params[3]); BattleManager.forceAction(battler); this.setWaitMode('action'); } }.bind(this)); return true; };
2) BattleManager의 강제적 행동 설정
BattleManager.forceAction = function(battler) { this._actionForcedBattler = battler; var index = this._actionBattlers.indexOf(battler); if (index >= 0) { this._actionBattlers.splice(index, 1); } };
3. 배틀러는 어떤 행동을 하게 됩니까?
action = subejct.currentAction();
subejct.currentAction() 에 설정되어 있는 action 을 하게 됩니다.
Game_Battler.prototype.currentAction = function() { return this._actions[0]; };
4. 행동의 대상은 누가 됩니까?
targets = action.makeTargets();
targets 로 지정된 대상들입니다.
Game_Action.prototype.makeTargets = function() { var targets = []; if (!this._forcing && this.subject().isConfused()) { targets = [this.confusionTarget()]; } else if (this.isForOpponent()) { targets = this.targetsForOpponents(); } else if (this.isForFriend()) { targets = this.targetsForFriends(); } return this.repeatTargets(targets); }; Game_Action.prototype.repeatTargets = function(targets) { var repeatedTargets = []; var repeats = this.numRepeats(); for (var i = 0; i < targets.length; i++) { var target = targets[i]; if (target) { for (var j = 0; j < repeats; j++) { repeatedTargets.push(target); } } } return repeatedTargets; };
4-1. 행동의 대상을 결정하는 함수들
Game_Action.prototype.confusionTarget = function() { switch (this.subject().confusionLevel()) { case 1: return this.opponentsUnit().randomTarget(); case 2: if (Math.randomInt(2) === 0) { return this.opponentsUnit().randomTarget(); } return this.friendsUnit().randomTarget(); default: return this.friendsUnit().randomTarget(); } }; Game_Action.prototype.targetsForOpponents = function() { var targets = []; var unit = this.opponentsUnit(); if (this.isForRandom()) { for (var i = 0; i < this.numTargets(); i++) { targets.push(unit.randomTarget()); } } else if (this.isForOne()) { if (this._targetIndex < 0) { targets.push(unit.randomTarget()); } else { targets.push(unit.smoothTarget(this._targetIndex)); } } else { targets = unit.aliveMembers(); } return targets; }; Game_Action.prototype.targetsForFriends = function() { var targets = []; var unit = this.friendsUnit(); if (this.isForUser()) { return [this.subject()]; } else if (this.isForDeadFriend()) { if (this.isForOne()) { targets.push(unit.smoothDeadTarget(this._targetIndex)); } else { targets = unit.deadMembers(); } } else if (this.isForOne()) { if (this._targetIndex < 0) { targets.push(unit.randomTarget()); } else { targets.push(unit.smoothTarget(this._targetIndex)); } } else { targets = unit.aliveMembers(); } return targets; };
[ 생략 - BattleManager에 _phase / _action / _targets 데이터 지정해주기 ]
5. 배틀러가 아이템을 사용합니까?
: subject.useItem(action.item());
action.item()을 통해 알려주는 아이템을 사용합니다.
Game_Battler.prototype.useItem = function(item) { if (DataManager.isSkill(item)) { this.paySkillCost(item); } else if (DataManager.isItem(item)) { this.consumeItem(item); } };
6. 사용한 아이템의 효과를 적용해줍니다.
Game_Action.prototype.applyGlobal = function() { this.item().effects.forEach(function(effect) { if (effect.code === Game_Action.EFFECT_COMMON_EVENT) { $gameTemp.reserveCommonEvent(effect.dataId); } }, this); };
7. BattleManager._phase 에 따른 실행 함수모음
실제로 상황에 따른 행동을 실행하는 로직들입니다.
//Scene_Battle부분은 배틀러의 행동을 //결정하는 행동선택 메뉴가 나오는 부분입니다. //Scene_Battle을 통해서 행동을 결정하기에 //간략하게 넣었습니다. //어느 곳에서부터 온 코드들인지는 //직접 찾아보시는 것이 도움이됩니다. Scene_Battle.prototype.updateBattleProcess = function() { if (!this.isAnyInputWindowActive() || BattleManager.isAborting() || BattleManager.isBattleEnd()) { BattleManager.update(); this.changeInputWindow(); } }; BattleManager.update = function() { if (!this.isBusy() && !this.updateEvent()) { switch (this._phase) { case 'start': this.startInput(); break; case 'turn': this.updateTurn(); break; case 'action': this.updateAction(); break; case 'turnEnd': this.updateTurnEnd(); break; case 'battleEnd': this.updateBattleEnd(); break; } } }; BattleManager.updateAction = function() { var target = this._targets.shift(); if (target) { this.invokeAction(this._subject, target); } else { this.endAction(); } }; BattleManager.endAction = function() { this._logWindow.endAction(this._subject); this._phase = 'turn'; }; //------------------------------------------------------------------------ BattleManager.invokeAction = function(subject, target) { this._logWindow.push('pushBaseLine'); if (Math.random() < this._action.itemCnt(target)) { this.invokeCounterAttack(subject, target); } else if (Math.random() < this._action.itemMrf(target)) { this.invokeMagicReflection(subject, target); } else { this.invokeNormalAction(subject, target); } subject.setLastTarget(target); this._logWindow.push('popBaseLine'); this.refreshStatus(); }; BattleManager.invokeNormalAction = function(subject, target) { var realTarget = this.applySubstitute(target); this._action.apply(realTarget); this._logWindow.displayActionResults(subject, realTarget); }; BattleManager.invokeCounterAttack = function(subject, target) { var action = new Game_Action(target); action.setAttack(); action.apply(subject); this._logWindow.displayCounter(target); this._logWindow.displayActionResults(subject, subject); }; BattleManager.invokeMagicReflection = function(subject, target) { this._logWindow.displayReflection(target); this._action.apply(subject); this._logWindow.displayActionResults(subject, subject); }; Game_Action.prototype.apply = function(target) { var result = target.result(); this.subject().clearResult(); result.clear(); result.used = this.testApply(target); result.missed = (result.used && Math.random() >= this.itemHit(target)); result.evaded = (!result.missed && Math.random() < this.itemEva(target)); result.physical = this.isPhysical(); result.drain = this.isDrain(); if (result.isHit()) { if (this.item().damage.type > 0) { result.critical = (Math.random() < this.itemCri(target)); var value = this.makeDamageValue(target, result.critical); this.executeDamage(target, value); } this.item().effects.forEach(function(effect) { this.applyItemEffect(target, effect); }, this); this.applyItemUserEffect(target); } };
결론적으로 정리해드리자면...
target 은 행동의 대상이 되는 actor이고
subject는 행동의 주체가 되는 actor라는 것입니다.
질문에 들어간 다음의 스크립트에서
$gameActors.actor(7).mat *35.8 - $gameTroop.members()[0].mdf * 2
다음의 변수를 공격의 주체인
subject 변수가 가르키게 됩니다.
$gameActors.actor(7)
다음의 변수를 공격의 대상인
target 변수가 가르기케 됩니다.
$gameTroop.members()[0]
공격의 대상인 $gameTroop.members()[0]
가
결과적으로 크리티컬 상태가 true인지
조건문으로 체크해주시면 되겠습니다.
//actor 의 데이터를 세팅하는 함수들
Game_Actor.prototype.initialize = function(actorId) {
Game_Battler.prototype.initialize.call(this);
this.setup(actorId);
};
Game_Actor.prototype.initMembers = function() {
Game_Battler.prototype.initMembers.call(this);
this._actorId = 0;
this._name = '';
this._nickname = '';
this._classId = 0;
this._level = 0;
this._characterName = '';
this._characterIndex = 0;
this._faceName = '';
this._faceIndex = 0;
this._battlerName = '';
this._exp = {};
this._skills = [];
this._equips = [];
this._actionInputIndex = 0;
this._lastMenuSkill = new Game_Item();
this._lastBattleSkill = new Game_Item();
this._lastCommandSymbol = '';
};
Game_Actor.prototype.setup = function(actorId) {
var actor = $dataActors[actorId];
this._actorId = actorId;
this._name = actor.name;
this._nickname = actor.nickname;
this._profile = actor.profile;
this._classId = actor.classId;
this._level = actor.initialLevel;
this.initImages();
this.initExp();
this.initSkills();
this.initEquips(actor.equips);
this.clearParamPlus();
this.recoverAll();
};
//._actorId는 전역변수 $dataActors에 있는 actor 데이터에
//접근하는데 쓸 수 있고, 액터간의 키 데이터가 됩니다.
//1. 현재 행동의 주체가 되는 actor의 ._actorId 와
//행동의 대상이 되는 actor의 _actorId를 사용하여
//데이터의 일치성을 검사해주시면 됩니다.
//2. 행동의 대상이 되는 actor의 .result().critical 의
//boolean값이 true인가 false인가를 체크해주시면 됩니다.
//3. 연산자는 모든 조건이 만족되어야지만 실행을 해야하기에
//&& 연산자로 모두 묶어주셔야 합니다.
BattleManager.set_target = function (actor){
BattleManager._target = actor;
};
function find_target_actorId(actor){
return BattleManager._target._actorId === actor._actorId;
};
//---------------------------------------------------------------------------------
var subject = $gameActors.actor(7);
var target;
BattleManager.set_target($gameTroop.members()[0]);
target = BattleManager._targets.find(find_target_actorId);
if(BattleManager._subject._actorId === subject._actorId
&& typeof target === "object"
&& target.result().critical){
//데미지 처리는 알아서 하실 수 있을 것이라 봅니다.
}
결과부분의 2개 함수추가는 하지 않으시면
제대로 데이터를 걸러 낼 수 없습니다.
하나는 BattleManager에서 전역변수화해서
BattleManager._target 으로 접근할 수 있도록 합니다.
다른 하나는 BattleManager._targets 배열 변수에 있는
actor 데이터에서 ._actorId 가 BattleManager._target._actorId와
같으면 해당 actor의 데이터를 target변수가 가르키게 하는 용도입니다.
ps. 혹시나 해보시고 제대로 안되시는 부분이
있다면 과감히 질문해주시기 바랍니다.
실제 게임을 실행하면서 스크립팅을 한 것이
아니라 RPG MV 시스템 스크립트를 뜯어보면서
작성한 것이라 실제 실행시에 문제가
있을 수 있으니 참고하시기 바랍니다.
TypeError
Cannot read property 'list' of undefined 라고 오류가 뜨네요.
그리고...
스크립트 적는 칸이 저렇게 작아서 한줄로 적어야 되는데 처리상의 문제는 없는건가요?