Ticket #13: diffFormation-01-05-2011.patch
File diffFormation-01-05-2011.patch, 27.8 KB (added by , 13 years ago) |
---|
-
binaries/data/mods/public/gui/session/utility_functions.js
223 223 { 224 224 switch (formationName) 225 225 { 226 case " Formation0":226 case "Loose": 227 227 return 0; 228 case " Formation1":228 case "Box": 229 229 return 1; 230 case " Formation2":230 case "Column Closed": 231 231 return 2; 232 case " Formation3":232 case "Line Closed": 233 233 return 3; 234 case " Formation4":234 case "Column Open": 235 235 return 4; 236 case " Formation5":236 case "Line Open": 237 237 return 5; 238 case "F ormation6":238 case "Flank": 239 239 return 6; 240 case " Formation7":240 case "Skirmish": 241 241 return 7; 242 case " Formation8":242 case "Wedge": 243 243 return 8; 244 case " Formation9":244 case "Testudo": 245 245 return 9; 246 case " Formation10":247 return 10; 248 case " Formation11":246 case "Phalanx": 247 return 10; 248 case "Syntagma": 249 249 return 11; 250 250 case "Formation12": 251 251 return 12; … … 273 273 274 274 function getEntityFormationsList(entState) 275 275 { 276 var formations = []; 277 278 formations.push("Formation0"); 279 formations.push("Formation1"); 280 formations.push("Formation2"); 281 formations.push("Formation3"); 282 formations.push("Formation4"); 283 formations.push("Formation5"); 284 formations.push("Formation6"); 285 formations.push("Formation7"); 286 formations.push("Formation8"); 287 formations.push("Formation9"); 288 formations.push("Formation10"); 289 formations.push("Formation11"); 290 formations.push("Formation12"); 276 var civ = g_Players[entState.player].civ; 277 var formations = getCivFormations(civ); 291 278 return formations; 292 279 } 293 280 281 function getCivFormations(civ) 282 { 283 var civFormations = ["Loose","Box","Column Closed","Line Closed","Column Open","Line Open","Flank","Skirmish","Wedge","Formation12"]; 284 if (civ == "hele") 285 { 286 civFormations.push("Phalanx"); 287 civFormations.push("Syntagma"); 288 } 289 else if (civ == "rome") 290 { 291 civFormations.push("Testudo"); 292 } 293 return civFormations; 294 } 295 294 296 function getEntityCommandsList(entState) 295 297 { 296 298 var commands = []; -
binaries/data/mods/public/gui/session/input.js
1090 1090 // Performs the specified formation 1091 1091 function performFormation(entity, formationName) 1092 1092 { 1093 submitChatDirectly("FORMATIONS are not implemented yet.");1094 1095 1093 if (entity) 1096 1094 { 1097 console.write(formationName); 1095 var selection = g_Selection.toList(); 1096 Engine.PostNetworkCommand({ 1097 "type": "formation", 1098 "entities": selection, 1099 "name": formationName 1100 }); 1098 1101 } 1099 1102 } 1100 1103 -
binaries/data/mods/public/gui/session/unit_commands.js
248 248 if (guiName == "Formation") 249 249 { 250 250 icon.cell_id = getFormationCellId(item); 251 icon.enabled = false; 251 //var formationOk = CanMoveEntsIntoFormation(g_Selection.toList(),item); 252 var formationOk = Engine.GuiInterfaceCall("CanMoveEntsIntoFormation", { 253 "ents": g_Selection.toList(), 254 "formationName": item 255 }); 256 257 icon.enabled = formationOk; 258 button.enabled = formationOk; 252 259 if (!icon.enabled) 260 { 253 261 icon.sprite = "formation_disabled"; 262 button.tooltip += " (disabled)"; 263 } 264 else 265 { 266 icon.sprite = "formation"; 267 } 254 268 } 255 269 else if (guiName == "Command") 256 270 { … … 355 369 setupUnitPanel("Formation", usedPanels, entState, formations, 356 370 function (item) { performFormation(entState.id, item); } ); 357 371 372 358 373 if (entState.buildEntities && entState.buildEntities.length) 359 374 { 360 375 setupUnitPanel("Construction", usedPanels, entState, entState.buildEntities, startBuildingPlacement); … … 399 414 { 400 415 for each (var panelName in g_unitPanels) 401 416 getGUIObjectByName("unit" + panelName + "Panel").hidden = true; 402 } 403 No newline at end of file 417 } -
binaries/data/mods/public/simulation/helpers/Commands.js
236 236 237 237 var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder); 238 238 cmpGarrisonHolder.UnloadAll(); 239 break; 240 241 case "formation": 242 var cmpUnitAI = GetFormationUnitAI(cmd.entities); 243 if(!cmpUnitAI) 244 break; 245 var cmpFormation = Engine.QueryInterface(cmpUnitAI.entity, IID_Formation); 246 if (!cmpFormation) 247 break; 248 cmpFormation.LoadFormation(cmd.name); 249 cmpFormation.MoveMembersIntoFormation(true); 239 250 break; 240 251 241 252 default: … … 339 350 formationEnt = Engine.AddEntity("special/formation"); 340 351 var cmpFormation = Engine.QueryInterface(formationEnt, IID_Formation); 341 352 cmpFormation.SetMembers(formation.entities); 353 354 // We retrieve the last common formation name 355 var lastFormationName = undefined; 356 for each (var ent in formation.entities) 357 { 358 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 359 if (cmpUnitAI) 360 { 361 var name = cmpUnitAI.GetLastFormationName(); 362 if (lastFormationName == undefined) 363 { 364 lastFormationName = name; 365 } 366 else if (lastFormationName != name) 367 { 368 lastFormationName = undefined; 369 break; 370 } 371 } 372 } 373 var formationName; 374 if (lastFormationName) 375 formationName = lastFormationName; 376 else 377 formationName = "Line Closed"; 378 379 if (CanMoveEntsIntoFormation(formation.entities,formationName)) 380 { 381 cmpFormation.LoadFormation(formationName); 382 } 383 else 384 { 385 cmpFormation.LoadFormation("Loose"); 386 } 342 387 } 343 388 344 389 return Engine.QueryInterface(formationEnt, IID_UnitAI); 345 390 } 346 391 392 function CanMoveEntsIntoFormation(ents,formationName) 393 { 394 var count = ents.length; 395 var classesRequired; 396 397 if (formationName == "Loose") 398 { 399 return true; 400 } 401 else if (formationName == "Box") 402 { 403 if (count < 4) 404 return false; 405 } 406 else if (formationName == "Column Closed") 407 { 408 } 409 else if (formationName == "Line Closed") 410 { 411 } 412 else if (formationName == "Column Open") 413 { 414 } 415 else if (formationName == "Line Open") 416 { 417 } 418 else if (formationName == "Flank") 419 { 420 if (count < 8) 421 return false; 422 } 423 else if (formationName == "Skirmish") 424 { 425 classesRequired = ["Ranged"]; 426 } 427 else if (formationName == "Wedge") 428 { 429 if (count < 3) 430 return false; 431 classesRequired = ["Cavalry"]; 432 } 433 else if (formationName == "Formation12") 434 { 435 } 436 else if (formationName == "Phalanx") 437 { 438 if (count < 10) 439 return false; 440 classesRequired = ["Melee","Infantry"]; 441 } 442 else if (formationName == "Syntagma") 443 { 444 if (count < 9) 445 return false; 446 classesRequired = ["Melee","Infantry"]; //todo: pike only 447 } 448 else if (formationName == "Testudo") 449 { 450 if (count < 9) 451 return false; 452 classesRequired = ["Melee","Infantry"]; 453 } 454 else 455 { 456 return false; 457 } 458 459 var looseOnlyUnits = true; 460 for each (var ent in ents) 461 { 462 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); 463 if (cmpIdentity) 464 { 465 //var classes = GetEntityState(ent).identity.classes; 466 var classes = cmpIdentity.GetClassesList(); 467 if (looseOnlyUnits && (classes.indexOf("Worker") == -1 || classes.indexOf("Support") == -1)) 468 looseOnlyUnits = false; 469 for each (var classRequired in classesRequired) 470 { 471 if (classes.indexOf(classRequired) == -1) 472 { 473 return false; 474 } 475 } 476 } 477 } 478 479 if (looseOnlyUnits) 480 return false; 481 482 return true; 483 } 484 485 Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation); 347 486 Engine.RegisterGlobal("ProcessCommand", ProcessCommand); -
binaries/data/mods/public/simulation/components/GuiInterface.js
264 264 return ""; 265 265 }; 266 266 267 GuiInterface.prototype.CanMoveEntsIntoFormation = function(player, data) 268 { 269 return CanMoveEntsIntoFormation(data.ents, data.formationName); 270 } 271 267 272 GuiInterface.prototype.SetSelectionHighlight = function(player, cmd) 268 273 { 269 274 var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); … … 510 515 "GetTemplateData": 1, 511 516 "GetNextNotification": 1, 512 517 518 "CanMoveEntsIntoFormation":1, 519 513 520 "SetSelectionHighlight": 1, 514 521 "SetStatusBars": 1, 515 522 "DisplayRallyPoint": 1, -
binaries/data/mods/public/simulation/components/UnitAI.js
1161 1161 this.order = undefined; // always == this.orderQueue[0] 1162 1162 this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to 1163 1163 this.isIdle = false; 1164 this.lastFormationName = "Line Closed"; 1164 1165 1165 1166 this.SetStance(this.template.DefaultStance); 1166 1167 }; … … 1740 1741 return this.formationController; 1741 1742 }; 1742 1743 1744 UnitAI.prototype.SetLastFormationName = function(name) 1745 { 1746 this.lastFormationName = name; 1747 }; 1748 1749 UnitAI.prototype.GetLastFormationName = function() 1750 { 1751 return this.lastFormationName; 1752 }; 1753 1743 1754 /** 1744 1755 * Returns the estimated distance that this unit will travel before either 1745 1756 * finishing all of its orders, or reaching a non-walk target (attack, gather, etc). -
binaries/data/mods/public/simulation/components/Identity.js
68 68 "<value>Infantry</value>" + 69 69 "<value>Cavalry</value>" + 70 70 "<value>Ranged</value>" + 71 "<value>Melee</value>" + 71 72 "<value>Mechanical</value>" + 72 73 "<value>Ship</value>" + 73 74 "<value>Siege</value>" + -
binaries/data/mods/public/simulation/components/Formation.js
3 3 Formation.prototype.Schema = 4 4 "<a:component type='system'/><empty/>"; 5 5 6 var g_ColumnDistanceThreshold = 48; // distance at which we'll switch between column/box formations6 var g_ColumnDistanceThreshold = 96; // distance at which we'll switch between column/box formations 7 7 8 8 Formation.prototype.Init = function() 9 9 { 10 10 this.members = []; // entity IDs currently belonging to this formation 11 11 this.columnar = false; // whether we're travelling in column (vs box) formation 12 this.formationName = "Line Closed"; 12 13 }; 13 14 14 15 Formation.prototype.GetMemberCount = function() … … 114 115 var active = []; 115 116 var positions = []; 116 117 117 var types = { "Unknown": 0 }; // TODO: make this work so we put ranged behind infantry etc118 119 118 for each (var ent in this.members) 120 119 { 121 120 var cmpPosition = Engine.QueryInterface(ent, IID_Position); … … 124 123 125 124 active.push(ent); 126 125 positions.push(cmpPosition.GetPosition()); 127 128 types["Unknown"] += 1; // TODO129 126 } 130 127 131 128 // Work out whether this should be a column or box formation 132 129 var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); 133 130 var walkingDistance = cmpUnitAI.ComputeWalkingDistance(); 134 this.columnar = (walkingDistance > g_ColumnDistanceThreshold) ;131 this.columnar = (walkingDistance > g_ColumnDistanceThreshold) && this.formationName != "Column Open"; 135 132 136 var offsets = this.ComputeFormationOffsets( types, this.columnar);133 var offsets = this.ComputeFormationOffsets(active, this.columnar); 137 134 138 135 var avgpos = this.ComputeAveragePosition(positions); 139 136 var avgoffset = this.ComputeAveragePosition(offsets); … … 143 140 if (moveCenter || !cmpPosition.IsInWorld()) 144 141 cmpPosition.JumpTo(avgpos.x, avgpos.z); 145 142 146 // TODO: assign to minimise worst-case distances or whatever 147 148 for (var i = 0; i < active.length; ++i) 143 for (var i = 0; i < offsets.length; ++i) 149 144 { 150 145 var offset = offsets[i]; 151 146 152 var cmpUnitAI = Engine.QueryInterface( active[i], IID_UnitAI);147 var cmpUnitAI = Engine.QueryInterface(offset.ent, IID_UnitAI); 153 148 cmpUnitAI.ReplaceOrder("FormationWalk", { 154 149 "target": this.entity, 155 150 "x": offset.x - avgoffset.x, … … 177 172 cmpPosition.JumpTo(avgpos.x, avgpos.z); 178 173 }; 179 174 180 Formation.prototype.ComputeFormationOffsets = function( types, columnar)175 Formation.prototype.ComputeFormationOffsets = function(active, columnar) 181 176 { 182 177 var separation = 4; // TODO: don't hardcode this 183 178 184 var count = types["Unknown"]; 179 var types = { "Cavalry" : [], 180 "Hero" : [], 181 "Melee" : [], 182 "Ranged" : [], 183 "Support" : [], 184 "Unknown": [] 185 }; // TODO: make this work so we put ranged behind infantry etc 185 186 187 for each (var ent in active) 188 { 189 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); 190 var classes = cmpIdentity.GetClassesList(); 191 var done = false; 192 for each (var cla in classes) 193 { 194 if(cla in types) 195 { 196 types[cla].push(ent); 197 done = true; 198 break; 199 } 200 } 201 if (!done) 202 { 203 types["Unknown"].push(ent); 204 } 205 } 206 207 var count = active.length; 208 209 var shape = undefined; 210 var ordering = "default"; 211 212 var offsets = []; 213 186 214 // Choose a sensible width for the basic default formation 187 215 var cols; 188 if (columnar )216 if (columnar || this.formationName == "Column Closed") 189 217 { 190 218 // Have at most 3 files 191 219 if (count <= 3) 192 220 cols = count; 193 221 else 194 222 cols = 3; 223 shape = "square"; 195 224 } 196 else 225 else if (this.formationName == "Phalanx") 197 226 { 198 227 // Try to have at least 5 files (so batch training gives a single line), 199 228 // and at most 8 200 229 if (count <= 5) 201 230 cols = count; 202 if (count <= 10)231 else if (count <= 10) 203 232 cols = 5; 204 233 else if (count <= 16) 205 234 cols = Math.ceil(count / 2); 235 else if(count <= 48) 236 cols = 8; 206 237 else 238 cols = Math.ceil(count/6); 239 shape = "square"; 240 } 241 else if (this.formationName == "Line Closed") 242 { 243 if (count <= 3) 244 cols = count; 245 else if(count < 30) 246 cols = Math.max(Math.ceil(count/2),3); 247 else 248 cols = Math.ceil(count/3); 249 shape = "square"; 250 } 251 else if (this.formationName == "Testudo") 252 { 253 cols = Math.ceil(Math.sqrt(count)); 254 shape = "square"; 255 } 256 else if (this.formationName == "Column Open") 257 { 258 cols = 2 259 shape = "opensquare"; 260 } 261 else if (this.formationName == "Line Open") 262 { 263 if (count <= 5) 264 cols = 3; 265 else if (count <= 11) 266 cols = 4; 267 else if (count <= 18) 268 cols = 5; 269 else 270 cols = 6; 271 shape = "opensquare"; 272 } 273 else if (this.formationName == "Loose") 274 { 275 var width = Math.sqrt(count) * separation * 5; 276 277 for (var i = 0; i < count; ++i) 278 { 279 offsets.push({"x": Math.random()*width, "z": Math.random()*width}); 280 } 281 } 282 else if (this.formationName == "Circle") 283 { 284 var depth; 285 var pop; 286 if (count <= 36) 287 { 288 pop = 12; 289 depth = Math.ceil(count / pop); 290 } 291 else 292 { 293 depth = 3 294 pop = Math.ceil(count / depth); 295 } 296 297 var left = count; 298 var radius = Math.min(left,pop) * separation / (2 * Math.PI); 299 for (var c = 0; c < depth; ++c) 300 { 301 var ctodo = Math.min(left,pop); 302 var cradius = radius - c * separation / 2; 303 var delta = 2 * Math.PI / ctodo; 304 for (var alpha = 0; ctodo; alpha+=delta) 305 { 306 var x = Math.cos(alpha) * cradius; 307 var z = Math.sin(alpha) * cradius; 308 offsets.push({"x": x, "z": z}); 309 ctodo--; 310 left--; 311 } 312 } 313 } 314 else if (this.formationName == "Box") // Box 315 { 316 var root = Math.ceil(Math.sqrt(count)); 317 318 var left = count; 319 var meleeleft = types["Melee"].length; 320 for (var sq = Math.floor(root/2); sq >= 0; --sq) 321 { 322 var width = sq * 2 + 1; 323 var stodo; 324 if (sq == 0) 325 { 326 stodo = left; 327 } 328 else 329 { 330 if (meleeleft >= width*width - (width-2)*(width-2)) // form a complete box 331 { 332 stodo = width*width - (width-2)*(width-2); 333 meleeleft -= stodo; 334 } 335 else // compact 336 stodo = Math.max(0,left - (width-2)*(width-2)); 337 } 338 339 for (var r = -sq; r <= sq && stodo; ++r) 340 { 341 for (var c = -sq; c <= sq && stodo; ++c) 342 { 343 if (Math.abs(r) == sq || Math.abs(c) == sq) 344 { 345 var x = c * separation; 346 var z = -r * separation; 347 offsets.push({"x": x, "z": z}); 348 stodo--; 349 left--; 350 } 351 } 352 } 353 } 354 } 355 else if (this.formationName == "Skirmish") 356 { 357 cols = Math.ceil(count/2); 358 shape = "opensquare"; 359 } 360 else if (this.formationName == "Wedge") 361 { 362 var depth = Math.ceil(Math.sqrt(count)); 363 364 var left = count; 365 var width = 2 * depth - 1; 366 for (var p = 0; p < depth && left; ++p) 367 { 368 for (var r = p; r < depth && left; ++r) 369 { 370 var c1 = depth - r + p; 371 var c2 = depth + r - p; 372 373 if (left) 374 { 375 var x = c1 * separation; 376 var z = -r * separation; 377 offsets.push({"x": x, "z": z}); 378 left--; 379 } 380 if (left && c1 != c2) 381 { 382 var x = c2 * separation; 383 var z = -r * separation; 384 offsets.push({"x": x, "z": z}); 385 left--; 386 } 387 } 388 } 389 } 390 else if (this.formationName == "Flank") 391 { 392 cols = 3; 393 var leftside = []; 394 leftside[0] = Math.ceil(count/2); 395 leftside[1] = Math.floor(count/2); 396 ranks = Math.ceil(leftside[0] / cols); 397 var off = - separation * 4; 398 for (var side = 0; side < 2; ++side) 399 { 400 var left = leftside[side]; 401 off += side * separation * 8; 402 for (var r = 0; r < ranks; ++r) 403 { 404 var n = Math.min(left, cols); 405 for (var c = 0; c < n; ++c) 406 { 407 var x = off + ((n-1)/2 - c) * separation; 408 var z = -r * separation; 409 offsets.push({"x": x, "z": z}); 410 } 411 left -= n; 412 } 413 } 414 } 415 else if (this.formationName == "Syntagma") 416 { 417 var cols = Math.ceil(Math.sqrt(count)); 418 shape = "square"; 419 } 420 else if (this.formationName == "Formation12") 421 { 422 if (count <= 5) 423 cols = count; 424 else if (count <= 10) 425 cols = 5; 426 else if (count <= 16) 427 cols = Math.ceil(count / 2); 428 else if(count <= 48) 207 429 cols = 8; 430 else 431 cols = Math.ceil(count/6); 432 shape = "opensquare"; 433 separation /= 1.5; 434 ordering = "cavalryOnTheSides"; 208 435 } 209 436 210 var ranks = Math.ceil(count / cols); 437 if (shape == "square") 438 { 439 var ranks = Math.ceil(count / cols); 211 440 212 var offsets = []; 441 var left = count; 442 for (var r = 0; r < ranks; ++r) 443 { 444 var n = Math.min(left, cols); 445 for (var c = 0; c < n; ++c) 446 { 447 var x = ((n-1)/2 - c) * separation; 448 var z = -r * separation; 449 offsets.push({"x": x, "z": z}); 450 } 451 left -= n; 452 } 453 } 454 else if (shape == "opensquare") 455 { 456 var left = count; 457 for (var r = 0; left; ++r) 458 { 459 var n = Math.min(left, cols - (r&1?1:0)); 460 for (var c = 0; c < 2*n; c+=2) 461 { 462 var x = (- c - (r&1)) * separation; 463 var z = -r * separation; 464 offsets.push({"x": x, "z": z}); 465 } 466 left -= n; 467 } 468 } 213 469 214 var left = count;215 for (var r = 0; r < ranks; ++r)470 // TODO: assign to minimise worst-case distances or whatever 471 if (ordering == "default") 216 472 { 217 var n = Math.min(left, cols); 218 for (var c = 0; c < n; ++c) 473 for each (var offset in offsets) 219 474 { 220 var x = ((n-1)/2 - c) * separation; 221 var z = -r * separation; 222 offsets.push({"x": x, "z": z}); 475 var ent = undefined; 476 for (var t in types) 477 { 478 if (types[t].length) 479 { 480 ent = types[t].pop(); 481 offset.ent = ent; 482 break; 483 } 484 } 223 485 } 224 left -= n;225 486 } 487 else if (ordering == "cavalryOnTheSides") 488 { 489 var noffset = []; 490 var cavalrycount = types["Cavalry"].length; 491 offsets.sort(function (a,b) { 492 if (a.x < b.x) return -1; 493 else if (a.x == b.x && a.z < b.z) return -1; 494 return 1; 495 }); 226 496 497 while (offsets.length && types["Cavalry"].length && types["Cavalry"].length > cavalrycount/2) 498 { 499 var offset = offsets.pop(); 500 offset.ent = types["Cavalry"].pop(); 501 noffset.push(offset); 502 } 503 504 offsets.sort(function (a,b) { 505 if (a.x > b.x) return -1; 506 else if (a.x == b.x && a.z < b.z) return -1; 507 return 1; 508 }); 509 510 while (offsets.length && types["Cavalry"].length) 511 { 512 var offset = offsets.pop(); 513 offset.ent = types["Cavalry"].pop(); 514 noffset.push(offset); 515 } 516 517 offsets.sort(function (a,b) { 518 if (a.z < b.z) return -1; 519 else if (a.z == b.z && a.x < b.x) return -1; 520 return 1; 521 }); 522 523 while (offsets.length) 524 { 525 var offset = offsets.pop(); 526 for (var t in types) 527 { 528 if (types[t].length) 529 { 530 offset.ent = types[t].pop(); 531 break; 532 } 533 } 534 noffset.push(offset); 535 } 536 offsets = noffset; 537 } 538 227 539 return offsets; 228 540 }; 229 541 … … 286 598 this.RemoveMembers([msg.entity]); 287 599 }; 288 600 601 Formation.prototype.LoadFormation = function(formationName) 602 { 603 this.formationName = formationName; 604 for each(var ent in this.members) 605 { 606 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 607 cmpUnitAI.SetLastFormationName(this.formationName); 608 } 609 }; 610 289 611 Engine.RegisterComponentType(IID_Formation, "Formation", Formation); -
binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_swordsman.xml
2 2 <Entity parent="template_unit_hero_infantry"> 3 3 <Identity> 4 4 <GenericName>Hero</GenericName> 5 <Classes datatype="tokens">Hero Infantry </Classes>5 <Classes datatype="tokens">Hero Infantry Melee</Classes> 6 6 </Identity> 7 7 <Health> 8 8 <Max>400</Max> -
binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml
2 2 <Entity parent="template_unit_infantry"> 3 3 <Identity> 4 4 <GenericName>Ranged</GenericName> 5 <Classes datatype="tokens">Ranged</Classes> 5 6 </Identity> 6 7 <Health> 7 8 <Max>90</Max> -
binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_pikeman.xml
2 2 <Entity parent="template_unit_hero_infantry"> 3 3 <Identity> 4 4 <GenericName>Hero</GenericName> 5 <Classes datatype="tokens">Hero Infantry </Classes>5 <Classes datatype="tokens">Hero Infantry Melee</Classes> 6 6 </Identity> 7 7 <Cost> 8 8 <Resources> -
binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_spearman.xml
2 2 <Entity parent="template_unit_hero_infantry"> 3 3 <Identity> 4 4 <GenericName>Hero</GenericName> 5 <Classes datatype="tokens">Hero Infantry </Classes>5 <Classes datatype="tokens">Hero Infantry Melee</Classes> 6 6 </Identity> 7 7 <Minimap> 8 8 <Type>hero</Type> -
binaries/data/mods/public/simulation/templates/template_unit_super_infantry_spearman.xml
2 2 <Entity parent="template_unit_super_infantry"> 3 3 <Identity> 4 4 <GenericName>Super Infantry Spearman</GenericName> 5 <Classes datatype="tokens">Melee</Classes> 5 6 </Identity> 6 7 <Cost> 7 8 <Resources> -
binaries/data/mods/public/simulation/templates/template_unit_super_infantry_swordsman.xml
2 2 <Entity parent="template_unit_super_infantry"> 3 3 <Identity> 4 4 <GenericName>Super Infantry Swordsman</GenericName> 5 <Classes datatype="tokens">Melee</Classes> 5 6 </Identity> 6 7 <Cost> 7 8 <Resources> -
binaries/data/mods/public/simulation/templates/template_unit_infantry_melee.xml
2 2 <Entity parent="template_unit_infantry"> 3 3 <Identity> 4 4 <GenericName>Melee Infantry</GenericName> 5 <Classes datatype="tokens">Melee</Classes> 5 6 </Identity> 6 7 <Health> 7 8 <Max>100</Max> -
binaries/data/mods/public/simulation/templates/template_unit_super_cavalry_javelinist.xml
2 2 <Entity parent="template_unit_super_cavalry"> 3 3 <Identity> 4 4 <GenericName>Super Cavalry Javelinist</GenericName> 5 <Classes datatype="tokens"> CavalryRanged</Classes>5 <Classes datatype="tokens">Ranged</Classes> 6 6 </Identity> 7 7 <Cost> 8 8 <Resources> -
binaries/data/mods/public/simulation/templates/template_unit_super_cavalry_swordsman.xml
2 2 <Entity parent="template_unit_super_cavalry"> 3 3 <Identity> 4 4 <GenericName>Super Cavalry Spearman</GenericName> 5 <Classes datatype="tokens">Melee</Classes> 5 6 </Identity> 6 7 <Cost> 7 8 <BuildTime>16</BuildTime> -
binaries/data/mods/public/simulation/templates/template_unit_super_cavalry_spearman.xml
2 2 <Entity parent="template_unit_super_cavalry"> 3 3 <Identity> 4 4 <GenericName>Super Cavalry Spearman</GenericName> 5 <Classes datatype="tokens">Melee</Classes> 5 6 </Identity> 6 7 <Cost> 7 8 <BuildTime>16</BuildTime> -
binaries/data/mods/public/simulation/templates/template_unit_super_infantry_pikeman.xml
2 2 <Entity parent="template_unit_super_infantry"> 3 3 <Identity> 4 4 <GenericName>Super Infantry Pikeman</GenericName> 5 <Classes datatype="tokens">Melee</Classes> 5 6 </Identity> 6 7 <Cost> 7 8 <Resources>