]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/monsters/monster/spider.qc
Replace enforcer model with e-wheel
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / monster / spider.qc
1 // cvars
2 float autocvar_g_monster_spider;
3 float autocvar_g_monster_spider_stopspeed;
4 float autocvar_g_monster_spider_attack_leap_delay;
5 float autocvar_g_monster_spider_attack_stand_damage;
6 float autocvar_g_monster_spider_attack_stand_delay;
7 float autocvar_g_monster_spider_health;
8 float autocvar_g_monster_spider_speed_walk;
9 float autocvar_g_monster_spider_speed_run;
10 float autocvar_g_monster_spider_attack_type;
11
12 // spider animations
13 #define spider_anim_idle                        0
14 #define spider_anim_walk                        1
15 #define spider_anim_attack                      2
16 #define spider_anim_attack2                     3
17
18 const vector SPIDER_MIN                          = '-18 -18 -25';
19 const vector SPIDER_MAX                          = '18 18 30';
20
21 .float spider_type; // used to switch between fire & ice attacks
22 const float SPIDER_TYPE_ICE             = 0;
23 const float SPIDER_TYPE_FIRE    = 1;
24
25 void spider_spawn();
26 void spawnfunc_monster_spider();
27 void spider_think();
28
29 void spider_die ()
30 {
31         Monster_CheckDropCvars ("spider");
32         
33         self.angles += '180 0 0';
34         self.solid                      = SOLID_NOT;
35         self.takedamage         = DAMAGE_NO;
36         self.event_damage   = func_null;
37         self.enemy                      = world;
38         self.movetype           = MOVETYPE_TOSS;
39         self.think                      = Monster_Fade;
40         self.nextthink          = time + 2.1;
41         self.pain_finished  = self.nextthink;
42         self.frame                      = spider_anim_attack;
43         
44         monster_hook_death(); // for post-death mods
45 }
46
47 /**
48  * Performe a standing attack on self.enemy.
49  */
50 void spider_attack_standing() {
51         float dot = 0, bigdmg = autocvar_g_monster_spider_attack_stand_damage * self.scale;
52
53         self.velocity_x = 0;
54         self.velocity_y = 0;
55         
56         if(self.monster_owner == self.enemy)
57         {
58                 self.enemy = world;
59                 return;
60         }
61
62         makevectors (self.angles);
63         dot = normalize (self.enemy.origin - self.origin) * v_forward;
64         if(dot > 0.3)
65         {
66                 Damage(self.enemy, self, self, bigdmg * monster_skill, DEATH_MONSTER_MELEE, self.origin, '0 0 0');
67         }
68         
69         if (!monster_isvalidtarget(self.enemy, self, FALSE))
70                 self.enemy = world;
71                 
72         if(random() < 0.50)
73                 self.frame = spider_anim_attack;
74         else
75                 self.frame = spider_anim_attack2;
76
77         self.nextthink = time + autocvar_g_monster_spider_attack_stand_delay;
78 }
79
80 void spider_web_explode ()
81 {
82         RadiusDamage (self, self.realowner, 0, 0, 1, world, 0, self.projectiledeathtype, other);
83         remove (self);
84 }
85
86 void spider_web_touch ()
87 {
88         PROJECTILE_TOUCH;
89         if (other.takedamage == DAMAGE_AIM)
90                 Freeze(other, 0.3);
91                 
92         spider_web_explode();
93 }
94
95 void spider_shootweb()
96 {
97         // clone of the electro secondary attack, with less bouncing
98         entity proj = world;
99         
100         makevectors(self.angles);
101
102         W_SetupShot_ProjectileSize (self, '0 0 -4', '0 0 -4', FALSE, 2, "weapons/electro_fire2.wav", CH_WEAPON_A, 0);
103
104         w_shotdir = v_forward; // no TrueAim for grenades please
105
106         pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
107
108         proj = spawn ();
109         proj.classname = "plasma";
110         proj.owner = proj.realowner = self;
111         proj.use = spider_web_touch;
112         proj.think = adaptor_think2use_hittype_splash;
113         proj.bot_dodge = TRUE;
114         proj.bot_dodgerating = 0;
115         proj.nextthink = time + autocvar_g_balance_electro_secondary_lifetime;
116         PROJECTILE_MAKETRIGGER(proj);
117         proj.projectiledeathtype = WEP_ELECTRO | HITTYPE_SECONDARY;
118         setorigin(proj, w_shotorg);
119
120         //proj.glow_size = 50;
121         //proj.glow_color = 45;
122         proj.movetype = MOVETYPE_BOUNCE;
123         W_SETUPPROJECTILEVELOCITY_UP(proj, g_balance_electro_secondary);
124         proj.touch = spider_web_touch;
125         setsize(proj, '0 0 -4', '0 0 -4');
126         proj.takedamage = DAMAGE_YES;
127         proj.damageforcescale = 0;
128         proj.health = 500;
129         proj.event_damage = W_Plasma_Damage;
130         proj.flags = FL_PROJECTILE;
131         proj.damagedbycontents = TRUE;
132
133         proj.bouncefactor = 0.3;
134         proj.bouncestop = 0.05;
135         proj.missile_flags = MIF_SPLASH | MIF_ARC;
136
137         CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, FALSE); // no culling, it has sound
138
139         other = proj; MUTATOR_CALLHOOK(EditProjectile);
140 }
141
142 void spider_attack_leap()
143 {
144         vector angles_face = vectoangles(self.enemy.origin - self.origin);
145
146         // face the enemy       
147         self.frame = spider_anim_attack2;
148         self.angles_y = angles_face_y ;
149         self.nextthink = time + autocvar_g_monster_spider_attack_leap_delay;
150         
151         makevectors(self.angles);
152         
153         switch(self.spider_type)
154         {
155                 default:
156                 case SPIDER_TYPE_ICE:
157                         spider_shootweb(); break; // must... remember... breaks!
158                 case SPIDER_TYPE_FIRE:
159                         W_Fireball_Attack2(); break;
160         }
161 }
162
163 float spider_attack_ranged()
164 {
165         if(self.enemy.frozen || self.enemy.freezetag_frozen)
166                 return FALSE;
167                 
168         spider_attack_leap();
169         return TRUE;
170 }
171
172 void spider_think()
173 {
174         self.think = spider_think;
175         self.nextthink = time + 0.1;
176         
177         monster_move(autocvar_g_monster_spider_speed_run, autocvar_g_monster_spider_speed_walk, autocvar_g_monster_spider_stopspeed, spider_anim_walk, spider_anim_walk, spider_anim_idle);
178 }
179
180 /**
181  * Spawn the spider.
182  */
183 void spider_spawn() 
184 {
185         if not(self.health)
186                 self.health = autocvar_g_monster_spider_health * self.scale;
187         
188         self.classname                  = "monster_spider";
189         self.nextthink                  = time + random() * 0.5 + 0.1;
190         self.pain_finished      = self.nextthink;
191         self.frame                              = spider_anim_idle;
192         self.checkattack                = GenericCheckAttack;
193         self.attack_melee               = spider_attack_standing;
194         self.attack_ranged              = spider_attack_ranged;
195         self.think                              = spider_think;
196         self.sprite_height      = 40 * self.scale;
197         
198         monster_hook_spawn(); // for post-spawn mods
199 }
200
201 /*QUAKED monster_spider (1 0 0) (-18 -18 -25) (18 18 47)
202 Spider, 60 health points.
203 -------- KEYS --------
204 -------- SPAWNFLAGS --------
205 MONSTERFLAG_APPEAR: monster will spawn when triggered.
206 ---------NOTES----------
207 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
208 modeldisabled="models/monsters/spider.dpm"
209 */
210 void spawnfunc_monster_spider() 
211 {
212         if not(autocvar_g_monster_spider) { remove(self); return; }
213         
214         self.monster_spawnfunc = spawnfunc_monster_spider;
215         self.classname = "monster_spider";
216         if(!self.spider_type)
217                 self.spider_type = autocvar_g_monster_spider_attack_type;
218         
219         if(self.spawnflags & MONSTERFLAG_APPEAR)
220         {
221                 self.think = func_null;
222                 self.nextthink = -1;
223                 self.use = Monster_Appear;
224                 return;
225         }
226         
227         if not (monster_initialize(
228                          "Spider",
229                          "models/monsters/spider.dpm",
230                          SPIDER_MIN, SPIDER_MAX,
231                          FALSE,
232                          spider_die, spider_spawn))
233         {
234                 remove(self);
235                 return;
236         }
237 }