Ticket #13: diffFormation-16-04-2011.patch
File diffFormation-16-04-2011.patch, 16.2 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; … … 275 275 { 276 276 var formations = []; 277 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("F ormation6");285 formations.push(" Formation7");286 formations.push(" Formation8");287 formations.push(" Formation9");288 formations.push(" Formation10");289 formations.push(" Formation11");278 formations.push("Loose"); 279 formations.push("Box"); 280 formations.push("Column Closed"); 281 formations.push("Line Closed"); 282 formations.push("Column Open"); 283 formations.push("Line Open"); 284 formations.push("Flank"); 285 formations.push("Skirmish"); 286 formations.push("Wedge"); 287 formations.push("Testudo"); 288 formations.push("Phalanx"); 289 formations.push("Syntagma"); 290 290 formations.push("Formation12"); 291 291 return formations; 292 292 } -
binaries/data/mods/public/gui/session/input.js
1062 1062 // Performs the specified formation 1063 1063 function performFormation(entity, formationName) 1064 1064 { 1065 submitChatDirectly("FORMATIONS are not implemented yet.");1066 1067 1065 if (entity) 1068 1066 { 1069 1067 console.write(formationName); 1068 1069 var selection = g_Selection.toList(); 1070 Engine.PostNetworkCommand({ 1071 "type": "formation", 1072 "entities": selection, 1073 "name": formationName 1074 }); 1075 1070 1076 } 1071 1077 } 1072 1078 -
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 if (lastFormationName) 374 cmpFormation.LoadFormation(lastFormationName); 375 else 376 cmpFormation.LoadFormation("default"); 342 377 } 343 378 344 379 return Engine.QueryInterface(formationEnt, IID_UnitAI); -
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/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 = "default"; 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/Formation.js
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 = "default"; 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 … … 133 130 var walkingDistance = cmpUnitAI.ComputeWalkingDistance(); 134 131 this.columnar = (walkingDistance > g_ColumnDistanceThreshold); 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 = { "Hero" : [], 180 "Melee" : [], 181 "Ranged" : [], 182 "Support" : [], 183 "Unknown": [] 184 }; // TODO: make this work so we put ranged behind infantry etc 185 185 186 for each (var ent in active) 187 { 188 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); 189 var classes = cmpIdentity.GetClassesList(); 190 var done = false; 191 for each (var cla in classes) 192 { 193 if(cla in types) 194 { 195 types[cla].push(ent); 196 done = true; 197 break; 198 } 199 } 200 if (!done) 201 { 202 types["Unknown"].push(ent); 203 } 204 } 205 206 var count = active.length; 207 208 var shape = undefined; 209 var ordering = "default"; 210 211 var offsets = []; 212 186 213 // Choose a sensible width for the basic default formation 187 214 var cols; 188 if (columnar )215 if (columnar || this.formationName == "Column Closed") 189 216 { 190 217 // Have at most 3 files 191 218 if (count <= 3) 192 219 cols = count; 193 220 else 194 221 cols = 3; 222 shape = "square"; 195 223 } 196 else 224 else if (this.formationName == "default" || this.formationName == "Phalanx") 197 225 { 198 226 // Try to have at least 5 files (so batch training gives a single line), 199 227 // and at most 8 200 228 if (count <= 5) 201 229 cols = count; 202 if (count <= 10)230 else if (count <= 10) 203 231 cols = 5; 204 232 else if (count <= 16) 205 233 cols = Math.ceil(count / 2); 206 234 else 207 235 cols = 8; 236 shape = "square"; 208 237 } 238 else if (this.formationName == "Line Closed") 239 { 240 if (count <= 3) 241 cols = count; 242 else if(count < 30) 243 cols = Math.ceil(count/2); 244 else 245 cols = Math.ceil(count/3); 246 shape = "square"; 247 } 248 else if (this.formationName == "Testudo") 249 { 250 if (count < 9 || count > 25) 251 { 252 this.LoadFormation("default"); 253 return offsets; 254 } 255 cols = Math.ceil(Math.sqrt(count)); 209 256 210 var ranks = Math.ceil(count / cols); 257 shape = "square"; 258 } 259 else if (this.formationName == "Column Open") 260 { 261 cols = 2 262 shape = "opensquare"; 263 } 264 else if (this.formationName == "Line Open") 265 { 266 if (count <= 5) 267 cols = 3; 268 else if (count <= 11) 269 cols = 4; 270 else if (count <= 18) 271 cols = 5; 272 else 273 cols = 6; 274 shape = "opensquare"; 275 } 276 else if (this.formationName == "Loose") // Circle 277 { 278 var depth; 279 if (count < 5 || count > 36) 280 { 281 this.LoadFormation("default"); 282 return offsets; 283 } 284 depth = Math.ceil(count / 12); 211 285 212 var offsets = []; 286 var left = count; 287 var radius = Math.min(left,12) * separation / (2 * Math.PI); 288 for (var c = 0; c < depth; ++c) 289 { 290 var ctodo = Math.min(left,12); 291 var cradius = radius - c * separation / 2; 292 var delta = 2 * Math.PI / ctodo; 293 for (var alpha = 0; ctodo; alpha+=delta) 294 { 295 var x = Math.cos(alpha) * cradius; 296 var z = Math.sin(alpha) * cradius; 297 offsets.push({"x": x, "z": z}); 298 ctodo--; 299 left--; 300 } 301 } 302 } 303 else if (this.formationName == "Box") // Box 304 { 305 var root; 306 if (count > 49) 307 { 308 this.LoadFormation("default"); 309 return offsets; 310 } 311 if (count <= 9) 312 root = 3; 313 else if (count > 9 && count <= 25) 314 root = 5; 315 else if (count > 25 && count <= 49) 316 root = 7; 213 317 214 var left = count; 215 for (var r = 0; r < ranks; ++r) 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") 216 356 { 217 var n = Math.min(left, cols); 218 for (var c = 0; c < n; ++c) 357 if (count < 9 || count > 25) 219 358 { 220 var x = ((n-1)/2 - c) * separation; 221 var z = -r * separation; 222 offsets.push({"x": x, "z": z}); 359 this.LoadFormation("default"); 360 return offsets; 223 361 } 224 left -= n; 362 cols = Math.ceil(count/2); 363 shape = "opensquare"; 225 364 } 365 else if (this.formationName == "Wedge") 366 { 367 var depth; 368 if (count > 49) 369 { 370 this.LoadFormation("default"); 371 return offsets; 372 } 373 if (count <= 9) 374 depth = 3; 375 else if (count > 9 && count <= 16) 376 depth = 4; 377 else if (count > 16 && count <= 25) 378 depth = 5; 379 else if (count > 25 && count <= 49) 380 depth = 7; 226 381 382 var left = count; 383 var width = 2 * depth - 1; 384 for (var p = 0; p < depth && left; ++p) 385 { 386 for (var r = p; r < depth && left; ++r) 387 { 388 var c1 = depth - r + p; 389 var c2 = depth + r - p; 390 391 if (left) 392 { 393 var x = c1 * separation; 394 var z = -r * separation; 395 offsets.push({"x": x, "z": z}); 396 left--; 397 } 398 if (left && c1 != c2) 399 { 400 var x = c2 * separation; 401 var z = -r * separation; 402 offsets.push({"x": x, "z": z}); 403 left--; 404 } 405 } 406 } 407 } 408 else if (this.formationName == "Flank") 409 { 410 if (count < 8 || count > 24) 411 { 412 this.LoadFormation("default"); 413 return offsets; 414 } 415 cols = 3; 416 var leftside = []; 417 leftside[0] = Math.ceil(count/2); 418 leftside[1] = Math.floor(count/2); 419 ranks = Math.ceil(leftside[0] / cols); 420 var off = - separation * 4; 421 for (var side = 0; side < 2; ++side) 422 { 423 var left = leftside[side]; 424 off += side * separation * 8; 425 for (var r = 0; r < ranks; ++r) 426 { 427 var n = Math.min(left, cols); 428 for (var c = 0; c < n; ++c) 429 { 430 var x = off + ((n-1)/2 - c) * separation; 431 var z = -r * separation; 432 offsets.push({"x": x, "z": z}); 433 } 434 left -= n; 435 } 436 } 437 } 438 else if (this.formationName == "Formation12" || this.formationName == "Syntagma") 439 { 440 return offsets; //@todo 441 } 442 443 if (shape == "square") 444 { 445 var ranks = Math.ceil(count / cols); 446 447 var left = count; 448 for (var r = 0; r < ranks; ++r) 449 { 450 var n = Math.min(left, cols); 451 for (var c = 0; c < n; ++c) 452 { 453 var x = ((n-1)/2 - c) * separation; 454 var z = -r * separation; 455 offsets.push({"x": x, "z": z}); 456 } 457 left -= n; 458 } 459 } 460 else if (shape == "opensquare") 461 { 462 var left = count; 463 for (var r = 0; left; ++r) 464 { 465 var n = Math.min(left, cols - (r&1?1:0)); 466 for (var c = 0; c < 2*n; c+=2) 467 { 468 var x = (- c - (r&1)) * separation; 469 var z = -r * separation; 470 offsets.push({"x": x, "z": z}); 471 } 472 left -= n; 473 } 474 } 475 476 // TODO: assign to minimise worst-case distances or whatever 477 if (ordering == "default") 478 { 479 for each (var offset in offsets) 480 { 481 var ent = undefined; 482 for (var t in types) 483 { 484 if (types[t].length) 485 { 486 ent = types[t].pop(); 487 offset.ent = ent 488 break; 489 } 490 } 491 } 492 } 493 227 494 return offsets; 228 495 }; 229 496 … … 286 553 this.RemoveMembers([msg.entity]); 287 554 }; 288 555 556 Formation.prototype.LoadFormation = function(formationName) 557 { 558 this.formationName = formationName; 559 for each(var ent in this.members) 560 { 561 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 562 cmpUnitAI.SetLastFormationName(this.formationName); 563 } 564 } 565 289 566 Engine.RegisterComponentType(IID_Formation, "Formation", Formation); -
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 Infantry</Classes> 5 6 </Identity> 6 7 <Health> 7 8 <Max>90</Max> -
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 Infantry</Classes> 5 6 </Identity> 6 7 <Health> 7 8 <Max>100</Max>