UNPKG

mdx-m3-viewer

Version:

A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.

235 lines (184 loc) 7.46 kB
import Trigger from '../../parsers/w3x/wtg/trigger'; import ECA from '../../parsers/w3x/wtg/eca'; import Parameter from '../../parsers/w3x/wtg/parameter'; import SubParameters from '../../parsers/w3x/wtg/subparameters'; import WeuData from './data'; import { createCustomScriptECA, convertSingleToMultiple, isConditionECA, ensureCustomScriptCodeSafety } from './utils'; import { convertFunctionCall } from './conversions'; import transformFunction from './transformations/functions'; import transformPreset from './transformations/presets'; export function processTrigger(data: WeuData, trigger: Trigger, callbacks: string[]) { data.push(trigger); let eventsAndConditions = []; let actions = []; for (let eca of trigger.ecas) { let type = eca.type; if (type === 0 || type === 1) { eventsAndConditions.push(eca); } else if (type === 2) { actions.push(eca); } } let outputEcas = []; for (let eventOrCondition of eventsAndConditions) { let result = processECA(data, eventOrCondition, callbacks); if (result.convert) { data.pop(); return result; } outputEcas.push(eventOrCondition); } for (let action of actions) { let result = processECA(data, action, callbacks); if (result.convert) { let customScripts = convertFunctionCall(data, action, callbacks); data.change('inlinecustomscript', result.reason, customScripts.map((eca) => eca.parameters[0].value).join('\n')); outputEcas.push(...customScripts); } else { outputEcas.push(action); } } trigger.ecas = ensureCustomScriptCodeSafety(outputEcas); data.pop(); return { convert: false, reason: '' }; } export function processECA(data: WeuData, eca: ECA, callbacks: string[]) { data.push(eca); // Test if this function call, or anything down its hierarchy, needs to be converted to custom script. let result = processFunctionCall(data, eca, callbacks); if (result.convert) { let reason = result.reason; // If conversion is needed, try first to see if this is a RoC control flow ECA, and convert it to its TFT equivalent. // This includes things like IfThenElse (RoC) and IfThenElseMultiple (TFT). // This allows to potentially only convert to custom script one part of the control flow block, rather than all of it. if (convertSingleToMultiple(eca)) { // If the test passes here (that is, false is returned), the TFT conversion allowed to handle the conversion down the hierarchy. // In this case, this ECA no longer needs to be converted to custom script. let result = processFunctionCall(data, eca, callbacks); if (result.convert) { data.pop(); return result; } else { data.change('singletomultiple', reason, eca.name); } } else { data.pop(); return result; } } let outputEcas = []; // Test the child ECAs if there are any. for (let child of eca.ecas) { let result = processECA(data, child, callbacks); if (result.convert) { let customScripts; // If this is a condition ECA, make a custom script condition. if (isConditionECA(eca.name, child.group)) { let condition = convertFunctionCall(data, child, callbacks)[0].parameters[0].value; // Normally type 2 (function) ECAs have the call keyword prepended to them. // If one was added, remove it now, since this is a condition. if (condition.startsWith('call ')) { condition = condition.slice(5); } let finalCondition; let returnValue; // IfThenElseMultiple and AndMultiple return false if any condition is false. // OrMultiple needs to return true if any condition is true. if (eca.name === 'OrMultiple') { finalCondition = condition; returnValue = 'true'; } else { finalCondition = `not (${condition})`; returnValue = 'false'; } customScripts = [ createCustomScriptECA(`if ${finalCondition} then`), createCustomScriptECA(`return ${returnValue}`), createCustomScriptECA('endif'), ]; } else { customScripts = convertFunctionCall(data, child, callbacks); } // All of the custom scripts should be in the same group as the original child. for (let script of customScripts) { script.group = child.group; } data.change('inlinecustomscript', result.reason, customScripts.map((eca) => eca.parameters[0].value).join('\n')); outputEcas.push(...customScripts); } else { outputEcas.push(child); } } eca.ecas = ensureCustomScriptCodeSafety(outputEcas); data.pop(); return { convert: false, reason: '' }; } function processFunctionCall(data: WeuData, object: ECA | SubParameters, callbacks: string[]) { let name = object.name; // Check if this object can be converted back to normal GUI. // If it's already normal GUI, nothing will happen. if (transformFunction(data, object)) { data.change('inlinegui', name, object.name); } // If this function is not from normal GUI, it has to be converted. if (!data.triggerData.isBaseFunction(object.type, object.name)) { return { convert: true, reason: object.name }; } // Check the parameters. // Note that they will also be checked if GUI was inlined. // This is needed, because the inline functions don't check the parameters, only move them around. for (let parameter of object.parameters) { // Check for custom presets. if (parameter.type === 0 && data.triggerData.isCustomPreset(parameter.value)) { let value = parameter.value; if (transformPreset(data, parameter)) { data.change('inlinepreset', value, parameter.value); } else { return { convert: true, reason: value }; } } let result = processParameter(data, parameter, callbacks); if (result.convert) { return result; } } return { convert: false, reason: '' }; } function processParameter(data: WeuData, parameter: Parameter, callbacks: string[]): { convert: boolean, reason: string } { data.push(parameter); let type = parameter.type; let value = parameter.value; if (type === 1) { if (value.startsWith('gg_')) { // Used to track global generated variables and their status. data.updateGUIReference(value, true); } } else if (type === 2) { let result = processSubParameters(data, <SubParameters>parameter.subParameters, callbacks); if (result.convert) { data.pop(); return result; } } // If this is an array element, test the array index. let index = parameter.arrayIndex; if (index) { let result = processParameter(data, index, callbacks); if (result.convert) { data.pop(); return result; } } data.pop(); return { convert: false, reason: '' }; } function processSubParameters(data: WeuData, subParameters: SubParameters, callbacks: string[]) { data.push(subParameters); let result = processFunctionCall(data, subParameters, callbacks); if (result.convert) { data.pop(); return result; } data.pop(); return { convert: false, reason: '' }; }