제대로 된 스크립트를 작성하기 위해서
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 라고 오류가 뜨네요.
그리고...
스크립트 적는 칸이 저렇게 작아서 한줄로 적어야 되는데 처리상의 문제는 없는건가요?