/
painelemental.txt
227 lines (199 loc) · 5.17 KB
/
painelemental.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
//===========================================================================
//
// Pain Elemental
//
//===========================================================================
class PainElemental : Actor
{
Default
{
Health 400;
Radius 31;
Height 56;
Mass 400;
Speed 8;
PainChance 128;
Monster;
+FLOAT
+NOGRAVITY
SeeSound "pain/sight";
PainSound "pain/pain";
DeathSound "pain/death";
ActiveSound "pain/active";
}
States
{
Spawn:
PAIN A 10 A_Look;
Loop;
See:
PAIN AABBCC 3 A_Chase;
Loop;
Missile:
PAIN D 5 A_FaceTarget;
PAIN E 5 A_FaceTarget;
PAIN F 5 BRIGHT A_FaceTarget;
PAIN F 0 BRIGHT A_PainAttack;
Goto See;
Pain:
PAIN G 6;
PAIN G 6 A_Pain;
Goto See;
Death:
PAIN H 8 BRIGHT;
PAIN I 8 BRIGHT A_Scream;
PAIN JK 8 BRIGHT;
PAIN L 8 BRIGHT A_PainDie;
PAIN M 8 BRIGHT;
Stop;
Raise:
PAIN MLKJIH 8;
Goto See;
}
}
//===========================================================================
//
// Code (must be attached to Actor)
//
//===========================================================================
extend class Actor
{
//
// A_PainShootSkull
// Spawn a lost soul and launch it at the target
//
void A_PainShootSkull(Class<Actor> spawntype, double angle, int flags = 0, int limit = -1)
{
// Don't spawn if we get massacred.
if (DamageType == 'Massacre') return;
if (spawntype == null) spawntype = "LostSoul";
// [RH] check to make sure it's not too close to the ceiling
if (pos.z + height + 8 > ceilingz)
{
if (bFloat)
{
Vel.Z -= 2;
bInFloat = true;
bVFriction = true;
}
return;
}
// [RH] make this optional
if (limit < 0 && compat_limitpain)
limit = 21;
if (limit > 0)
{
// count total number of skulls currently on the level
// if there are already 21 skulls on the level, don't spit another one
int count = limit;
ThinkerIterator it = ThinkerIterator.Create(spawntype);
Thinker othink;
while ( (othink = it.Next ()) )
{
if (--count == 0)
return;
}
}
// okay, there's room for another one
double otherradius = GetDefaultByType(spawntype).radius;
double prestep = 4 + (radius + otherradius) * 1.5;
Vector2 move = AngleToVector(angle, prestep);
Vector3 spawnpos = pos + (0,0,8);
Vector3 destpos = spawnpos + move;
Actor other = Spawn(spawntype, spawnpos, ALLOW_REPLACE);
// Now check if the spawn is legal. Unlike Boom's hopeless attempt at fixing it, let's do it the same way
// P_XYMovement solves the line skipping: Spawn the Lost Soul near the PE's center and then use multiple
// smaller steps to get it to its intended position. This will also result in proper clipping, but
// it will avoid all the problems of the Boom method, which checked too many lines that weren't even touched
// and despite some adjustments never worked with portals.
if (other != null)
{
double maxmove = other.radius - 1;
if (maxmove <= 0) maxmove = 16;
double xspeed = abs(move.X);
double yspeed = abs(move.Y);
int steps = 1;
if (xspeed > yspeed)
{
if (xspeed > maxmove)
{
steps = int(1 + xspeed / maxmove);
}
}
else
{
if (yspeed > maxmove)
{
steps = int(1 + yspeed / maxmove);
}
}
Vector2 stepmove = move / steps;
bool savedsolid = bSolid;
bool savednoteleport = other.bNoTeleport;
// make the PE nonsolid for the check and the LS non-teleporting so that P_TryMove doesn't do unwanted things.
bSolid = false;
other.bNoTeleport = true;
for (int i = 0; i < steps; i++)
{
Vector2 ptry = other.pos.xy + stepmove;
double oldangle = other.angle;
if (!other.TryMove(ptry, 0))
{
// kill it immediately
other.ClearCounters();
other.DamageMobj(self, self, TELEFRAG_DAMAGE, 'None');
bSolid = savedsolid;
other.bNoTeleport = savednoteleport;
return;
}
if (other.pos.xy != ptry)
{
// If the new position does not match the desired position, the player
// must have gone through a portal.
// For that we need to adjust the movement vector for the following steps.
double anglediff = deltaangle(oldangle, other.angle);
if (anglediff != 0)
{
stepmove = RotateVector(stepmove, anglediff);
}
}
}
bSolid = savedsolid;
other.bNoTeleport = savednoteleport;
// [RH] Lost souls hate the same things as their pain elementals
other.CopyFriendliness (self, !(flags & PAF_NOTARGET));
if (!(flags & PAF_NOSKULLATTACK))
{
other.A_SkullAttack();
}
}
}
void A_PainAttack(class<Actor> spawntype = "LostSoul", double addangle = 0, int flags = 0, int limit = -1)
{
if (target)
{
A_FaceTarget();
A_PainShootSkull(spawntype, angle + addangle, flags, limit);
}
}
void A_DualPainAttack(class<Actor> spawntype = "LostSoul")
{
if (target)
{
A_FaceTarget();
A_PainShootSkull(spawntype, angle + 45);
A_PainShootSkull(spawntype, angle - 45);
}
}
void A_PainDie(class<Actor> spawntype = "LostSoul")
{
if (target && IsFriend(target))
{ // And I thought you were my friend!
bFriendly = false;
}
A_NoBlocking();
A_PainShootSkull(spawntype, angle + 90);
A_PainShootSkull(spawntype, angle + 180);
A_PainShootSkull(spawntype, angle + 270);
}
}