]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/monsters/m_monsters.qc
Initial checkout of Vore Tournament 0.1.alpha.
[voretournament/voretournament.git] / data / qcsrc / server / monsters / m_monsters.qc
1 /* ALL MONSTERS SHOULD BE 1 0 0 IN COLOR */\r
2 \r
3 // name =[framenum,     nexttime, nextthink] {code}\r
4 // expands to:\r
5 // name ()\r
6 // {\r
7 //              self.frame=framenum;\r
8 //              self.nextthink = time + nexttime;\r
9 //              self.think = nextthink\r
10 //              <code>\r
11 // };\r
12 \r
13 .float ismonster;\r
14 \r
15 .float modelindex2;\r
16 \r
17 /*\r
18 ================\r
19 monster_use\r
20 \r
21 Using a monster makes it angry at the current activator\r
22 LordHavoc: using a monster with the spawnflag 'Appear' makes it appear\r
23 ================\r
24 */\r
25 void() monster_use =\r
26 {\r
27         if (self.enemy)\r
28                 return;\r
29         if (self.health < 1)\r
30                 return;\r
31         if (self.mdl)\r
32         if (self.spawnflags & MONSTER_APPEAR)\r
33         {\r
34                 self.nextthink = time + 0.1;\r
35                 self.spawnflags = self.spawnflags - MONSTER_APPEAR;\r
36                 self.solid = SOLID_SLIDEBOX;\r
37                 self.takedamage = DAMAGE_AIM;\r
38                 //self.movetype = MOVETYPE_STEP;\r
39                 self.model = self.mdl;\r
40                 self.mdl = "";\r
41                 self.modelindex = self.modelindex2;\r
42                 self.modelindex2 = 0;\r
43                 //setorigin(self, self.origin + '0 0 1');\r
44                 spawn_tdeath(self.origin, self, self.origin);\r
45                 return;\r
46         }\r
47 \r
48 #if 0\r
49         if (activator.items & IT_INVISIBILITY)\r
50                 return;\r
51 #endif\r
52         if (activator.flags & FL_NOTARGET)\r
53                 return;\r
54         if (activator.classname != "player")\r
55                 return;\r
56 \r
57         // delay reaction so if the monster is teleported, its sound is still heard\r
58         self.enemy = activator;\r
59         self.nextthink = time + 0.1;\r
60         self.think = FoundTarget;\r
61 };\r
62 \r
63 void() monster_appearsetup =\r
64 {\r
65         if ((self.spawnflags & MONSTER_APPEAR) == 0)\r
66                 return;\r
67         self.mdl = self.model;\r
68         self.modelindex2 = self.modelindex;\r
69         self.modelindex = 0;\r
70         self.solid = SOLID_NOT;\r
71         self.takedamage = DAMAGE_NO;\r
72         //self.movetype = MOVETYPE_NONE;\r
73         self.nextthink = -1;\r
74         self.model = "";\r
75 };\r
76 \r
77 /*\r
78 ================\r
79 monster_setalpha\r
80 \r
81 Sets relative alpha of monster in skill 4 mode.\r
82 ================\r
83 */\r
84 void(float a) monster_setalpha =\r
85 {\r
86         if (skill < 4 || self.classname == "monster_hellfish")\r
87         {\r
88                 self.alpha = 1.0;\r
89                 return;\r
90         }\r
91 \r
92         if (skill >= 5)\r
93         {\r
94                 // randomly forget enemy, this makes monsters randomly return to their normal ghostlike state\r
95                 if (a == 0)\r
96                 if (self.enemy)\r
97                 if (random() < 0.1)\r
98                         self.enemy = world;\r
99                 // randomly blink (playing the same alarming sound as if attacking)\r
100                 if (self.enemy == world)\r
101                 {\r
102                         a = 0;\r
103                         if (time >= 0.3) // don't blink during the init process because it might become permanent\r
104                         if (random() < 0.005)\r
105                         {\r
106                                 // blink for an instant, this causes the appear sound, alarming the player as if under attack\r
107                                 sound(self, CHAN_AUTO, "wizard/wsight.wav", 1, ATTN_NORM);\r
108                                 a = 1;\r
109                         }\r
110                 }\r
111                 // if ghosted, become non-solid and immune to damage\r
112                 if (a <= 0 || self.enemy == world)\r
113                 {\r
114                         self.solid = SOLID_NOT;\r
115                         self.takedamage = DAMAGE_NO;\r
116                 }\r
117                 else\r
118                 {\r
119                         // if unghosting, make sure we have an enemy, otherwise stay ghosted (even if blinking) so we can't be shot while blinking\r
120                         if (self.solid != SOLID_SLIDEBOX)\r
121                                 sound(self, CHAN_AUTO, "wizard/wsight.wav", 1, ATTN_NORM);\r
122                         self.solid = SOLID_SLIDEBOX;\r
123                         self.takedamage = DAMAGE_AIM;\r
124                 }\r
125         }\r
126         self.alpha = SKILL4_MINALPHA + (1 - SKILL4_MINALPHA) * bound(0, a, 1);\r
127 };\r
128 \r
129 /*\r
130 ================\r
131 monster_death_use\r
132 \r
133 When a mosnter dies, it fires all of its targets with the current\r
134 enemy as activator.\r
135 ================\r
136 */\r
137 void() monster_death_use =\r
138 {\r
139 // fall to ground\r
140         if (self.flags & FL_FLY)\r
141                 self.flags = self.flags - FL_FLY;\r
142         if (self.flags & FL_SWIM)\r
143                 self.flags = self.flags - FL_SWIM;\r
144 \r
145         if (!self.target)\r
146                 return;\r
147 \r
148         activator = self.enemy;\r
149         SUB_UseTargets ();\r
150 };\r
151 \r
152 \r
153 void() monsterinwall =\r
154 {\r
155         local entity e;\r
156         if (!cvar("developer"))\r
157                 return;\r
158         // this is handy for level designers,\r
159         // puts a spikey ball where the error is...\r
160         e = spawn();\r
161         setorigin(e, self.origin);\r
162         setmodel (e, "models/ebomb.mdl");\r
163         e.movetype = MOVETYPE_NONE;\r
164         e.solid = SOLID_NOT;\r
165         e.think = SUB_Null;\r
166         e.nextthink = -1;\r
167         e.scale = 16;\r
168 };\r
169 \r
170 //============================================================================\r
171 \r
172 void() walkmonster_start_go =\r
173 {\r
174         self.origin_z = self.origin_z + 1; // raise off floor a bit\r
175 \r
176         tracebox(self.origin, self.mins, self.maxs, self.origin, TRUE, self);\r
177         if (trace_startsolid)\r
178         {\r
179                 dprint("walkmonster in wall at: ");\r
180                 dprint(vtos(self.origin));\r
181                 dprint("\n");\r
182                 monsterinwall();\r
183                 droptofloor();\r
184         }\r
185         else\r
186         {\r
187                 droptofloor();\r
188                 if (!walkmove(0,0))\r
189                 {\r
190                         dprint("walkmonster in wall at: ");\r
191                         dprint(vtos(self.origin));\r
192                         dprint("\n");\r
193                         monsterinwall();\r
194                 }\r
195         }\r
196 \r
197         //self.cantrigger = TRUE;\r
198 \r
199         self.takedamage = DAMAGE_AIM;\r
200 \r
201         self.ideal_yaw = self.angles * '0 1 0';\r
202         if (!self.yaw_speed)\r
203                 self.yaw_speed = 20;\r
204         self.view_ofs = '0 0 25';\r
205         self.use = monster_use;\r
206 \r
207         self.flags = self.flags | FL_MONSTER;\r
208 \r
209         if (monsterwander)\r
210                 self.spawnflags = self.spawnflags | MONSTER_WANDER;\r
211 \r
212         if (self.target)\r
213         {\r
214                 self.goalentity = self.movetarget = find(world, targetname, self.target);\r
215                 self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r
216                 if (!self.movetarget)\r
217                 {\r
218                         dprint("Monster can't find target at ");\r
219                         dprint(vtos(self.origin));\r
220                         dprint("\n");\r
221                 }\r
222                 // this used to be an objerror\r
223                 if (self.movetarget.classname == "path_corner")\r
224                         self.th_walk ();\r
225                 else\r
226                 {\r
227                         if ((self.spawnflags & MONSTER_WANDER) && (!self.monsterawaitingteleport) && (self.spawnflags & 3) == 0 && (world.model != "maps/e1m7.bsp"))\r
228                         {\r
229                                 monster_spawnwanderpath();\r
230                                 self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r
231                                 self.th_walk ();\r
232                         }\r
233                         else\r
234                         {\r
235                                 self.pausetime = 99999999;\r
236                                 self.th_stand ();\r
237                         }\r
238                 }\r
239         }\r
240         else\r
241         {\r
242                 if ((self.spawnflags & MONSTER_WANDER) && (!self.monsterawaitingteleport) && (self.spawnflags & 3) == 0 && (world.model != "maps/e1m7.bsp"))\r
243                 {\r
244                         monster_spawnwanderpath();\r
245                         self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r
246                         self.th_walk ();\r
247                 }\r
248                 else\r
249                 {\r
250                         self.pausetime = 99999999;\r
251                         self.th_stand ();\r
252                 }\r
253         }\r
254 \r
255 // spread think times so they don't all happen at same time\r
256         self.nextthink = self.nextthink + random()*0.5 + 0.1;\r
257         self.iscreature = TRUE;\r
258 \r
259         force_retouch = 2; // mainly to detect teleports\r
260 \r
261         monster_appearsetup();\r
262 };\r
263 \r
264 \r
265 void() walkmonster_start =\r
266 {\r
267         self.candrown = 1; // this is turned off by some monsters like zombies\r
268         // delay drop to floor to make sure all doors have been spawned\r
269         // spread think times so they don't all happen at same time\r
270         self.nextthink = time + random()*0.5 + 0.3;\r
271         self.think = walkmonster_start_go;\r
272         total_monsters = total_monsters + 1;\r
273         self.bot_attack = TRUE;\r
274         self.frags = 2; // actually just used to get havocbots to attack it...\r
275         self.bleedfunc = genericbleedfunc;\r
276         self.ismonster = TRUE;\r
277 \r
278         monster_setalpha (0);\r
279 };\r
280 \r
281 \r
282 \r
283 void() flymonster_start_go =\r
284 {\r
285         self.takedamage = DAMAGE_AIM;\r
286 \r
287         self.ideal_yaw = self.angles * '0 1 0';\r
288         if (!self.yaw_speed)\r
289                 self.yaw_speed = 10;\r
290         self.view_ofs = '0 0 25';\r
291         self.use = monster_use;\r
292 \r
293         self.flags = self.flags | FL_FLY;\r
294         self.flags = self.flags | FL_MONSTER;\r
295 \r
296         if (!walkmove(0,0))\r
297         {\r
298                 dprint("flymonster in wall at: ");\r
299                 dprint(vtos(self.origin));\r
300                 dprint("\n");\r
301                 monsterinwall();\r
302         }\r
303 \r
304         //self.cantrigger = TRUE;\r
305 \r
306         if (monsterwander)\r
307                 self.spawnflags = self.spawnflags | MONSTER_WANDER;\r
308 \r
309         if (self.target)\r
310         {\r
311                 self.goalentity = self.movetarget = find(world, targetname, self.target);\r
312                 if (!self.movetarget)\r
313                 {\r
314                         dprint("Monster can't find target at ");\r
315                         dprint(vtos(self.origin));\r
316                         dprint("\n");\r
317                 }\r
318                 // this used to be an objerror\r
319                 if (self.movetarget.classname == "path_corner")\r
320                         self.th_walk ();\r
321                 else\r
322                 {\r
323                         if ((self.spawnflags & MONSTER_WANDER) && (!self.monsterawaitingteleport) && (self.spawnflags & 3) == 0 && (world.model != "maps/e1m7.bsp"))\r
324                         {\r
325                                 monster_spawnwanderpath();\r
326                                 self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r
327                                 self.th_walk ();\r
328                         }\r
329                         else\r
330                         {\r
331                                 self.pausetime = 99999999;\r
332                                 self.th_stand ();\r
333                         }\r
334                 }\r
335         }\r
336         else\r
337         {\r
338                 if ((self.spawnflags & MONSTER_WANDER) && (!self.monsterawaitingteleport) && (self.spawnflags & 3) == 0 && (world.model != "maps/e1m7.bsp"))\r
339                 {\r
340                         monster_spawnwanderpath();\r
341                         self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r
342                         self.th_walk ();\r
343                 }\r
344                 else\r
345                 {\r
346                         self.pausetime = 99999999;\r
347                         self.th_stand ();\r
348                 }\r
349         }\r
350         self.iscreature = TRUE;\r
351 \r
352         force_retouch = 2; // mainly to detect teleports\r
353 \r
354         monster_appearsetup();\r
355 };\r
356 \r
357 void() flymonster_start =\r
358 {\r
359         self.candrown = 1;\r
360         // spread think times so they don't all happen at same time\r
361         self.nextthink = time + random()*0.5 + 0.1;\r
362         self.think = flymonster_start_go;\r
363         total_monsters = total_monsters + 1;\r
364         self.bot_attack = TRUE;\r
365         self.frags = 2; // actually just used to get havocbots to attack it...\r
366         self.bleedfunc = genericbleedfunc;\r
367         self.ismonster = TRUE;\r
368 \r
369         monster_setalpha (0);\r
370 };\r
371 \r
372 \r
373 void() swimmonster_start_go =\r
374 {\r
375         if (deathmatch)\r
376         {\r
377                 remove(self);\r
378                 return;\r
379         }\r
380 \r
381         //self.cantrigger = TRUE;\r
382 \r
383         self.takedamage = DAMAGE_AIM;\r
384 \r
385         self.ideal_yaw = self.angles * '0 1 0';\r
386         if (!self.yaw_speed)\r
387                 self.yaw_speed = 10;\r
388         self.view_ofs = '0 0 10';\r
389         self.use = monster_use;\r
390 \r
391         self.flags = self.flags | FL_SWIM;\r
392         self.flags = self.flags | FL_MONSTER;\r
393 \r
394         if (monsterwander)\r
395                 self.spawnflags = self.spawnflags | MONSTER_WANDER;\r
396 \r
397         if (self.target)\r
398         {\r
399                 self.goalentity = self.movetarget = find(world, targetname, self.target);\r
400                 if (!self.movetarget)\r
401                 {\r
402                         dprint("Monster can't find target at ");\r
403                         dprint(vtos(self.origin));\r
404                         dprint("\n");\r
405                 }\r
406                 // this used to be an objerror\r
407                 if (self.movetarget.classname == "path_corner")\r
408                         self.th_walk ();\r
409                 else\r
410                 {\r
411                         if ((self.spawnflags & MONSTER_WANDER) && (!self.monsterawaitingteleport) && (self.spawnflags & 3) == 0 && (world.model != "maps/e1m7.bsp"))\r
412                         {\r
413                                 monster_spawnwanderpath();\r
414                                 self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r
415                                 self.th_walk ();\r
416                         }\r
417                         else\r
418                         {\r
419                                 self.pausetime = 99999999;\r
420                                 self.th_stand ();\r
421                         }\r
422                 }\r
423         }\r
424         else\r
425         {\r
426                 if ((self.spawnflags & MONSTER_WANDER) && (!self.monsterawaitingteleport) && (self.spawnflags & 3) == 0 && (world.model != "maps/e1m7.bsp"))\r
427                 {\r
428                         monster_spawnwanderpath();\r
429                         self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r
430                         self.th_walk ();\r
431                 }\r
432                 else\r
433                 {\r
434                         self.pausetime = 99999999;\r
435                         self.th_stand ();\r
436                 }\r
437         }\r
438         self.iscreature = TRUE;\r
439 \r
440         force_retouch = 2; // mainly to detect teleports\r
441 \r
442         monster_appearsetup();\r
443 };\r
444 \r
445 void() swimmonster_start =\r
446 {\r
447         // spread think times so they don't all happen at same time\r
448         self.candrown = 0;\r
449         self.nextthink = time + random()*0.5 + 0.1;\r
450         self.think = swimmonster_start_go;\r
451         total_monsters = total_monsters + 1;\r
452         self.bot_attack = TRUE;\r
453         self.frags = 2; // actually just used to get havocbots to attack it...\r
454         self.bleedfunc = genericbleedfunc;\r
455         self.ismonster = TRUE;\r
456 \r
457         monster_setalpha(0);\r
458 };\r
459 \r
460 void(vector org, float bodydamage, float armordamage, vector force, float damgtype) genericbleedfunc =\r
461 {\r
462         local vector v;\r
463         v = '0 0 0' - force * 0.05;\r
464         if (armordamage > 0)\r
465                 te_spark(org, v, armordamage * 3);\r
466         if (bodydamage > 0)\r
467                 te_blood(org, v, bodydamage);\r
468 }\r