forked from SimsOCallaghan/ArcaneDimensions
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathai_minions.qc
267 lines (233 loc) · 9.74 KB
/
ai_minions.qc
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*======================================================================
MINIONS FUNCTIONS
Minion Support functions
Setup and update minion counters centralized to reduce errors
monster_death_precheck remove from the counter (dying event)
minion_voreling, minion_spider add to the counter (spawn event)
======================================================================*/
.float minion_baseattack; // Block base attack within melee range
.float minion_count; // Current active amount of minions
.float minion_maxcount; // Maximum amount of active minions
.float minion_trigger; // Spawn counter for trigger event
.float minion_maxtrigger; // Spawn total for trigger event
.string miniontarget; // targetname string for trigger event
.float minion_throwspeed; // Starting forward speed of ball
.float minion_throwextra; // Extra random forward speed of ball
.float minion_throwside; // Extra sideways momentum (+/-)
.entity minion_test; // Test entity for minion collision
float MIN_MAXACTIVE = 5; // Maximum amount active at once
float MIN_MAXTRIGGER = 5; // How many spawns before triggering
float MIN_THROWSPD = 200; // Monster jump speed
float MIN_THROWRND = 50; // Some randomness
float MIN_THROWSIDE = 50; // Sideways movement (+/-)
void(vector org, vector dir, vector avel, float proj_type) Launch_Grenade;
//----------------------------------------------------------------------
void() setup_minionsupport =
{
// Reset all counters
self.minion_active = TRUE;
self.minion_count = 0;
self.minion_trigger = 0;
// Setup default max counters (check for existing values)
if (!self.minion_maxcount) self.minion_maxcount = MIN_MAXACTIVE;
if (!self.minion_maxtrigger) self.minion_maxtrigger = MIN_MAXTRIGGER;
// Chance to fire original (base) attack of monster
if (!self.minion_baseattack) self.minion_baseattack = TRUE;
// Throw speed of minion ball/egg
if (!self.minion_throwspeed) self.minion_throwspeed = MIN_THROWSPD;
if (!self.minion_throwextra) self.minion_throwextra = MIN_THROWRND;
if (!self.minion_throwside) self.minion_throwside = MIN_THROWSIDE;
};
//----------------------------------------------------------------------
void(entity msource, float countupd) update_minioncount =
{
if (!msource.minion_active) return;
// Add to the active minion counters (check range limits)
msource.minion_count = msource.minion_count + countupd;
if (msource.minion_count < 0) msource.minion_count = 0;
if (msource.minion_count > msource.minion_maxcount)
msource.minion_count = msource.minion_maxcount;
// Check for trigger events (ignore death triggers)
if (countupd > 0) {
msource.minion_trigger = msource.minion_trigger + countupd;
if (msource.minion_trigger >= msource.minion_maxtrigger) {
msource.minion_trigger = 0;
// Check for trigger event string?
if (msource.miniontarget != "") {
trigger_strs(msource.miniontarget, msource);
}
}
}
};
//----------------------------------------------------------------------
// Check if anymore minions can be spawned at the moment
//----------------------------------------------------------------------
float(entity msource) query_minionactive =
{
if (!msource.minion_active) return NEGATIVE;
if (msource.minion_count < msource.minion_maxcount) return TRUE;
else return FALSE;
};
//======================================================================
// Check if there is space to spawn a minion
//
// Version 1 - use a touch trigger to detect players
// Version 2 - use a findradius command to find players
//======================================================================
void() delete_minionspace = { if (self.minion_test) entity_remove(self.minion_test,0.1); }
void() touch_minionspace = { if (other.flags & FL_CLIENT) self.aflag = TRUE; };
//----------------------------------------------------------------------
// Touch trigger version for detecting client/players too close
//----------------------------------------------------------------------
void(vector org, vector minspace, vector maxspace) setup_minionspace =
{
// Create a touch trigger if one does not exist
if (!self.minion_test) self.minion_test = spawn();
// reset all parameters
self.minion_test.movetype = MOVETYPE_NONE;
self.minion_test.solid = SOLID_TRIGGER;
setmodel(self.minion_test, MODEL_EMPTY);
self.minion_test.skin = self.minion_test.frame = 0;
self.minion_test.flags = 0;
// Move touch trigger to correct location
setorigin(self.minion_test, org);
setsize (self.minion_test, minspace, maxspace);
// Enable touch function and reset collision flag
self.minion_test.touch = touch_minionspace;
self.minion_test.aflag = FALSE;
};
float() test_minionspace =
{
// Quick exit if no touch entity exists
if (!self.minion_test) return FALSE;
// Basic world collision test for origin/min/max of spawn
if (pointcontents(self.minion_test.origin) != CONTENT_EMPTY) return FALSE;
if (pointcontents(self.minion_test.origin+'-16 -16 -24') != CONTENT_EMPTY) return FALSE;
if (pointcontents(self.minion_test.origin+'16 16 40') != CONTENT_EMPTY) return FALSE;
// Switch off touch function and check for collision
self.minion_test.touch = SUB_Null;
if (self.minion_test.aflag == TRUE) return FALSE;
else return TRUE;
};
//----------------------------------------------------------------------
// Findradius version for detecting client/players too close
//----------------------------------------------------------------------
float(vector org) find_minionspace =
{
local entity minstuff;
local float foundclient;
// Basic world collision test for origin/min/max of spawn
if (pointcontents(org) != CONTENT_EMPTY) return FALSE;
if (pointcontents(org+'-16 -16 -24') != CONTENT_EMPTY) return FALSE;
if (pointcontents(org+'16 16 40') != CONTENT_EMPTY) return FALSE;
foundclient = TRUE;
// The player can cover crazy amount of distance very fast
// Extend radius out further to catch running players
minstuff = findradius(org, MONAI_RANGESUMMON);
while (minstuff) {
if (minstuff.flags & FL_CLIENT) {
foundclient = FALSE;
minstuff = world;
}
minstuff = minstuff.chain;
}
return foundclient;
};
/*======================================================================
MINION EGGS
* Spawns a projecile (egg) at the player
* Wait for egg to bounce and settle
* Spawn (rotating) minion inside egg and then explode
======================================================================*/
void(vector minion_org, entity minion_targ) minion_spider;
void(vector minion_org, entity minion_targ) minion_scorpion;
void(vector minion_org, entity minion_targ) minion_voreling;
//----------------------------------------------------------------------------
void() Explode_Egg =
{
// Remove egg and throw gibs up as minion grows
setmodel (self, "");
if (self.classgroup == CT_MONWRAITH) self.gib1mdl = MODEL_PROJ_WSHELL;
else if (self.classgroup == CT_MONSHAL) self.gib1mdl = MODEL_PROJ_SSHELL;
self.gib1frame = 9;
self.max_health = MON_GIBFOUNTAIN;
ThrowGib(11,rint(2+random()*4));
// Safely remove egg
self.think = SUB_Remove;
self.nextthink = time + 0.1;
};
//----------------------------------------------------------------------
// Once the egg has landed via Grenade Touch function
// The minion is spawned and the egg is set to explode
//----------------------------------------------------------------------
void() Hatch_Egg =
{
// Block this functions
if (self.state == STATE_DISABLED) return;
// Has the egg been marked for removal?
if (self.touchedvoid) return;
// Its possible for the egg to get stuck, setup death timer
if (!self.gibbed) {
self.gibbed = TRUE;
self.attack_finished = time + 6;
}
// Egg has been stuck for too long, destroy it
if (self.attack_finished < time) {
self.state = STATE_DISABLED;
self.think = Explode_Egg;
self.nextthink = time + 0.1;
return;
}
// Turn off touch function and any movement
self.touch = SUB_Null;
self.movetype = MOVETYPE_NONE;
self.takedamage = DAMAGE_NO;
self.velocity = '0 0 0';
// Check if the egg can hatch? turn on collision
// setup new bounds and extend upwards for testing
setsize (self, VEC_HULLT_MIN, VEC_HULLT_MAX);
self.solid = SOLID_SLIDEBOX;
self.movetype = MOVETYPE_TOSS;
self.oldorigin = self.origin;
self.origin_z = self.origin_z + 8;
droptofloor();
// Can the egg hatch yet? Solid content?
if (!walkmove(0,0) || pointcontents(self.origin) == CONTENT_SOLID) {
self.origin = self.oldorigin;
self.think = Hatch_Egg;
self.nextthink = time + 1 + random();
self.solid = SOLID_NOT; // turn off again
return;
}
// Setup egg ready for minion
self.state = STATE_DISABLED;
// Setup spawn location for minion
traceline (self.origin+'0 0 128', self.origin-'0 0 256', TRUE, world);
self.oldorigin = trace_endpos + '0 0 32';
// Spawn minion inside of egg model
// self.owner is re-assigned to minion to avoid shell gibs interacting
if (self.owner.classtype == CT_MONWRAITH) {
if (self.owner.spawnflags & MON_WRAITH_SCORPIONS) minion_scorpion(self.oldorigin, self.enemy);
else minion_spider(self.oldorigin, self.enemy);
}
else if (self.owner.classtype == CT_MONSHAL) minion_voreling(self.oldorigin, self.enemy);
// Skip a couple of frames
self.think = Explode_Egg;
self.nextthink = time + 0.4;
};
//----------------------------------------------------------------------
// Work out direction speed of egg and then launch it!
//----------------------------------------------------------------------
void(vector eggofs) Create_Egg =
{
local vector org, dir;
local float egg_speed;
if (self.health < 1) return;
// Turn towards player and drop the egg!
makevectors(self.angles);
org = self.origin + attack_vector(eggofs);
egg_speed = self.minion_throwspeed + (random() * self.minion_throwextra);
dir = (v_forward * egg_speed) + (v_right * (crandom()*self.minion_throwside));
Launch_Grenade(org,dir,'0 0 0',CT_PROJ_MEGG);
SUB_AttackFinished (2 + random());
};