1#include "steerlib.qh" 2#if defined(CSQC) 3#elif defined(MENUQC) 4#elif defined(SVQC) 5 #include "pathlib/utility.qh" 6#endif 7 8/** 9 Uniform pull towards a point 10**/ 11#define steerlib_pull(ent,point) normalize(point - (ent).origin) 12/*vector steerlib_pull(entity this, vector point) 13{ 14 return normalize(point - this.origin); 15}*/ 16 17/** 18 Uniform push from a point 19**/ 20#define steerlib_push(ent,point) normalize(ent.origin - point) 21/* 22vector steerlib_push(entity this, vector point) 23{ 24 return normalize(this.origin - point); 25} 26*/ 27/** 28 Pull toward a point, The further away, the stronger the pull. 29**/ 30vector steerlib_arrive(entity this, vector point, float maximal_distance) 31{ 32 float distance; 33 vector direction; 34 35 distance = bound(0.001,vlen(this.origin - point),maximal_distance); 36 direction = normalize(point - this.origin); 37 return direction * (distance / maximal_distance); 38} 39 40/** 41 Pull toward a point increasing the pull the closer we get 42**/ 43vector steerlib_attract(entity this, vector point, float maximal_distance) 44{ 45 float distance; 46 vector direction; 47 48 distance = bound(0.001,vlen(this.origin - point),maximal_distance); 49 direction = normalize(point - this.origin); 50 51 return direction * (1-(distance / maximal_distance)); 52} 53 54vector steerlib_attract2(entity this, vector point, float min_influense,float max_distance,float max_influense) 55{ 56 float distance; 57 vector direction; 58 float influense; 59 60 distance = bound(0.00001,vlen(this.origin - point),max_distance); 61 direction = normalize(point - this.origin); 62 63 influense = 1 - (distance / max_distance); 64 influense = min_influense + (influense * (max_influense - min_influense)); 65 66 return direction * influense; 67} 68 69/* 70vector steerlib_attract2(vector point, float maximal_distance,float min_influense,float max_influense,float distance) 71{ 72 //float distance; 73 vector current_direction; 74 vector target_direction; 75 float i_target,i_current; 76 77 if(!distance) 78 distance = vlen(this.origin - point); 79 80 distance = bound(0.001,distance,maximal_distance); 81 82 target_direction = normalize(point - this.origin); 83 current_direction = normalize(this.velocity); 84 85 i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense); 86 i_current = 1 - i_target; 87 88 // i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense); 89 90 string s; 91 s = ftos(i_target); 92 bprint("IT: ",s,"\n"); 93 s = ftos(i_current); 94 bprint("IC : ",s,"\n"); 95 96 return normalize((target_direction * i_target) + (current_direction * i_current)); 97} 98*/ 99/** 100 Move away from a point. 101**/ 102vector steerlib_repell(entity this, vector point,float maximal_distance) 103{ 104 float distance; 105 vector direction; 106 107 distance = bound(0.001,vlen(this.origin - point),maximal_distance); 108 direction = normalize(this.origin - point); 109 110 return direction * (1-(distance / maximal_distance)); 111} 112 113/** 114 Try to keep at ideal_distance away from point 115**/ 116vector steerlib_standoff(entity this, vector point,float ideal_distance) 117{ 118 float distance; 119 vector direction; 120 121 distance = vlen(this.origin - point); 122 123 124 if(distance < ideal_distance) 125 { 126 direction = normalize(this.origin - point); 127 return direction * (distance / ideal_distance); 128 } 129 130 direction = normalize(point - this.origin); 131 return direction * (ideal_distance / distance); 132 133} 134 135/** 136 A random heading in a forward halfcicrle 137 138 use like: 139 this.target = steerlib_wander(256,32,this.target) 140 141 where range is the cicrle radius and tresh is how close we need to be to pick a new heading. 142**/ 143vector steerlib_wander(entity this, float range, float tresh, vector oldpoint) 144{ 145 vector wander_point; 146 wander_point = v_forward - oldpoint; 147 148 if (vdist(wander_point, >, tresh)) 149 return oldpoint; 150 151 range = bound(0,range,1); 152 153 wander_point = this.origin + v_forward * 128; 154 wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128); 155 156 return normalize(wander_point - this.origin); 157} 158 159/** 160 Dodge a point. dont work to well. 161**/ 162vector steerlib_dodge(entity this, vector point, vector dodge_dir, float min_distance) 163{ 164 float distance; 165 166 distance = max(vlen(this.origin - point),min_distance); 167 if (min_distance < distance) 168 return '0 0 0'; 169 170 return dodge_dir * (min_distance/distance); 171} 172 173/** 174 flocking by .flock_id 175 Group will move towards the unified direction while keeping close to eachother. 176**/ 177.float flock_id; 178vector steerlib_flock(entity this, float _radius, float standoff,float separation_force,float flock_force) 179{ 180 entity flock_member; 181 vector push = '0 0 0', pull = '0 0 0'; 182 float ccount = 0; 183 184 flock_member = findradius(this.origin, _radius); 185 while(flock_member) 186 { 187 if(flock_member != this) 188 if(flock_member.flock_id == this.flock_id) 189 { 190 ++ccount; 191 push = push + (steerlib_repell(this, flock_member.origin,standoff) * separation_force); 192 pull = pull + (steerlib_arrive(this, flock_member.origin + flock_member.velocity, _radius) * flock_force); 193 } 194 flock_member = flock_member.chain; 195 } 196 return push + (pull* (1 / ccount)); 197} 198 199/** 200 flocking by .flock_id 201 Group will move towards the unified direction while keeping close to eachother. 202 xy only version (for ground movers). 203**/ 204vector steerlib_flock2d(entity this, float _radius, float standoff,float separation_force,float flock_force) 205{ 206 entity flock_member; 207 vector push = '0 0 0', pull = '0 0 0'; 208 float ccount = 0; 209 210 flock_member = findradius(this.origin,_radius); 211 while(flock_member) 212 { 213 if(flock_member != this) 214 if(flock_member.flock_id == this.flock_id) 215 { 216 ++ccount; 217 push = push + (steerlib_repell(this, flock_member.origin, standoff) * separation_force); 218 pull = pull + (steerlib_arrive(this, flock_member.origin + flock_member.velocity, _radius) * flock_force); 219 } 220 flock_member = flock_member.chain; 221 } 222 223 push.z = 0; 224 pull.z = 0; 225 226 return push + (pull * (1 / ccount)); 227} 228 229/** 230 All members want to be in the center, and keep away from eachother. 231 The furtehr form the center the more they want to be there. 232 233 This results in a aligned movement (?!) much like flocking. 234**/ 235vector steerlib_swarm(entity this, float _radius, float standoff,float separation_force,float swarm_force) 236{ 237 entity swarm_member; 238 vector force = '0 0 0', center = '0 0 0'; 239 float ccount = 0; 240 241 swarm_member = findradius(this.origin,_radius); 242 243 while(swarm_member) 244 { 245 if(swarm_member.flock_id == this.flock_id) 246 { 247 ++ccount; 248 center = center + swarm_member.origin; 249 force = force + (steerlib_repell(this, swarm_member.origin,standoff) * separation_force); 250 } 251 swarm_member = swarm_member.chain; 252 } 253 254 center = center * (1 / ccount); 255 force = force + (steerlib_arrive(this, center,_radius) * swarm_force); 256 257 return force; 258} 259 260/** 261 Steer towards the direction least obstructed. 262 Run four tracelines in a forward funnel, bias each diretion negative if something is found there. 263 You need to call makevectors() (or equivalent) before this function to set v_forward and v_right 264**/ 265vector steerlib_traceavoid(entity this, float pitch,float length) 266{ 267 vector vup_left,vup_right,vdown_left,vdown_right; 268 float fup_left,fup_right,fdown_left,fdown_right; 269 vector upwish,downwish,leftwish,rightwish; 270 vector v_left,v_down; 271 272 273 v_left = v_right * -1; 274 v_down = v_up * -1; 275 276 vup_left = (v_forward + (v_left * pitch + v_up * pitch)) * length; 277 traceline(this.origin, this.origin + vup_left,MOVE_NOMONSTERS,this); 278 fup_left = trace_fraction; 279 280 //te_lightning1(NULL,this.origin, trace_endpos); 281 282 vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length; 283 traceline(this.origin,this.origin + vup_right ,MOVE_NOMONSTERS,this); 284 fup_right = trace_fraction; 285 286 //te_lightning1(NULL,this.origin, trace_endpos); 287 288 vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length; 289 traceline(this.origin,this.origin + vdown_left,MOVE_NOMONSTERS,this); 290 fdown_left = trace_fraction; 291 292 //te_lightning1(NULL,this.origin, trace_endpos); 293 294 vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length; 295 traceline(this.origin,this.origin + vdown_right,MOVE_NOMONSTERS,this); 296 fdown_right = trace_fraction; 297 298 //te_lightning1(NULL,this.origin, trace_endpos); 299 upwish = v_up * (fup_left + fup_right); 300 downwish = v_down * (fdown_left + fdown_right); 301 leftwish = v_left * (fup_left + fdown_left); 302 rightwish = v_right * (fup_right + fdown_right); 303 304 return (upwish+leftwish+downwish+rightwish) * 0.25; 305 306} 307 308/** 309 Steer towards the direction least obstructed. 310 Run tracelines in a forward trident, bias each direction negative if something is found there. 311**/ 312vector steerlib_traceavoid_flat(entity this, float pitch, float length, vector vofs) 313{ 314 vector vt_left, vt_right,vt_front; 315 float f_left, f_right,f_front; 316 vector leftwish, rightwish,frontwish, v_left; 317 318 v_left = v_right * -1; 319 320 321 vt_front = v_forward * length; 322 traceline(this.origin + vofs, this.origin + vofs + vt_front,MOVE_NOMONSTERS,this); 323 f_front = trace_fraction; 324 325 vt_left = (v_forward + (v_left * pitch)) * length; 326 traceline(this.origin + vofs, this.origin + vofs + vt_left,MOVE_NOMONSTERS,this); 327 f_left = trace_fraction; 328 329 //te_lightning1(NULL,this.origin, trace_endpos); 330 331 vt_right = (v_forward + (v_right * pitch)) * length; 332 traceline(this.origin + vofs, this.origin + vofs + vt_right ,MOVE_NOMONSTERS,this); 333 f_right = trace_fraction; 334 335 //te_lightning1(NULL,this.origin, trace_endpos); 336 337 leftwish = v_left * f_left; 338 rightwish = v_right * f_right; 339 frontwish = v_forward * f_front; 340 341 return normalize(leftwish + rightwish + frontwish); 342} 343 344//#define BEAMSTEER_VISUAL 345float beamsweep(entity this, vector from, vector dir,float length, float step,float step_up, float step_down) 346{ 347 float i; 348 vector a, b, u, d; 349 350 u = '0 0 1' * step_up; 351 d = '0 0 1' * step_down; 352 353 traceline(from + u, from - d,MOVE_NORMAL,this); 354 if(trace_fraction == 1.0) 355 return 0; 356 357 if(!location_isok(trace_endpos, false, false)) 358 return 0; 359 360 a = trace_endpos; 361 for(i = 0; i < length; i += step) 362 { 363 364 b = a + dir * step; 365 tracebox(a + u,'-4 -4 -4','4 4 4', b + u,MOVE_NORMAL,this); 366 if(trace_fraction != 1.0) 367 return i / length; 368 369 traceline(b + u, b - d,MOVE_NORMAL,this); 370 if(trace_fraction == 1.0) 371 return i / length; 372 373 if(!location_isok(trace_endpos, false, false)) 374 return i / length; 375#ifdef BEAMSTEER_VISUAL 376 te_lightning1(NULL,a+u,b+u); 377 te_lightning1(NULL,b+u,b-d); 378#endif 379 a = trace_endpos; 380 } 381 382 return 1; 383} 384 385vector steerlib_beamsteer(entity this, vector dir, float length, float step, float step_up, float step_down) 386{ 387 float bm_forward, bm_right, bm_left,p; 388 vector vr,vl; 389 390 dir.z *= 0.15; 391 vr = vectoangles(dir); 392 //vr_x *= -1; 393 394 tracebox(this.origin + '0 0 1' * step_up, this.mins, this.maxs, ('0 0 1' * step_up) + this.origin + (dir * length), MOVE_NOMONSTERS, this); 395 if(trace_fraction == 1.0) 396 { 397 //te_lightning1(this,this.origin,this.origin + (dir * length)); 398 return dir; 399 } 400 401 makevectors(vr); 402 bm_forward = beamsweep(this, this.origin, v_forward, length, step, step_up, step_down); 403 404 vr = normalize(v_forward + v_right * 0.125); 405 vl = normalize(v_forward - v_right * 0.125); 406 407 bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down); 408 bm_left = beamsweep(this, this.origin, vl, length, step, step_up, step_down); 409 410 411 p = bm_left + bm_right; 412 if(p == 2) 413 { 414 //te_lightning1(this,this.origin + '0 0 32',this.origin + '0 0 32' + vr * length); 415 //te_lightning1(this.tur_head,this.origin + '0 0 32',this.origin + '0 0 32' + vl * length); 416 417 return v_forward; 418 } 419 420 p = 2 - p; 421 422 vr = normalize(v_forward + v_right * p); 423 vl = normalize(v_forward - v_right * p); 424 bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down); 425 bm_left = beamsweep(this, this.origin, vl, length, step, step_up, step_down); 426 427 428 if(bm_left + bm_right < 0.15) 429 { 430 vr = normalize((v_forward*-1) + v_right * 0.90); 431 vl = normalize((v_forward*-1) - v_right * 0.90); 432 433 bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down); 434 bm_left = beamsweep(this, this.origin, vl, length, step, step_up, step_down); 435 } 436 437 //te_lightning1(this,this.origin + '0 0 32',this.origin + '0 0 32' + vr * length); 438 //te_lightning1(this.tur_head,this.origin + '0 0 32',this.origin + '0 0 32' + vl * length); 439 440 bm_forward *= bm_forward; 441 bm_right *= bm_right; 442 bm_left *= bm_left; 443 444 vr = vr * bm_right; 445 vl = vl * bm_left; 446 447 return normalize(vr + vl); 448 449} 450 451 452////////////////////////////////////////////// 453// Testting // 454// Everything below this point is a mess :D // 455////////////////////////////////////////////// 456//#define TLIBS_TETSLIBS 457#ifdef TLIBS_TETSLIBS 458void flocker_die(entity this) 459{ 460 Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); 461 462 this.owner.cnt += 1; 463 this.owner = NULL; 464 465 this.nextthink = time; 466 setthink(this, SUB_Remove); 467} 468 469 470void flocker_think(entity this) 471{ 472 vector dodgemove,swarmmove; 473 vector reprellmove,wandermove,newmove; 474 475 this.angles_x = this.angles.x * -1; 476 makevectors(this.angles); 477 this.angles_x = this.angles.x * -1; 478 479 dodgemove = steerlib_traceavoid(this, 0.35,1000); 480 swarmmove = steerlib_flock(this, 500,75,700,500); 481 reprellmove = steerlib_repell(this, this.owner.enemy.origin+this.enemy.velocity,2000) * 700; 482 483 if(dodgemove == '0 0 0') 484 { 485 this.pos1 = steerlib_wander(this, 0.5,0.1,this.pos1); 486 wandermove = this.pos1 * 50; 487 } 488 else 489 this.pos1 = normalize(this.velocity); 490 491 dodgemove = dodgemove * vlen(this.velocity) * 5; 492 493 newmove = swarmmove + reprellmove + wandermove + dodgemove; 494 this.velocity = movelib_inertmove_byspeed(this, newmove,300,0.2,0.9); 495 //this.velocity = movelib_inertmove(this, dodgemove,0.65); 496 497 this.velocity = movelib_dragvec(this, 0.01,0.6); 498 499 this.angles = vectoangles(this.velocity); 500 501 if(this.health <= 0) 502 flocker_die(this); 503 else 504 this.nextthink = time + 0.1; 505} 506 507MODEL(FLOCKER, "models/turrets/rocket.md3"); 508 509void spawn_flocker(entity this) 510{ 511 entity flocker = new(flocker); 512 513 setorigin(flocker, this.origin + '0 0 32'); 514 setmodel (flocker, MDL_FLOCKER); 515 setsize (flocker, '-3 -3 -3', '3 3 3'); 516 517 flocker.flock_id = this.flock_id; 518 flocker.owner = this; 519 setthink(flocker, flocker_think); 520 flocker.nextthink = time + random() * 5; 521 PROJECTILE_MAKETRIGGER(flocker); 522 set_movetype(flocker, MOVETYPE_BOUNCEMISSILE); 523 flocker.effects = EF_LOWPRECISION; 524 flocker.velocity = randomvec() * 300; 525 flocker.angles = vectoangles(flocker.velocity); 526 flocker.health = 10; 527 flocker.pos1 = normalize(flocker.velocity + randomvec() * 0.1); 528 529 this.cnt = this.cnt -1; 530 531} 532 533void flockerspawn_think(entity this) 534{ 535 if(this.cnt > 0) 536 spawn_flocker(this); 537 538 this.nextthink = time + this.delay; 539 540} 541 542void flocker_hunter_think(entity this) 543{ 544 vector dodgemove,attractmove,newmove; 545 entity ee; 546 547 this.angles_x = this.angles.x * -1; 548 makevectors(this.angles); 549 this.angles_x = this.angles.x * -1; 550 551 if(this.enemy) 552 if(vdist(this.enemy.origin - this.origin, <, 64)) 553 { 554 ee = this.enemy; 555 ee.health = -1; 556 this.enemy = NULL; 557 558 } 559 560 if(!this.enemy) 561 { 562 FOREACH_ENTITY_FLOAT(flock_id, this.flock_id, 563 { 564 if(it == this.owner || it == ee) 565 continue; 566 567 if(!this.enemy || vlen2(this.origin - it.origin) > vlen2(this.origin - this.enemy.origin)) 568 this.enemy = it; 569 }); 570 } 571 572 if(this.enemy) 573 attractmove = steerlib_attract(this, this.enemy.origin+this.enemy.velocity * 0.1,5000) * 1250; 574 else 575 attractmove = normalize(this.velocity) * 200; 576 577 dodgemove = steerlib_traceavoid(this, 0.35,1500) * vlen(this.velocity); 578 579 newmove = dodgemove + attractmove; 580 this.velocity = movelib_inertmove_byspeed(this, newmove,1250,0.3,0.7); 581 this.velocity = movelib_dragvec(this, 0.01,0.5); 582 583 this.angles = vectoangles(this.velocity); 584 this.nextthink = time + 0.1; 585} 586 587 588float globflockcnt; 589spawnfunc(flockerspawn) 590{ 591 ++globflockcnt; 592 593 if(!this.cnt) this.cnt = 20; 594 if(!this.delay) this.delay = 0.25; 595 if(!this.flock_id) this.flock_id = globflockcnt; 596 597 setthink(this, flockerspawn_think); 598 this.nextthink = time + 0.25; 599 600 this.enemy = new(FLock Hunter); 601 602 setmodel(this.enemy, MDL_FLOCKER); 603 setorigin(this.enemy, this.origin + '0 0 768' + (randomvec() * 128)); 604 605 this.enemy.scale = 3; 606 this.enemy.effects = EF_LOWPRECISION; 607 set_movetype(this.enemy, MOVETYPE_BOUNCEMISSILE); 608 PROJECTILE_MAKETRIGGER(this.enemy); 609 setthink(this.enemy, flocker_hunter_think); 610 this.enemy.nextthink = time + 10; 611 this.enemy.flock_id = this.flock_id; 612 this.enemy.owner = this; 613} 614#endif 615 616 617 618