Ticket #13: diffFormation-13-03-2011.patch
File diffFormation-13-03-2011.patch, 11.4 KB (added by , 13 years ago) |
---|
-
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: -
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
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 == "Formation2") // Column 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 == "Formation9") // Square 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 == "Formation3") // Line 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 == "Formation4") // OpenColumn 249 { 250 cols = 2 251 shape = "opensquare"; 252 } 253 else if (this.formationName == "Formation5") // OpenSquare 254 { 255 if (count <= 5) 256 cols = 3; 257 else if (count <= 11) 258 cols = 4; 259 else if (count <= 18) 260 cols = 5; 261 else 262 cols = 6; 263 shape = "opensquare"; 264 } 265 else if (this.formationName == "Formation0") // Circle 266 { 267 var depth; 268 if (count < 5 || count > 36) 269 { 270 this.LoadFormation("default"); 271 return offsets; 272 } 273 depth = Math.ceil(count / 12); 209 274 210 var ranks = Math.ceil(count / cols); 275 var left = count; 276 var radius = Math.min(left,12) * separation / (2 * Math.PI); 277 for (var c = 0; c < depth; ++c) 278 { 279 var ctodo = Math.min(left,12); 280 var cradius = radius - c * separation / 2; 281 var delta = 2 * Math.PI / ctodo; 282 for (var alpha = 0; ctodo; alpha+=delta) 283 { 284 var x = Math.cos(alpha) * cradius; 285 var z = Math.sin(alpha) * cradius; 286 offsets.push({"x": x, "z": z}); 287 ctodo--; 288 left--; 289 } 290 } 291 } 292 else if (this.formationName == "Formation1") // Box 293 { 294 var root; 295 if (count > 49) 296 { 297 this.LoadFormation("default"); 298 return offsets; 299 } 300 if (count <= 9) 301 root = 3; 302 else if (count > 9 && count <= 25) 303 root = 5; 304 else if (count > 25 && count <= 49) 305 root = 7; 211 306 212 var offsets = []; 307 var left = count; 308 var meleeleft = types["Melee"].length; 309 for (var sq = Math.floor(root/2); sq >= 0; --sq) 310 { 311 var width = sq * 2 + 1; 312 var stodo; 313 if (sq == 0) 314 { 315 stodo = left; 316 } 317 else 318 { 319 if (meleeleft >= width*width - (width-2)*(width-2)) // form a complete box 320 { 321 stodo = width*width - (width-2)*(width-2); 322 meleeleft -= stodo; 323 } 324 else // compact 325 stodo = Math.max(0,left - (width-2)*(width-2)); 326 } 213 327 214 var left = count; 215 for (var r = 0; r < ranks; ++r) 328 for (var r = -sq; r <= sq && stodo; ++r) 329 { 330 for (var c = -sq; c <= sq && stodo; ++c) 331 { 332 if (Math.abs(r) == sq || Math.abs(c) == sq) 333 { 334 var x = c * separation; 335 var z = -r * separation; 336 offsets.push({"x": x, "z": z}); 337 stodo--; 338 left--; 339 } 340 } 341 } 342 } 343 } 344 else if (this.formationName == "Formation7") // Skirmish 216 345 { 217 var n = Math.min(left, cols); 218 for (var c = 0; c < n; ++c) 346 if (count < 9 || count > 25) 219 347 { 220 var x = ((n-1)/2 - c) * separation; 221 var z = -r * separation; 222 offsets.push({"x": x, "z": z}); 348 this.LoadFormation("default"); 349 return offsets; 223 350 } 224 left -= n; 351 cols = Math.ceil(count/2); 352 shape = "opensquare"; 225 353 } 354 else if (this.formationName == "Formation8") // Wedge 355 { 356 var depth; 357 if (count > 49) 358 { 359 this.LoadFormation("default"); 360 return offsets; 361 } 362 if (count <= 9) 363 depth = 3; 364 else if (count > 9 && count <= 16) 365 depth = 4; 366 else if (count > 16 && count <= 25) 367 depth = 5; 368 else if (count > 25 && count <= 49) 369 depth = 7; 226 370 371 var left = count; 372 var width = 2 * depth - 1; 373 for (var p = 0; p < depth && left; ++p) 374 { 375 for (var r = p; r < depth && left; ++r) 376 { 377 var c1 = depth - r + p; 378 var c2 = depth + r - p; 379 380 if (left) 381 { 382 var x = c1 * separation; 383 var z = -r * separation; 384 offsets.push({"x": x, "z": z}); 385 left--; 386 } 387 if (left && c1 != c2) 388 { 389 var x = c2 * separation; 390 var z = -r * separation; 391 offsets.push({"x": x, "z": z}); 392 left--; 393 } 394 } 395 } 396 } 397 398 if (shape == "square") 399 { 400 var ranks = Math.ceil(count / cols); 401 402 var left = count; 403 for (var r = 0; r < ranks; ++r) 404 { 405 var n = Math.min(left, cols); 406 for (var c = 0; c < n; ++c) 407 { 408 var x = ((n-1)/2 - c) * separation; 409 var z = -r * separation; 410 offsets.push({"x": x, "z": z}); 411 } 412 left -= n; 413 } 414 } 415 else if (shape == "opensquare") 416 { 417 var left = count; 418 for (var r = 0; left; ++r) 419 { 420 var n = Math.min(left, cols - (r&1?1:0)); 421 for (var c = 0; c < 2*n; c+=2) 422 { 423 var x = (- c - (r&1)) * separation; 424 var z = -r * separation; 425 offsets.push({"x": x, "z": z}); 426 } 427 left -= n; 428 } 429 } 430 431 // TODO: assign to minimise worst-case distances or whatever 432 if (ordering == "default") 433 { 434 for each (var offset in offsets) 435 { 436 var ent = undefined; 437 for (var t in types) 438 { 439 if (types[t].length) 440 { 441 ent = types[t].pop(); 442 offset.ent = ent 443 break; 444 } 445 } 446 } 447 } 448 227 449 return offsets; 228 450 }; 229 451 … … 286 508 this.RemoveMembers([msg.entity]); 287 509 }; 288 510 511 Formation.prototype.LoadFormation = function(formationName) 512 { 513 this.formationName = formationName; 514 } 515 289 516 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>