etayn

old gzdoom project
git clone git://moonbender.net/etayn
Log | Files | Refs | README

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 }