]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/monsters/monster/wizard.qc
Fix TD monster spawn protection. Improve TD monster pathfinding
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / monster / wizard.qc
1 // size
2 const vector WIZARD_MIN = '-16 -16 -24';
3 const vector WIZARD_MAX = '16 16 24';
4
5 // cvars
6 float autocvar_g_monster_wizard;
7 float autocvar_g_monster_wizard_health;
8 float autocvar_g_monster_wizard_speed_walk;
9 float autocvar_g_monster_wizard_speed_run;
10 float autocvar_g_monster_wizard_spike_damage;
11 float autocvar_g_monster_wizard_spike_edgedamage;
12 float autocvar_g_monster_wizard_spike_radius;
13 float autocvar_g_monster_wizard_spike_speed;
14
15 // animations
16 #define wizard_anim_hover       0
17 #define wizard_anim_fly         1
18 #define wizard_anim_magic       2
19 #define wizard_anim_pain        3
20 #define wizard_anim_death       4
21
22 void Wiz_FastExplode()
23 {
24         self.event_damage = func_null;
25         self.takedamage = DAMAGE_NO;
26         RadiusDamage (self, self.realowner, autocvar_g_monster_wizard_spike_damage, autocvar_g_monster_wizard_spike_edgedamage, autocvar_g_monster_wizard_spike_radius, world, 0, self.projectiledeathtype, other);
27
28         remove (self);
29 }
30
31 void Wiz_FastTouch ()
32 {
33         PROJECTILE_TOUCH;
34         
35         if(other == self.owner)
36                 return;
37                 
38         if(teamplay)
39         if(other.team == self.owner.team)
40                 return;
41                 
42         pointparticles(particleeffectnum("TE_WIZSPIKE"), self.origin, '0 0 0', 1);
43         
44         Wiz_FastExplode();
45 }
46
47 void Wiz_StartFast ()
48 {
49         local   entity  missile;
50         local   vector  dir = '0 0 0';
51         local   float   dist = 0, flytime = 0;
52
53         dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
54         dist = vlen (self.enemy.origin - self.origin);
55         flytime = dist * 0.002;
56         if (flytime < 0.1)
57                 flytime = 0.1;
58         
59         self.v_angle = self.angles;
60         makevectors (self.angles);
61
62         missile = spawn ();
63         missile.owner = missile.realowner = self;
64         setsize (missile, '0 0 0', '0 0 0');            
65         setorigin (missile, self.origin + v_forward * 14 + '0 0 30' + v_right * 14);
66         missile.enemy = self.enemy;
67         missile.nextthink = time + 3;
68         missile.think = Wiz_FastExplode;
69         missile.velocity = dir * autocvar_g_monster_wizard_spike_speed;
70         missile.avelocity = '300 300 300';
71         missile.solid = SOLID_BBOX;
72         missile.movetype = MOVETYPE_FLYMISSILE;
73         missile.touch = Wiz_FastTouch;
74         CSQCProjectile(missile, TRUE, PROJECTILE_CRYLINK, TRUE);
75         
76         missile = spawn ();
77         missile.owner = missile.realowner = self;
78         setsize (missile, '0 0 0', '0 0 0');            
79         setorigin (missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14);
80         missile.enemy = self.enemy;
81         missile.nextthink = time + 3;
82         missile.touch = Wiz_FastTouch;
83         missile.solid = SOLID_BBOX;
84         missile.movetype = MOVETYPE_FLYMISSILE;
85         missile.think = Wiz_FastExplode;
86         missile.velocity = dir * autocvar_g_monster_wizard_spike_speed;
87         missile.avelocity = '300 300 300';
88         CSQCProjectile(missile, TRUE, PROJECTILE_CRYLINK, TRUE);
89 }
90
91 void wizard_think ()
92 {
93         self.think = wizard_think;
94         self.nextthink = time + 0.1;
95         
96         monster_move(autocvar_g_monster_wizard_speed_run, autocvar_g_monster_wizard_speed_walk, 300, wizard_anim_fly, wizard_anim_hover, wizard_anim_hover);
97 }
98
99 void wizard_fastattack ()
100 {
101         Wiz_StartFast();
102 }
103
104 void wizard_die ()
105 {
106         Monster_CheckDropCvars ("wizard");
107         
108         self.think                      = Monster_Fade;
109         self.solid                      = SOLID_NOT;
110         self.takedamage         = DAMAGE_NO;
111         self.event_damage   = func_null;
112         self.enemy                      = world;
113         self.movetype           = MOVETYPE_TOSS;
114         self.flags                      = FL_ONGROUND;
115         self.nextthink          = time + 2.1;
116         self.pain_finished  = self.nextthink; 
117         self.velocity_x         = -200 + 400*random();
118         self.velocity_y         = -200 + 400*random();
119         self.velocity_z         = 100 + 100*random();
120         self.frame                      = wizard_anim_death;
121         
122         monster_hook_death(); // for post-death mods
123 }
124
125 float Wiz_Missile ()
126 {
127         wizard_fastattack();
128         return TRUE;
129 }
130
131 void wizard_spawn ()
132 {
133         if not(self.health)
134                 self.health = autocvar_g_monster_wizard_health * self.scale;
135         
136         self.classname                  = "monster_wizard";
137         self.checkattack                = GenericCheckAttack;
138         self.attack_ranged              = Wiz_Missile;
139         self.nextthink                  = time + random() * 0.5 + 0.1;
140         self.movetype                   = MOVETYPE_FLY; // TODO: make it fly up/down
141         self.flags                         |= FL_FLY;
142         self.think                              = wizard_think;
143         self.sprite_height              = 30 * self.scale;
144         
145         monster_hook_spawn(); // for post-spawn mods
146 }
147
148 void spawnfunc_monster_wizard ()
149 {       
150         if not(autocvar_g_monster_wizard)
151         {
152                 remove(self);
153                 return;
154         }
155         
156         self.monster_spawnfunc = spawnfunc_monster_wizard;
157         
158         if(self.spawnflags & MONSTERFLAG_APPEAR)
159         {
160                 self.think = func_null;
161                 self.nextthink = -1;
162                 self.use = Monster_Appear;
163                 return;
164         }
165         
166         self.scale = 1.3;
167         
168         if not (monster_initialize(
169                          "Scrag",
170                          "models/monsters/wizard.mdl",
171                          WIZARD_MIN, WIZARD_MAX,
172                          TRUE,
173                          wizard_die, wizard_spawn))
174         {
175                 remove(self);
176                 return;
177         }
178         
179         precache_model ("models/spike.mdl");
180         precache_sound ("weapons/spike.wav");
181 }
182
183 // compatibility with old spawns
184 void spawnfunc_monster_scrag () { spawnfunc_monster_wizard(); }