]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/monsters/lib/monsters.qc
The first of many bad mistakes (forgot to include the new files)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / lib / monsters.qc
1 // TODO: clean up this file?
2
3 void M_Item_Touch ()
4 {
5         if(self && other.classname == STR_PLAYER && other.deadflag == DEAD_NO)
6         {
7                 Item_Touch();
8                 self.think = SUB_Remove;
9                 self.nextthink = time + 0.1;
10         }
11 }
12
13 void Monster_DropItem (string itype, string itemsize)
14 {
15         if(itype == "0")
16                 return; // someone didnt want an item...
17         vector backuporigin = self.origin + ((self.mins + self.maxs) * 0.5);
18         entity oldself;
19         
20         oldself = self;
21         self = spawn();
22         
23         if (itype == "armor")
24         {
25                 if(itemsize == "large") spawnfunc_item_armor_large();
26                 else if (itemsize == "small") spawnfunc_item_armor_small();
27                 else if (itemsize == "medium") spawnfunc_item_armor_medium();
28                 else print("Invalid monster drop item selected.\n");
29         }
30         else if (itype == "health")
31         {
32                 if(itemsize == "large") spawnfunc_item_health_large();
33                 else if (itemsize == "small") spawnfunc_item_health_small();
34                 else if (itemsize == "medium") spawnfunc_item_health_medium();
35                 else if (itemsize == "mega") spawnfunc_item_health_mega();
36                 else print("Invalid monster drop item selected.\n");
37         }
38         else if (itype == "ammo")
39         {
40                 if(itemsize == "shells") spawnfunc_item_shells();
41                 else if (itemsize == "cells") spawnfunc_item_cells();
42                 else if (itemsize == "bullets") spawnfunc_item_bullets();
43                 else if (itemsize == "rockets") spawnfunc_item_rockets();
44                 else print("Invalid monster drop item selected.\n");
45         }
46         
47         self.velocity = randomvec() * 175 + '0 0 325';
48         
49         self.gravity = 1;
50         self.origin = backuporigin;
51         
52         self.touch = M_Item_Touch;
53         
54         SUB_SetFade(self, time + 5, 1);
55         
56         self = oldself;
57 }
58
59 float monster_isvalidtarget (entity targ, entity ent, float neutral)
60 {
61         if(!targ || !ent)
62                 return FALSE; // this check should fix a crash
63                 
64         if(targ.vehicle_flags & VHF_ISVEHICLE)
65                 targ = targ.vehicle;
66                 
67         if(time < game_starttime)
68                 return FALSE; // monsters do nothing before the match has started
69                 
70         traceline(ent.origin, targ.origin, FALSE, ent);
71         
72         if(vlen(targ.origin - ent.origin) >= 2000)
73                 return FALSE; // enemy is too far away
74
75         if(trace_ent != targ)
76                 return FALSE; // we can't see the enemy
77                 
78         if(neutral == TRUE)
79                 return TRUE; // we come in peace!
80                 
81         if(targ.takedamage == DAMAGE_NO)
82                 return FALSE; // enemy can't be damaged
83                 
84         if(targ.items & IT_INVISIBILITY)
85                 return FALSE; // enemy is invisible
86         
87         if(targ.classname == STR_SPECTATOR || targ.classname == STR_OBSERVER)
88                 return FALSE; // enemy is a spectator
89         
90         if(targ.deadflag != DEAD_NO || ent.deadflag != DEAD_NO || targ.health <= 0 || ent.health <= 0)
91                 return FALSE; // enemy/self is dead
92         
93         if(targ.monster_owner == ent || ent.monster_owner == targ)
94                 return FALSE; // enemy owns us, or we own them
95         
96         if(targ.flags & FL_NOTARGET)
97                 return FALSE; // enemy can't be targetted
98         
99         if not(autocvar_g_monsters_typefrag)
100         if(targ.BUTTON_CHAT)
101                 return FALSE; // no typefragging!
102         
103         if(teamplay)
104         if(targ.team == ent.team)
105                 return FALSE; // enemy is on our team
106         
107         return TRUE;
108 }
109
110 void MonsterTouch ()
111 {
112         if(other == world)
113                 return;
114                 
115         if(self.enemy != other)
116         if(monster_isvalidtarget(other, self, FALSE))
117                 self.enemy = other;
118 }
119
120 void monster_melee (entity targ, float damg, float er, float deathtype)
121 {
122         float bigdmg = 0, rdmg = damg * random();
123
124         if (self.health <= 0)
125                 return;
126         if (targ == world)
127                 return;
128
129         if (vlen(self.origin - targ.origin) > er * self.scale)
130                 return;
131                 
132         bigdmg = rdmg * self.scale;
133         
134         if(random() < 0.01) // critical hit ftw
135                 bigdmg = 200;
136         
137         Damage(targ, self, self, bigdmg * monster_skill, deathtype, targ.origin, normalize(targ.origin - self.origin));
138 }
139
140 void Monster_CheckDropCvars (string mon)
141 {
142         string dropitem;
143         string dropsize;
144         
145         dropitem = cvar_string(strcat("g_monster_", mon, "_drop"));
146         dropsize = cvar_string(strcat("g_monster_", mon, "_drop_size"));
147         
148         monster_dropitem = dropitem;
149         monster_dropsize = dropsize;
150         MUTATOR_CALLHOOK(MonsterDropItem);
151         dropitem = monster_dropitem;
152         dropsize = monster_dropsize;
153         
154         if(autocvar_g_monsters_forcedrop)
155                 Monster_DropItem(autocvar_g_monsters_drop_type, autocvar_g_monsters_drop_size);
156         else if(dropitem != "")
157                 Monster_DropItem(dropitem, dropsize);      
158         else
159                 Monster_DropItem("armor", "medium");
160 }
161
162 void ScaleMonster (float scle)
163 {
164         // this should prevent monster from falling through floor when scale changes
165         self.scale = scle;
166         setorigin(self, self.origin + ('0 0 30' * scle));
167 }
168
169 void Monster_CheckMinibossFlag ()
170 {
171         if(MUTATOR_CALLHOOK(MonsterCheckBossFlag))
172                 return;
173                 
174         float healthboost = autocvar_g_monsters_miniboss_healthboost;
175         float r = random() * 4;
176
177         // g_monsters_miniboss_chance cvar or spawnflags 64 causes a monster to be a miniboss
178         if ((self.spawnflags & MONSTERFLAG_MINIBOSS) || (random() * 100 < autocvar_g_monsters_miniboss_chance))
179         {
180                 if (r < 2 || self.team == COLOR_TEAM2)
181                 {
182                         self.strength_finished = -1;  
183                         healthboost *= monster_skill;
184                         self.effects |= (EF_FULLBRIGHT | EF_BLUE);
185                 }
186                 else if (r >= 1 || self.team == COLOR_TEAM1)
187                 {
188                         self.invincible_finished = -1;
189                         healthboost *= bound(0.5, monster_skill, 1.5);
190                         self.effects |= (EF_FULLBRIGHT | EF_RED);
191                 }
192                 self.health += healthboost;
193                 self.cnt += 20;
194                 ScaleMonster(1.5);
195                 self.flags |= MONSTERFLAG_MINIBOSS;
196                 if(teamplay && autocvar_g_monsters_teams)
197                         return;
198                 do
199                 {
200                         self.colormod_x = random();
201                         self.colormod_y = random();
202                         self.colormod_z = random();
203                         self.colormod = normalize(self.colormod);
204                 }
205                 while (self.colormod_x > 0.6 && self.colormod_y > 0.6 && self.colormod_z > 0.6);
206         }
207 }
208
209 void Monster_Fade ()
210 {
211         if not(self.spawnflags & MONSTERFLAG_NORESPAWN)
212         if(autocvar_g_monsters_respawn)
213         {
214                 self.monster_respawned = TRUE;
215                 setmodel(self, "");
216                 self.think = self.monster_spawnfunc;
217                 self.nextthink = time + autocvar_g_monsters_respawn_delay;
218                 setorigin(self, self.pos1);
219                 self.angles = self.pos2;
220                 self.health = 0;
221                 return;
222         }
223         self.think = SUB_Remove;
224         self.nextthink = time + 4;
225         SUB_SetFade(self, time + 3, 1);
226 }
227
228 float Monster_CanJump (vector vel)
229 {
230         local vector old = self.velocity;
231         
232         self.velocity = vel;
233         tracetoss(self, self);
234         self.velocity = old;
235         if (trace_ent != self.enemy)
236                 return FALSE;
237
238         return TRUE;
239 }
240
241 float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished)
242 {
243         if not(self.flags & FL_ONGROUND)
244                 return FALSE;
245         if(self.health < 1)
246                 return FALSE; // called when dead?
247         if not(Monster_CanJump(vel))
248                 return FALSE;
249                 
250         self.frame = anm;
251         self.state = MONSTER_STATE_ATTACK_LEAP;
252         self.touch = touchfunc;
253         self.origin_z += 1;
254         self.velocity = vel;
255         if (self.flags & FL_ONGROUND)
256                 self.flags -= FL_ONGROUND;
257                 
258         self.attack_finished_single = time + anim_finished;
259         
260         return TRUE;
261 }
262
263 float GenericCheckAttack ()
264 {
265         // checking attack while dead?
266         if (self.health <= 0 || self.enemy == world)
267                 return FALSE;
268                 
269         if(self.monster_delayedattack && self.delay != -1)
270         {
271                 if(time < self.delay)
272                         return FALSE;
273                         
274                 self.monster_delayedattack();
275         }
276         
277         if (time < self.attack_finished_single)
278                 return FALSE;
279         
280         if (enemy_range() > 2000) // long traces are slow
281                 return FALSE;   
282                 
283         if(self.attack_melee)
284         if(enemy_range() <= 100 * self.scale)
285         {
286                 self.attack_melee(); // don't wait for nextthink - too slow
287                 return TRUE;
288         }
289         
290         // monster doesn't have a ranged attack function, so stop here
291         if(!self.attack_ranged)
292                 return FALSE;
293
294         // see if any entities are in the way of the shot
295         if (!findtrajectorywithleading(self.origin, '0 0 0', '0 0 0', self.enemy, 800, 0, 2.5, 0, self))
296                 return FALSE;
297
298         self.attack_ranged(); // don't wait for nextthink - too slow
299         return TRUE;
300 }
301
302 void monster_use ()
303 {
304         if (self.enemy)
305                 return;
306         if (self.health <= 0)
307                 return;
308
309         if(!monster_isvalidtarget(activator, self, -1))
310                 return;
311
312         self.enemy = activator;
313 }
314
315 float trace_path(vector from, vector to)
316 {
317         vector dir = normalize(to - from) * 15, offset = '0 0 0';
318         float trace1 = trace_fraction;
319         
320         offset_x = dir_y;
321         offset_y = -dir_x;
322         traceline (from+offset, to+offset, TRUE, self);
323         
324         traceline(from-offset, to-offset, TRUE, self);
325                 
326         return ((trace1 < trace_fraction) ? trace1 : trace_fraction);
327 }
328
329 vector monster_pickmovetarget(entity targ)
330 {
331         // enemy is always preferred target
332         if(self.enemy && trace_path(self.origin + '0 0 10', self.enemy.origin + '0 0 10') > 0.99)
333                 return self.enemy.origin + 60 * normalize(self.enemy.origin - self.origin);
334         
335         switch(self.monster_moveflags)
336         {
337                 case MONSTER_MOVE_OWNER:
338                 {
339                         if(self.monster_owner && self.monster_owner.classname != "monster_swarm" && trace_path(self.origin + '0 0 10', self.monster_owner.origin + '0 0 10') > 0.99)
340                                 return self.monster_owner.origin;
341                 }
342                 case MONSTER_MOVE_WANDER:
343                 {
344                         if(targ)
345                                 return targ.origin;
346                                 
347                         self.angles_y = random() * 500;
348                         makevectors(self.angles);
349                         return self.origin + v_forward * 600;
350                 }
351                 case MONSTER_MOVE_SPAWNLOC:
352                         return self.pos1;
353                 default:
354                 case MONSTER_MOVE_NOMOVE:
355                         return self.origin;
356         }
357 }
358
359 .float last_trace;
360 .float breath_checks;
361 void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle)
362 {
363         if(self.target)
364                 self.goalentity = find(world, targetname, self.target);
365                 
366         float l = vlen(self.moveto - self.origin);
367         float t1 = trace_path(self.origin+'0 0 10', self.moveto+'0 0 10');
368         float t2 = trace_path(self.origin-'0 0 15', self.moveto-'0 0 15'); 
369         entity targ = self.goalentity;
370
371         if(self.frozen)
372         {
373                 self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
374                 self.health = max(1, self.revive_progress * self.max_health);
375                 
376                 if(self.sprite)
377                 {
378                         WaypointSprite_UpdateHealth(self.sprite, self.health);
379                 }
380                         
381                 self.velocity = '0 0 0';
382                 self.enemy = world;
383                 if(self.revive_progress >= 1)
384                         Unfreeze(self); // wait for next think before attacking
385                 self.nextthink = time + 0.1;
386                         
387                 return; // no moving while frozen
388         }
389         
390         if(self.flags & FL_SWIM)
391         {
392                 if(self.waterlevel < WATERLEVEL_WETFEET)
393                 {
394                         self.breath_checks += 1;
395                         self.angles = '0 0 -90';
396                         if(self.breath_checks == 25)
397                         {
398                                 if not(self.flags & FL_ONGROUND)
399                                         self.flags |= FL_ONGROUND;
400                                 self.monster_die();
401                                 if(self.realowner.flags & FL_CLIENT)
402                                         self.realowner.monstercount -= 1;
403                                 //if(!(self.spawnflags & MONSTERFLAG_SPAWNED) && !self.monster_respawned)
404                                         //monsters_killed += 1;
405                                 self.movetype = MOVETYPE_TOSS;
406                                 return;
407                         }
408                         if(random() < 0.5)
409                         {
410                                 self.velocity_y += random() * 50;
411                                 self.velocity_x -= random() * 50;
412                         }
413                         else
414                         {
415                                 self.velocity_y -= random() * 50;
416                                 self.velocity_x += random() * 50;
417                         }
418                         self.velocity_z += random()*150;
419                         if (self.flags & FL_ONGROUND)
420                                 self.flags -= FL_ONGROUND;
421                         self.movetype = MOVETYPE_BOUNCE;
422                         self.velocity_z = -200;
423                         return;
424                 }
425                 else
426                 {
427                         self.angles = '0 0 0';
428                         self.movetype = MOVETYPE_WALK;
429                         self.breath_checks = 0;
430                 }
431         }
432         
433         if(gameover || time < game_starttime)
434         {
435                 runspeed = walkspeed = 0;
436                 self.frame = manim_idle;
437                 movelib_beak_simple(stopspeed);
438                 return;
439         }
440         
441         runspeed *= monster_skill;
442         walkspeed *= monster_skill;
443         
444         monster_target = targ;
445         monster_speed_run = runspeed;
446         monster_speed_walk = walkspeed;
447         MUTATOR_CALLHOOK(MonsterMove);
448         targ = monster_target;
449         runspeed = monster_speed_run;
450         walkspeed = monster_speed_walk;
451                 
452         if(IsDifferentTeam(self.monster_owner, self))
453                 self.monster_owner = world;
454         
455         if(self.enemy.health <= 0 || (!autocvar_g_monsters_typefrag && self.enemy.BUTTON_CHAT))
456                 self.enemy = world;
457                 
458         if not(self.enemy)
459                 self.enemy = FindTarget(self);
460                 
461         if(time >= self.last_trace)
462         {
463                 if(self.monster_moveflags & MONSTER_MOVE_WANDER)
464                         self.last_trace = time + 2;
465                 else
466                         self.last_trace = time + 0.5;
467                 self.moveto = monster_pickmovetarget(targ);
468         }
469         
470         vector angles_face = vectoangles(self.moveto - self.origin);
471         self.angles_y = angles_face_y;
472         
473         if(self.state == MONSTER_STATE_ATTACK_LEAP && (self.flags & FL_ONGROUND))
474         {
475                 self.state = 0;
476                 self.touch = MonsterTouch;
477         }
478          
479         v_forward = normalize(self.moveto - self.origin);
480         
481         if(t1*l-t2*l>50 && (t1*l > 100 || t1 > 0.8))
482         if(self.flags & FL_ONGROUND)
483                 movelib_jump_simple(100);
484
485         if(vlen(self.moveto - self.origin) > 64)
486         {
487                 if(self.flags & FL_FLY)
488                         movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
489                 else
490                         movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
491                 if(time > self.pain_finished)
492                         if(time > self.attack_finished_single)
493                                 self.frame = ((self.enemy) ? manim_run : manim_walk);
494         }
495         else
496         {
497                 movelib_beak_simple(stopspeed);
498                         if(time > self.attack_finished_single)
499                                 if(time > self.pain_finished)
500                                         if (vlen(self.velocity) <= 30)
501                                                 self.frame = manim_idle;
502         }
503                 
504         if(self.enemy)
505         {
506                 if(!self.checkattack)
507                         return; // to stop other code from crashing here
508                         
509                 self.checkattack();
510         }
511 }
512
513 void monsters_setstatus()
514 {
515         self.stat_monsters_total = monsters_total;
516         self.stat_monsters_killed = monsters_killed;
517 }
518
519
520 /*
521 ===================
522
523 Monster spawn code
524
525 ===================
526 */
527
528 void Monster_Appear ()
529 {
530         self.enemy = activator;
531         self.spawnflags &~= MONSTERFLAG_APPEAR;
532         self.monster_spawnfunc();
533 }
534
535 entity FindTarget (entity ent) 
536 {
537         if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return self.goalentity; } // Handled by a mutator
538         local entity e;
539         for(e = world; (e = findflags(e, monster_attack, TRUE)); ) 
540         {
541                 if(monster_isvalidtarget(e, ent, FALSE))
542                 {
543                         return e;
544                 }
545         }
546         return world;
547 }
548
549 void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
550 {
551         if(self.frozen)
552                 return;
553                 
554         if(monster_isvalidtarget(attacker, self, FALSE))
555                 self.enemy = attacker;
556         
557         self.health -= damage;
558         
559         if(self.sprite)
560         {
561                 WaypointSprite_UpdateHealth(self.sprite, self.health);
562         }
563                 
564         self.dmg_time = time;
565
566         if(sound_allowed(MSG_BROADCAST, attacker))
567                 spamsound (self, CH_PAIN, "misc/bodyimpact1.wav", VOL_BASE, ATTN_NORM);  // FIXME: PLACEHOLDER
568         
569         if(self.damageforcescale < 1 && self.damageforcescale > 0)
570                 self.velocity += force * self.damageforcescale;
571         else
572                 self.velocity += force;
573                 
574         Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
575         if (damage > 50)
576                 Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, self, attacker);
577         if (damage > 100)
578                 Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, self, attacker);
579                 
580         if(self.health <= 0)
581         {        
582                 if(self.sprite)
583                 {
584                         // Update one more time to avoid waypoint fading without emptying healthbar
585                         WaypointSprite_UpdateHealth(self.sprite, 0);
586                 }
587                 
588                 if(self.flags & MONSTERFLAG_MINIBOSS) // TODO: cvarise the weapon drop?
589                         W_ThrowNewWeapon(self, WEP_NEX, 0, self.origin, self.velocity);
590                         
591                 activator = attacker;
592                 other = self.enemy;
593                 self.target = self.target2;
594                 self.target2 = "";
595                 SUB_UseTargets();
596         
597                 self.monster_die();     
598         }
599 }
600
601 // used to hook into monster post death functions without a mutator
602 void monster_hook_death()
603 {
604         if(self.sprite)
605         WaypointSprite_Kill(self.sprite);
606                 
607         if(self.realowner.flags & FL_CLIENT)
608                 self.realowner.monstercount -= 1;
609                 
610         if(!(self.spawnflags & MONSTERFLAG_SPAWNED) && !self.monster_respawned)
611                 monsters_killed += 1;
612                 
613         if(self.realowner.flags & FL_CLIENT)
614                         self.realowner.monstercount -= 1;
615                 
616         totalspawned -= 1;
617         
618         MUTATOR_CALLHOOK(MonsterDies);
619 }
620
621 // used to hook into monster post spawn functions without a mutator
622 void monster_hook_spawn()
623 {
624         self.health *= monster_skill; // skill based monster health?
625         self.max_health = self.health;
626         
627         if(teamplay && autocvar_g_monsters_teams)
628         {
629                 self.colormod = TeamColor(self.team);
630                 self.monster_attack = TRUE;
631         }
632         
633         if (self.target)
634         {
635                 self.target2 = self.target;
636                 self.goalentity = find(world, targetname, self.target);
637         }
638                 
639         if(autocvar_g_monsters_healthbars)
640         {
641                 WaypointSprite_Spawn(self.netname, 0, 600, self, '0 0 1' * self.sprite_height, world, 0, self, sprite, FALSE, RADARICON_DANGER, ((teamplay) ? TeamColor(self.team) : '1 0 0')); 
642                 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
643                 WaypointSprite_UpdateHealth(self.sprite, self.health);
644         }
645         
646         MUTATOR_CALLHOOK(MonsterSpawn);
647 }
648
649 float monster_initialize(string  net_name,
650                                                  string  bodymodel,
651                                                  vector  min_s,
652                                                  vector  max_s,
653                                                  float   nodrop,
654                                                  void() dieproc,
655                                                  void() spawnproc)
656 {
657         if not(autocvar_g_monsters)
658                 return FALSE;
659                 
660         // support for quake style removing monsters based on skill
661         if(autocvar_skill <= autocvar_g_monsters_skill_easy && (self.spawnflags & MONSTERSKILL_NOTEASY)) { return FALSE; }
662         else if(autocvar_skill == autocvar_g_monsters_skill_normal && (self.spawnflags & MONSTERSKILL_NOTMEDIUM)) { return FALSE; }
663         else if(autocvar_skill == autocvar_g_monsters_skill_hard && (self.spawnflags & MONSTERSKILL_NOTHARD)) { return FALSE; }
664         else if(autocvar_skill == autocvar_g_monsters_skill_insane && (self.spawnflags & MONSTERSKILL_NOTINSANE)) { return FALSE; }
665         else if(autocvar_skill >= autocvar_g_monsters_skill_nightmare && (self.spawnflags & MONSTERSKILL_NOTNIGHTMARE)) { return FALSE; }
666
667         if(self.model == "")
668         if(bodymodel == "")
669                 error("monsters: missing bodymodel!");
670
671         if(self.netname == "")
672         {
673                 if(net_name != "" && self.realowner.classname == STR_PLAYER)
674                         net_name = strzone(strdecolorize(sprintf("%s's %s", self.realowner.netname, net_name)));
675                 self.netname = ((net_name == "") ? self.classname : net_name);
676         }
677         
678         if(self.spawnflags & MONSTERFLAG_GIANT && !autocvar_g_monsters_nogiants)
679                 ScaleMonster(5);
680         else if(!self.scale)
681                 ScaleMonster(1);
682         else
683                 ScaleMonster(self.scale);
684                 
685         Monster_CheckMinibossFlag();
686                 
687         min_s *= self.scale;
688         max_s *= self.scale;
689
690         if(self.team && !teamplay)
691                 self.team = 0;
692
693         self.flags = FL_MONSTER;
694         
695         if(self.model != "")
696                 bodymodel = self.model;
697                 
698         if not(self.spawnflags & MONSTERFLAG_SPAWNED) // naturally spawned monster
699         if not(self.monster_respawned)
700                 monsters_total += 1;
701         
702         precache_model(bodymodel);
703
704         setmodel(self, bodymodel);
705         
706         setsize(self, min_s, max_s);
707
708         self.takedamage                 = DAMAGE_AIM;
709         self.bot_attack                 = TRUE;
710         self.iscreature                 = TRUE;
711         self.teleportable               = TRUE;
712         self.damagedbycontents  = TRUE;
713         self.damageforcescale   = 0.003;
714         self.monster_die                = dieproc;
715         self.event_damage               = monsters_damage;
716         self.touch                              = MonsterTouch;
717         self.use                                = monster_use;
718         self.solid                              = SOLID_BBOX;
719         self.movetype                   = MOVETYPE_WALK;
720         self.delay                              = -1; // used in attack delay code
721         monsters_spawned           += 1;
722         self.think                              = spawnproc;
723         self.nextthink                  = time;
724         self.enemy                              = world;
725         self.velocity                   = '0 0 0';
726         self.moveto                             = self.origin;
727         self.pos1                               = self.origin;
728         self.pos2                               = self.angles;
729         
730         if not(self.monster_moveflags)
731                 self.monster_moveflags = MONSTER_MOVE_WANDER;
732
733         if(autocvar_g_nodepthtestplayers)
734                 self.effects |= EF_NODEPTHTEST;
735
736         if(autocvar_g_fullbrightplayers)
737                 self.effects |= EF_FULLBRIGHT;
738
739         if not(nodrop)
740         {
741                 setorigin(self, self.origin);
742                 tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
743                 setorigin(self, trace_endpos);
744         }
745
746         return TRUE;
747 }