summons.zsc (11777B)
1 //TODO: Improve pathfinding (the permanent goal) 2 //TODO: Summons make a blood splatter when they hit the player 3 //(but deal no damage), fix that 4 //TODO: Make summons step back if they're within 5 //(say) 100 units of the player 6 //TODO: Have ChaseTarget() get called from a function that 7 //runs every tick instead of in the state block, so 8 //summons don't move slower if they have a running 9 //animation with more frames MAYBE ??? 10 //TODO: Code other two chucklefucks 11 //TODO: Jump to higher sectors, maybe? 12 //TODO: Summons should pioritize monsters that are behind you. 13 //TODO: If summons can't get to a target, should try to 14 //stand in front of it 15 16 class TestMark1 : Actor 17 { 18 default 19 { 20 +THRUACTORS 21 +NOGRAVITY 22 } 23 24 states 25 { 26 Spawn: 27 BON1 A 1; 28 loop; 29 Death: 30 TNT1 A 0; 31 stop; 32 } 33 34 } 35 36 class TestMark2 : TestMark1 37 { 38 states 39 { 40 Spawn: 41 BON2 A 8; 42 Death: 43 BON2 A 1; 44 stop; 45 } 46 } 47 48 //for pathfinding 49 struct NavSect 50 { 51 //line between current and destination sector 52 Line transLine; 53 54 //pointer to destination sector 55 Sector s; 56 57 //dest sector height, or ceiling z - floor z 58 double height; 59 60 //step up or down 61 double deltaZ; 62 63 Vector2 mid; 64 } 65 66 67 class SummonBase : Actor 68 { 69 //linedef flags for pathfinding 70 const ML_BLOCKING = 0x00000001; 71 const ML_BLOCKMONSTERS = 0x00000002; 72 const ML_TWOSIDED = 0x00000004; 73 const ML_BLOCKEVERYTHING = 0x00008000; 74 75 const PF_IMPOSSIBLE = 0; 76 const PF_CLOSERSECT = 1; 77 const PF_STRAIGHTSHOT = 2; 78 79 Actor attackTarget; 80 NavSect navGoal; 81 bool pathPlan; 82 83 //emotions! 84 int Anger, Frustration, Happiness; 85 86 //debug 87 // Actor Cmarker; 88 // Actor Fmarker; 89 90 91 default 92 { 93 Health 400; 94 Speed 15; 95 Radius 16; 96 Height 56; 97 Mass 100; 98 Monster; //TODO: Pick and choose specific flags instead of using the Monster combo 99 -COUNTKILL 100 +FLOORCLIP 101 +THRUSPECIES 102 +DROPOFF 103 Species "Player"; 104 DamageFactor "PlayerDamage", 0; 105 } 106 107 override void PostBeginPlay() 108 { 109 self.attackTarget = null; 110 self.Anger = 0; 111 self.Frustration = 0; 112 self.Happiness = 0; 113 114 self.pathPlan = false; 115 116 117 //test 118 // Cmarker = spawn("TestMark1", pos); 119 // Fmarker = spawn("TestMark1", pos); 120 } 121 122 override void Tick() 123 { 124 self.ManageEmotions(); 125 self.ChooseTarget(); 126 super.Tick(); 127 } 128 129 //Choose target to attack 130 void ChooseTarget() 131 { 132 if (!master) 133 return; 134 135 let ownerx = DevilbunnyPlayer(master); 136 137 attackTarget = ownerx.summonTarget; 138 } 139 140 141 //TODO: Make this only work when actor's on the ground 142 void SMoveForward() 143 { 144 double move; 145 move = Speed / 5; 146 147 Thrust(move, Angle); 148 } 149 150 151 //super advanced pathfinding go! 152 int PlanPath() 153 { 154 //if it's in the same sector, run straight in 155 if (CurSector == attackTarget.CurSector) 156 { 157 return PF_STRAIGHTSHOT; 158 } 159 160 NavSect gsect; 161 //we're comparing the (possible) destination sector's distance from target 162 //to the summon's current distance from target. If it's shorter 163 //move to the destination sector 164 double targ_dist = (CurSector.centerSpot - attackTarget.Pos.xy).Length(); 165 166 int plan = PF_IMPOSSIBLE; 167 168 //check all sectors bordering this one's linedefs 169 for (int it = 0; it < CurSector.lines.Size(); ++it) 170 { 171 //coding this is so much more complicated than it should be 172 line l; 173 sector s; 174 double tdist; 175 Vector2 mid; 176 double h, dz; 177 178 l = CurSector.lines[it]; 179 180 if ( 181 (l.flags & ML_TWOSIDED) && 182 !(l.flags & (ML_BLOCKING 183 | ML_BLOCKEVERYTHING 184 | ML_BLOCKMONSTERS)) 185 ) 186 { 187 //either the front or the back of this linedef 188 //should be the sector our summon is standing in 189 //so ignore it 190 if (l.backSector == CurSector) 191 { 192 s = l.frontSector; 193 } 194 else 195 { 196 s = l.backSector; 197 } 198 199 //debug 200 // Vector3 sp; 201 // sp.x = s.centerSpot.x; 202 // sp.y = s.centerSpot.y; 203 // sp.z = s.floorPlane.d; 204 // spawn("TestMark2", sp); 205 206 tdist = (s.centerSpot - attackTarget.Pos.xy).Length(); 207 mid = s.centerSpot; 208 //probably gets a little janky with sloped floors 209 h = s.ceilingPlane.ZatPoint(mid) - s.floorPlane.ZatPoint(mid); 210 dz = s.floorPlane.ZatPoint(mid) - curSector.floorPlane.ZatPoint(pos.xy); 211 212 //if ceiling is too low to run through 213 if (h < 64) 214 { 215 continue; 216 } 217 218 //if it's elevated above the current sector 219 if (dz > 16) 220 { 221 continue; 222 } 223 224 //if it's in an adjacent (pathable) sector, just run right in 225 if (s == attackTarget.CurSector) 226 { 227 return PF_STRAIGHTSHOT; 228 } 229 230 //if this distance is lower than any other sector 231 //checked (and the current distance between 232 //the summon and the target) then this is the sector we 233 //want to go to! 234 235 //"struct assignment not implemented yet" 236 //come onnnn, seriously?? 237 if (tdist < targ_dist) 238 { 239 plan = PF_CLOSERSECT; 240 //I'm so mad about this 241 navGoal.transLine = l; 242 navGoal.s = s; 243 navGoal.height = h; 244 navGoal.deltaZ = dz; 245 navGoal.mid = mid; 246 //ugh 247 } 248 } 249 } 250 251 //test 252 // if (plan == PF_CLOSERSECT) 253 // { 254 // Vector3 gp; 255 // gp.x = navGoal.mid.x; 256 // gp.y = navGoal.mid.y; 257 // gp.z = navGoal.s.floorPlane.ZatPoint(gp.xy); 258 // Cmarker.SetOrigin(gp, true); 259 // gp.z = navGoal.s.ceilingPlane.ZatPoint(gp.xy); 260 // Fmarker.SetOrigin(gp, true); 261 // } 262 //-- 263 264 return plan; 265 } 266 267 void FacePoint(Vector2 point) 268 { 269 Vector2 offs = point - Pos.xy; 270 Angle = atan2(offs.y, offs.x); 271 } 272 273 void ChaseTarget(StateLabel attackstate = "Melee") 274 { 275 if (!attackTarget) 276 return; 277 278 if (Distance2D(attackTarget) < 130) 279 { 280 A_Face(attackTarget); 281 SetStateLabel(attackstate); 282 return; 283 } 284 285 //can't run around if in the air 286 if (!((pos.z <= floorz) || bOnMobj)) 287 return; 288 289 int plan = PlanPath(); 290 291 //A_Log(attackTarget.GetClassName() .. " | " .. plan .. " | CSec " .. CurSector.Index() .. " | TSec " .. attackTarget.CurSector.Index()); 292 293 //if in the same sector as the target, run straight in 294 if (plan == PF_STRAIGHTSHOT) 295 { 296 A_Face(attackTarget); 297 SMoveForward(); 298 return; 299 } 300 if (plan == PF_CLOSERSECT) 301 { 302 FacePoint(navGoal.mid); 303 SMoveForward(); 304 return; 305 } 306 if (plan == PF_IMPOSSIBLE) 307 return; //TODO: Try to get between target and player 308 309 } 310 311 void ManageEmotions() 312 { 313 //GetPissed(); 314 FindHappiness(); 315 FindFrustration(); 316 } 317 318 virtual void FindHappiness() 319 { 320 if (master) 321 { 322 let owner1 = DevilbunnyPlayer(master); 323 if (Distance2D(owner1) <= 500) 324 { 325 if (owner1.hasArmorType == 0) 326 { 327 Happiness = 60; 328 } 329 else 330 { 331 Happiness = 40; 332 } 333 } 334 else 335 { 336 Happiness = 20; 337 } 338 } 339 340 HealThing(Happiness); 341 } 342 343 virtual void FindFrustration() 344 { 345 if (!attackTarget) 346 { 347 Frustration = 0; 348 return; 349 } 350 351 Frustration += 1; 352 } 353 } 354 355 356 class ChuckleFuckOne : SummonBase 357 { 358 void HammerAttack() 359 { 360 FTranslatedLineTarget t; 361 A_Face(attackTarget); 362 LineAttack(Angle, 130, Pitch, 90 + Happiness, "PlayerDamage", null, 0, t); 363 364 if (t.linetarget) 365 { 366 Frustration -= 100; 367 if (Frustration < 0) 368 { 369 Frustration = 0; 370 } 371 } 372 373 } 374 375 states 376 { 377 Spawn: 378 CHK1 KL 60; 379 See: 380 CHK1 A 0 381 { 382 if(attackTarget) 383 { 384 SetStateLabel("Run"); 385 } 386 else 387 { 388 if (!CheckIfCloser(master, 1024, false)) 389 { 390 SetStateLabel("FindMaster"); 391 } 392 if (!CheckIfCloser(master, 400)) 393 { 394 SetStateLabel("Follow"); 395 } 396 397 } 398 } 399 Lookout: 400 CHK1 A 1 A_FaceMaster(); 401 Goto See; 402 Run: 403 CHK1 BBBCCCDDDEEE 2 404 { 405 ChaseTarget("Swing"); 406 if (Frustration > 200) 407 { 408 SetStateLabel("FindMaster"); 409 Frustration = 0; 410 } 411 } 412 goto See; 413 Follow: 414 CHK1 BBBCCCDDDEEE 2 415 { 416 A_FaceMaster(); 417 SMoveForward(); 418 } 419 goto See; 420 FindMaster: 421 CHK1 KL 8 SetOrigin(master.pos, false); 422 Goto See; 423 Teleport: 424 CHK1 KL 8 SetOrigin(attackTarget.pos, false); 425 Goto See; 426 Swing: 427 CHK1 I 6 428 { 429 HammerAttack(); 430 } 431 CHK1 J 6; 432 Goto See; 433 Death: 434 CHK1 FGH 8; 435 CHK1 H -1; 436 Stop; 437 438 } 439 440 } 441 442 class FuckDebris : Actor 443 { 444 default 445 { 446 Radius 10; 447 Height 10; 448 +THRUACTORS 449 } 450 451 states 452 { 453 Spawn: 454 RBFI ABCDABCDABCDABCDABCDABCDABCDABCD 4; 455 Death: 456 TNT1 A 2; 457 stop; 458 } 459 } 460 461 class FuckArrow : Actor 462 { 463 default 464 { 465 Radius 10; 466 Height 4; 467 Speed 40; 468 Damage 50; 469 Projectile; 470 DamageType "PlayerDamage"; 471 Species "Player"; 472 +ROLLSPRITE 473 +THRUSPECIES 474 } 475 476 states 477 { 478 Spawn: 479 RBIN ABCD 2; 480 loop; 481 Crash: 482 Death: 483 XDeath: 484 TNT1 A 2 485 { 486 A_Explode(400, 256); 487 for (int it = 0; it < 8; it++) 488 { 489 actor a; 490 a = spawn("FuckDebris", pos); 491 a.Vel.X = frandom(-10.0, 10.0); 492 a.Vel.Y = frandom(-10.0, 10.0); 493 a.Vel.Z = frandom(-10.0, 10.0); 494 } 495 } 496 stop; 497 } 498 } 499 500 class ChuckleFuckTwo : SummonBase 501 { 502 states 503 { 504 Spawn: 505 CHK2 LM 60 A_FaceMaster; 506 See: 507 CHK2 A 0 508 { 509 if (!CheckIfCloser(master, 1024, false)) 510 { 511 SetStateLabel("FindMaster"); 512 } 513 if (!CheckIfCloser(master, 400)) 514 { 515 SetStateLabel("Follow"); 516 } 517 if (attackTarget) 518 { 519 if(CheckSight(attackTarget)) 520 { 521 SetStateLabel("Shoot"); 522 } 523 } 524 } 525 Lookout: 526 CHK2 A 1; 527 goto See; 528 Follow: 529 CHK2 BBBCCCDDDEEE 2 530 { 531 A_FaceMaster(); 532 SMoveForward(); 533 } 534 goto See; 535 FindMaster: 536 CHK2 LM 8 SetOrigin(master.pos, false); 537 goto See; 538 Shoot: 539 CHK2 FFGGHHIJHIJHIJHIJ 4 A_Face(attackTarget); 540 CHK2 K 1 541 { 542 A_Face(attackTarget); 543 //TODO: hacky, should make a function for this 544 target = attackTarget; 545 A_SpawnProjectile("FuckArrow", 50, 0, 0, CMF_TRACKOWNER); 546 } 547 CHK2 K 8; 548 goto See; 549 Death: 550 CHK2 NOP 8; 551 CHK2 P -1; 552 stop; 553 554 555 } 556 } 557 558 class ChuckleFuckThree : SummonBase 559 { 560 void AxeAttack() 561 { 562 FTranslatedLineTarget t; 563 A_Face(attackTarget); 564 LineAttack(Angle, 130, Pitch, 200 + Happiness, "PlayerDamage", null, 0, t); 565 566 if (t.linetarget) 567 { 568 t.linetarget.Thrust(30, Angle); 569 Frustration -= 100; 570 if (Frustration < 0) 571 { 572 Frustration = 0; 573 } 574 } 575 576 } 577 578 states 579 { 580 Spawn: 581 CHK3 KL 60 A_FaceMaster; 582 See: 583 CHK3 A 0 584 { 585 if(attackTarget) 586 { 587 SetStateLabel("Run"); 588 } 589 else 590 { 591 if (!CheckIfCloser(master, 1024, false)) 592 { 593 SetStateLabel("FindMaster"); 594 } 595 if (!CheckIfCloser(master, 400)) 596 { 597 SetStateLabel("Follow"); 598 } 599 600 } 601 } 602 Lookout: 603 CHK3 A 1 A_FaceMaster(); 604 Goto See; 605 Run: 606 CHK3 BBBCCCDDDEEE 2 607 { 608 ChaseTarget("Swing"); 609 if (Frustration > 200) 610 { 611 SetStateLabel("FindMaster"); 612 Frustration = 0; 613 } 614 } 615 goto See; 616 Follow: 617 CHK3 BBBCCCDDDEEE 2 618 { 619 A_FaceMaster(); 620 SMoveForward(); 621 } 622 goto See; 623 FindMaster: 624 CHK3 KL 8 SetOrigin(master.pos, false); 625 Goto See; 626 Teleport: 627 CHK3 KL 8 SetOrigin(attackTarget.pos, false); 628 Goto See; 629 Swing: 630 CHK3 F 8; 631 CHK3 G 10; 632 TNT1 A 0 633 { 634 AxeAttack(); 635 } 636 CHK3 HIJ 4; 637 Goto See; 638 Death: 639 CHK3 MNO 8; 640 CHK3 O -1; 641 Stop; 642 } 643 } 644 645 Class SummonFireBall : Actor 646 { 647 default 648 { 649 Radius 6; 650 Height 16; 651 Speed 20; 652 Damage 30; 653 Projectile; 654 +THRUSPECIES 655 Species "Player"; 656 DamageType "PlayerDamage"; 657 } 658 659 states 660 { 661 Spawn: 662 BAL7 AB 4 Bright; 663 loop; 664 Death: 665 BAL7 CDE 6 Bright; 666 stop; 667 } 668 }