430 | function LicensePlate:changeCharacter(variationIndex, currentCharacters, valueIndex, direction) |
431 | local variation = self.variations[variationIndex] |
432 | if variation ~= nil then |
433 | local value = variation.values[valueIndex] |
434 | local currentCharacter = currentCharacters[value.realIndex] |
435 | |
436 | local sourceCharactersSources = {} |
437 | if value.alphabetical then |
438 | table.insert(sourceCharactersSources, self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.ALPHABETICAL]) |
439 | end |
440 | if value.numerical then |
441 | table.insert(sourceCharactersSources, self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.NUMERICAL]) |
442 | end |
443 | if value.special then |
444 | table.insert(sourceCharactersSources, self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.SPECIAL]) |
445 | end |
446 | |
447 | local newSource |
448 | local newIndex = 1 |
449 | for j=1, #sourceCharactersSources do |
450 | local source = sourceCharactersSources[j] |
451 | |
452 | for i=1, #source do |
453 | local sourceCharacter = source[i] |
454 | if sourceCharacter.value == currentCharacter then |
455 | newIndex = i + direction |
456 | if newIndex > #source then |
457 | local nextSource = sourceCharactersSources[j+1] |
458 | if nextSource ~= nil then |
459 | newIndex = 1 |
460 | newSource = nextSource |
461 | else |
462 | newIndex = #source |
463 | newSource = source |
464 | end |
465 | elseif newIndex < 1 then |
466 | local prevSource = sourceCharactersSources[j-1] |
467 | if prevSource ~= nil then |
468 | newIndex = #prevSource |
469 | newSource = prevSource |
470 | else |
471 | newIndex = 1 |
472 | newSource = source |
473 | end |
474 | else |
475 | newSource = source |
476 | end |
477 | end |
478 | end |
479 | end |
480 | |
481 | if newSource ~= nil then |
482 | if newSource[newIndex] ~= nil then |
483 | currentCharacters[value.realIndex] = newSource[newIndex].value |
484 | end |
485 | end |
486 | end |
487 | |
488 | return currentCharacters |
489 | end |
143 | function LicensePlate:clone(includeFrame) |
144 | local licensePlateClone = LicensePlate.new() |
145 | |
146 | licensePlateClone.node = clone(self.node, false, false, false) |
147 | |
148 | licensePlateClone.type = self.type |
149 | licensePlateClone.filename = self.filename |
150 | licensePlateClone.width = self.width |
151 | licensePlateClone.height = self.height |
152 | licensePlateClone.fontSize = self.fontSize |
153 | licensePlateClone.fontScaleX = self.fontScaleX |
154 | licensePlateClone.fontScaleY = self.fontScaleY |
155 | |
156 | licensePlateClone.rawWidth = self.width |
157 | licensePlateClone.rawHeight = self.height |
158 | |
159 | licensePlateClone.widthOffsetLeft = 0 |
160 | licensePlateClone.widthOffsetRight = 0 |
161 | licensePlateClone.heightOffsetTop = 0 |
162 | licensePlateClone.heightOffsetBot = 0 |
163 | |
164 | licensePlateClone.font = self.font |
165 | licensePlateClone.fontMaxWidthRatio = self.fontMaxWidthRatio |
166 | |
167 | licensePlateClone.variations = table.copy(self.variations, math.huge) |
168 | for i=1, #licensePlateClone.variations do |
169 | local variation = licensePlateClone.variations[i] |
170 | for j=1, #variation.values do |
171 | local value = variation.values[j] |
172 | value.node = I3DUtil.indexToObject(licensePlateClone.node, value.nodePath) |
173 | end |
174 | end |
175 | |
176 | licensePlateClone.frame = table.copy(self.frame, math.huge) |
177 | if licensePlateClone.frame ~= nil then |
178 | includeFrame = includeFrame == true |
179 | local frameNode = I3DUtil.indexToObject(licensePlateClone.node, licensePlateClone.frame.node) |
180 | if frameNode ~= nil then |
181 | setVisibility(frameNode, includeFrame) |
182 | |
183 | if includeFrame then |
184 | licensePlateClone.width = licensePlateClone.width + 2 * licensePlateClone.frame.widthOffset |
185 | licensePlateClone.height = licensePlateClone.height + licensePlateClone.frame.heightOffsetTop + licensePlateClone.frame.heightOffsetBot |
186 | |
187 | licensePlateClone.widthOffsetLeft = licensePlateClone.frame.widthOffset |
188 | licensePlateClone.widthOffsetRight = licensePlateClone.frame.widthOffset |
189 | licensePlateClone.heightOffsetTop = licensePlateClone.frame.heightOffsetTop |
190 | licensePlateClone.heightOffsetBot = licensePlateClone.frame.heightOffsetBot |
191 | end |
192 | end |
193 | end |
194 | |
195 | licensePlateClone:setVariation(self.variationIndex) |
196 | |
197 | return licensePlateClone |
198 | end |
326 | function LicensePlate:getRandomCharacters(variationIndex) |
327 | local characters = {} |
328 | |
329 | local firstNumericCharacter = true |
330 | local variation = self.variations[variationIndex] |
331 | if variation ~= nil then |
332 | for i=1, #variation.values do |
333 | local value = variation.values[i] |
334 | |
335 | if not value.isStatic then |
336 | if value.character == nil then |
337 | local random = math.random() |
338 | |
339 | local sourceCharacters = self.font.characters |
340 | if value.alphabetical then |
341 | sourceCharacters = self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.ALPHABETICAL] |
342 | elseif value.numerical then |
343 | sourceCharacters = self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.NUMERICAL] |
344 | elseif value.special then |
345 | sourceCharacters = self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.SPECIAL] |
346 | end |
347 | |
348 | if sourceCharacters ~= nil and #sourceCharacters > 0 then |
349 | local index = math.max(math.floor(random * #sourceCharacters), 1) |
350 | |
351 | -- special case for numbers -> never start with leading zero |
352 | if value.numerical and firstNumericCharacter then |
353 | if index == 1 and #sourceCharacters > 1 then |
354 | index = 2 |
355 | end |
356 | firstNumericCharacter = false |
357 | end |
358 | |
359 | table.insert(characters, sourceCharacters[index].value) |
360 | else |
361 | table.insert(characters, "0") |
362 | end |
363 | else |
364 | table.insert(characters, value.character) |
365 | end |
366 | end |
367 | end |
368 | end |
369 | |
370 | return characters |
371 | end |
30 | function LicensePlate:loadFromXML(node, filename, customEnvironment, xmlFile, key) |
31 | local typeStr = xmlFile:getValue(key .. "#type", "ELONGATED") |
32 | if typeStr ~= nil then |
33 | self.type = LicensePlateManager.PLATE_TYPE[typeStr] |
34 | if self.type ~= nil then |
35 | self.node = node |
36 | self.filename = filename |
37 | self.width = xmlFile:getValue(key .. "#width", 1) |
38 | self.height = xmlFile:getValue(key .. "#height", 0.3) |
39 | self.fontSize = xmlFile:getValue(key .. ".font#size", 0.1) |
40 | self.fontScaleX = xmlFile:getValue(key .. ".font#scaleX", 1) |
41 | self.fontScaleY = xmlFile:getValue(key .. ".font#scaleY", 1) |
42 | |
43 | self.widthOffsetLeft = 0 |
44 | self.widthOffsetRight = 0 |
45 | self.heightOffsetTop = 0 |
46 | self.heightOffsetBot = 0 |
47 | |
48 | self.font = g_licensePlateManager:getFont() |
49 | |
50 | if self.font == nil then |
51 | Logging.error("LicensePlate: Unable to get font from LicensePlateManager, possibly not initialized/loaded yet") |
52 | printCallstack() |
53 | return false |
54 | end |
55 | self.fontMaxWidthRatio = self.font:getFontMaxWidthRatio() |
56 | |
57 | self.variations = {} |
58 | xmlFile:iterate(key .. ".variations.variation", function(_, variationKey) |
59 | local variation = {} |
60 | variation.values = {} |
61 | |
62 | local realIndex = 0 |
63 | xmlFile:iterate(variationKey .. ".value", function(index, valueKey) |
64 | local value = {} |
65 | value.index = index |
66 | value.realIndex = realIndex + 1 |
67 | value.nodePath = xmlFile:getValue(valueKey .. "#node") |
68 | value.node = I3DUtil.indexToObject(self.node, value.nodePath) |
69 | value.nextSection = xmlFile:getValue(valueKey .. "#nextSection", false) |
70 | if value.nodePath ~= nil and value.node ~= nil then |
71 | local x, y, z = getTranslation(value.node) |
72 | value.posX = xmlFile:getValue(valueKey .. "#posX", x) |
73 | value.posY = xmlFile:getValue(valueKey .. "#posY", y) |
74 | value.posZ = xmlFile:getValue(valueKey .. "#posZ", z) |
75 | |
76 | value.character = xmlFile:getValue(valueKey .. "#character") |
77 | value.numerical = xmlFile:getValue(valueKey .. "#numerical", false) |
78 | value.alphabetical = xmlFile:getValue(valueKey .. "#alphabetical", false) |
79 | value.special = xmlFile:getValue(valueKey .. "#special", false) |
80 | value.isStatic = xmlFile:getValue(valueKey .. "#isStatic", not value.numerical and not value.alphabetical and not value.special and value.character == nil) |
81 | value.locked = xmlFile:getValue(valueKey .. "#locked", value.character ~= nil) |
82 | |
83 | value.maxWidthRatio = self.font:getFontMaxWidthRatio(value.alphabetical, value.numerical, value.special) |
84 | |
85 | local positionStr = xmlFile:getValue(valueKey .. "#position", "ANY") |
86 | local position = LicensePlateManager.PLATE_POSITION[positionStr:upper()] |
87 | if position == nil then |
88 | Logging.xmlError(xmlFile, "Unknown position '%s' in '%s'", positionStr, valueKey) |
89 | end |
90 | value.position = position or LicensePlateManager.PLATE_POSITION.ANY |
91 | |
92 | if not value.isStatic then |
93 | realIndex = realIndex + 1 |
94 | end |
95 | |
96 | table.insert(variation.values, value) |
97 | end |
98 | end) |
99 | |
100 | variation.colors = {} |
101 | |
102 | xmlFile:iterate(variationKey .. ".color", function(_, colorKey) |
103 | local color = {} |
104 | color.shaderParameter = xmlFile:getValue(colorKey .. "#shaderParameter") |
105 | color.color = xmlFile:getValue(colorKey .. "#color", nil, true) |
106 | if color.shaderParameter ~= nil and color.color ~= nil then |
107 | table.insert(variation.colors, color) |
108 | end |
109 | |
110 | end) |
111 | |
112 | if #variation.values > 0 then |
113 | table.insert(self.variations, variation) |
114 | end |
115 | end) |
116 | |
117 | self.frame = {} |
118 | self.frame.node = xmlFile:getValue(key .. ".frame#node") |
119 | self.frame.widthOffset = xmlFile:getValue(key .. ".frame#widthOffset", 0) |
120 | self.frame.heightOffsetTop = xmlFile:getValue(key .. ".frame#heightOffsetTop", 0) |
121 | self.frame.heightOffsetBot = xmlFile:getValue(key .. ".frame#heightOffsetBot", 0) |
122 | |
123 | if self.frame.node == nil or I3DUtil.indexToObject(self.node, self.frame.node) == nil then |
124 | self.frame = nil |
125 | end |
126 | else |
127 | return false |
128 | end |
129 | end |
130 | |
131 | return true |
132 | end |
521 | function LicensePlate.registerXMLPaths(schema, baseName) |
522 | schema:register(XMLValueType.STRING, baseName .. "#filename", "License plate i3d filename") |
523 | schema:register(XMLValueType.NODE_INDEX, baseName .. "#node", "License plate node") |
524 | schema:register(XMLValueType.STRING, baseName .. "#type", "License plate type 'SQUARISH' or 'ELONGATED'", "ELONGATED") |
525 | |
526 | schema:register(XMLValueType.FLOAT, baseName .. "#width", "Width of license plate", 1) |
527 | schema:register(XMLValueType.FLOAT, baseName .. "#height", "Height of license plate", 0.2) |
528 | |
529 | schema:register(XMLValueType.FLOAT, baseName .. ".font#size", "Size of font", 0.1) |
530 | schema:register(XMLValueType.FLOAT, baseName .. ".font#scaleX", "Additional scaling of font X", 1) |
531 | schema:register(XMLValueType.FLOAT, baseName .. ".font#scaleY", "Additional scaling of font Y", 1) |
532 | |
533 | schema:register(XMLValueType.STRING, baseName .. ".variations.variation(?).value(?)#node", "Value mesh index") |
534 | schema:register(XMLValueType.FLOAT, baseName .. ".variations.variation(?).value(?)#posX", "X translation of value node") |
535 | schema:register(XMLValueType.FLOAT, baseName .. ".variations.variation(?).value(?)#posY", "Y translation of value node") |
536 | schema:register(XMLValueType.FLOAT, baseName .. ".variations.variation(?).value(?)#posZ", "Z translation of value node") |
537 | schema:register(XMLValueType.BOOL, baseName .. ".variations.variation(?).value(?)#nextSection", "Is start character for next section", false) |
538 | schema:register(XMLValueType.STRING, baseName .. ".variations.variation(?).value(?)#character", "Pre defined character of node") |
539 | schema:register(XMLValueType.BOOL, baseName .. ".variations.variation(?).value(?)#numerical", "Node supports numeric characters", false) |
540 | schema:register(XMLValueType.BOOL, baseName .. ".variations.variation(?).value(?)#alphabetical", "Node supports alphabetical characters", false) |
541 | schema:register(XMLValueType.BOOL, baseName .. ".variations.variation(?).value(?)#special", "Node supports special characters", false) |
542 | schema:register(XMLValueType.BOOL, baseName .. ".variations.variation(?).value(?)#isStatic", "Node is only static without applying of characters", "is static if 'numerical' and 'alphabetical' are both on false and no fixed character is given") |
543 | schema:register(XMLValueType.BOOL, baseName .. ".variations.variation(?).value(?)#locked", "Character value can not be changed", "locked when character is defined") |
544 | schema:register(XMLValueType.STRING, baseName .. ".variations.variation(?).value(?)#position", "Value will be hidden of position differs from placement position", "ANY") |
545 | |
546 | schema:register(XMLValueType.STRING, baseName .. ".variations.variation(?).color(?)#shaderParameter", "Shader parameter to set") |
547 | schema:register(XMLValueType.COLOR, baseName .. ".variations.variation(?).color(?)#color", "Color to apply on shader parameter") |
548 | |
549 | schema:register(XMLValueType.STRING, baseName .. ".frame#node", "Frame node that can be toggled") |
550 | schema:register(XMLValueType.FLOAT, baseName .. ".frame#widthOffset", "Width of frame on each side", 0) |
551 | schema:register(XMLValueType.FLOAT, baseName .. ".frame#heightOffsetTop", "Height of frame on top", 0) |
552 | schema:register(XMLValueType.FLOAT, baseName .. ".frame#heightOffsetBot", "Height of frame at bottom", 0) |
553 | end |
247 | function LicensePlate:setVariation(variationIndex) |
248 | local variation = self.variations[variationIndex] |
249 | if variation ~= nil then |
250 | local oldVariation = self.variations[self.variationIndex] |
251 | for i=1, #oldVariation.values do |
252 | local value = oldVariation.values[i] |
253 | if not value.isStatic then |
254 | for j=1, getNumOfChildren(value.node) do |
255 | delete(getChildAt(value.node, j - 1)) |
256 | end |
257 | end |
258 | end |
259 | |
260 | self.variationIndex = variationIndex |
261 | |
262 | for i=1, #variation.values do |
263 | local value = variation.values[i] |
264 | |
265 | if not value.isStatic then |
266 | value.singleCharacter = self.font:createSingleCharacter(value.node, self.fontSize, {0, 0, 0, 1}, nil, 0, self.fontScaleX, self.fontScaleY, 0) |
267 | end |
268 | end |
269 | |
270 | for i=1, #variation.colors do |
271 | local color = variation.colors[i] |
272 | I3DUtil.setShaderParameterRec(self.node, color.shaderParameter, color.color[1], color.color[2], color.color[3]) |
273 | end |
274 | end |
275 | end |
202 | function LicensePlate:updateData(variationIndex, position, characters, validate) |
203 | if variationIndex ~= self.variationIndex then |
204 | self:setVariation(variationIndex) |
205 | end |
206 | |
207 | if validate == true then |
208 | characters = self:validateLicensePlateCharacters(characters) |
209 | end |
210 | |
211 | self.characters = characters |
212 | |
213 | local variation = self.variations[self.variationIndex] |
214 | if variation ~= nil then |
215 | local stringPos = 1 |
216 | for i=1, #variation.values do |
217 | local value = variation.values[i] |
218 | if value.node ~= nil then |
219 | setTranslation(value.node, value.posX, value.posY, value.posZ) |
220 | |
221 | local samePosition = (value.position == LicensePlateManager.PLATE_POSITION.ANY or value.position == position) and value.position ~= LicensePlateManager.PLATE_POSITION.NONE |
222 | |
223 | local targetChar = value.character |
224 | if not value.locked then |
225 | targetChar = characters:sub(stringPos, stringPos) or targetChar |
226 | end |
227 | |
228 | if not value.isStatic and targetChar ~= "" and targetChar ~= "_" and samePosition then |
229 | self.font:updateSingleCharacter(value.singleCharacter, targetChar) |
230 | setVisibility(value.node, true) |
231 | elseif value.isStatic and samePosition then |
232 | setVisibility(value.node, true) |
233 | else |
234 | setVisibility(value.node, false) |
235 | end |
236 | |
237 | if not value.isStatic then |
238 | stringPos = stringPos + 1 |
239 | end |
240 | end |
241 | end |
242 | end |
243 | end |
377 | function LicensePlate:validateLicensePlateCharacters(characters) |
378 | local str = characters |
379 | local isTbl = type(characters) == "table" |
380 | local length |
381 | if isTbl then |
382 | str = table.concat(characters, "") |
383 | length = #characters |
384 | else |
385 | length = characters:len() |
386 | end |
387 | |
388 | local variation = self.variations[self.variationIndex] |
389 | |
390 | str = filterText(str, false, false) |
391 | for i=1, length do |
392 | local replacement = str:sub(i, i) |
393 | local old |
394 | if isTbl then |
395 | old = characters[i] |
396 | else |
397 | old = characters:sub(i, i) |
398 | end |
399 | |
400 | if replacement ~= old then |
401 | local value = variation.values[i] |
402 | if value ~= nil then |
403 | local sourceCharacters = self.font.characters |
404 | if value.alphabetical then |
405 | sourceCharacters = self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.ALPHABETICAL] |
406 | elseif value.numerical then |
407 | sourceCharacters = self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.NUMERICAL] |
408 | elseif value.special then |
409 | sourceCharacters = self.font.charactersByType[MaterialManager.FONT_CHARACTER_TYPE.SPECIAL] |
410 | end |
411 | |
412 | if isTbl then |
413 | characters[i] = sourceCharacters[1].value |
414 | else |
415 | characters = str:sub(1, i - 1) .. sourceCharacters[1].value .. str:sub(i + 1) |
416 | end |
417 | end |
418 | end |
419 | end |
420 | |
421 | return characters |
422 | end |