]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/attic/monsters/fight.qc
Merge remote-tracking branch 'origin/divVerent/new-laser-by-morphed'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / attic / monsters / fight.qc
1
2 /*
3
4 A monster is in fight mode if it thinks it can effectively attack its
5 enemy.
6
7 When it decides it can't attack, it goes into hunt mode.
8
9 */
10
11 void SUB_AttackFinished (float normal)
12 {
13         self.cnt = 0;           // refire count for nightmare
14         if (skill < 3)
15                 ATTACK_FINISHED(self) = time + normal;
16 }
17
18 float CanDamage(entity targ, entity inflictor)
19 {
20         if (targ.movetype == MOVETYPE_PUSH)
21         {
22                 traceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, self);
23                 if (trace_fraction == 1)
24                         return TRUE;
25                 if (trace_ent == targ)
26                         return TRUE;
27                 return FALSE;
28         }
29
30         traceline(inflictor.origin, targ.origin, TRUE, self);
31         if (trace_fraction == 1)
32                 return TRUE;
33         traceline(inflictor.origin, targ.origin + '15 15 0', TRUE, self);
34         if (trace_fraction == 1)
35                 return TRUE;
36         traceline(inflictor.origin, targ.origin + '-15 -15 0', TRUE, self);
37         if (trace_fraction == 1)
38                 return TRUE;
39         traceline(inflictor.origin, targ.origin + '-15 15 0', TRUE, self);
40         if (trace_fraction == 1)
41                 return TRUE;
42         traceline(inflictor.origin, targ.origin + '15 -15 0', TRUE, self);
43         if (trace_fraction == 1)
44                 return TRUE;
45
46         return FALSE;
47 }
48
49 float(float v) anglemod;
50
51 void(vector dest) ChooseTurn;
52
53 void() ai_face;
54
55
56 float enemy_range;
57
58
59 //=============================================================================
60
61 /*
62 ===========
63 GenericCheckAttack
64
65 The player is in view, so decide to move or launch an attack
66 Returns FALSE if movement should continue
67 ============
68 */
69 float() GenericCheckAttack =
70 {
71         vector spot1, spot2;
72         entity targ;
73         float chance;
74
75         if (self.health < 1)
76                 return FALSE;
77         targ = self.enemy;
78
79         if (vlen(targ.origin - self.origin) > 5000) // long traces are slow
80                 return FALSE;
81
82 // see if any entities are in the way of the shot
83         spot1 = self.origin + self.view_ofs;
84         spot2 = targ.origin + targ.view_ofs;
85
86         traceline (spot1, spot2, FALSE, self);
87
88         if (trace_ent != targ)
89                 return FALSE; // don't have a clear shot
90
91         if (trace_inopen && trace_inwater)
92                 return FALSE; // sight line crossed contents
93
94         if (enemy_range == RANGE_MELEE)
95         {       // melee attack
96                 if (self.th_melee)
97                 {
98                         self.th_melee ();
99                         return TRUE;
100                 }
101         }
102
103 // missile attack
104         if (time < ATTACK_FINISHED(self))
105                 return FALSE;
106
107         if (!self.th_missile)
108                 return FALSE;
109
110         if (enemy_range == RANGE_FAR)
111                 return FALSE;
112
113         if (enemy_range == RANGE_MELEE)
114         {
115                 chance = 0.9;
116                 ATTACK_FINISHED(self) = 0;
117         }
118         else if (enemy_range == RANGE_NEAR)
119         {
120                 if (self.th_melee)
121                         chance = 0.2;
122                 else
123                         chance = 0.4;
124         }
125         else if (enemy_range == RANGE_MID)
126         {
127                 if (self.th_melee)
128                         chance = 0.05;
129                 else
130                         chance = 0.1;
131         }
132         else
133                 chance = 0;
134
135         if (random () < chance)
136         if (self.th_missile ())
137         {
138                 SUB_AttackFinished (2*random());
139                 return TRUE;
140         }
141
142         return FALSE;
143 }
144
145
146 /*
147 =============
148 ai_face
149
150 Stay facing the enemy
151 =============
152 */
153 void() ai_face =
154 {
155         self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
156         ChangeYaw ();
157 }
158
159 /*
160 =============
161 ai_charge
162
163 The monster is in a melee attack, so get as close as possible to .enemy
164 =============
165 */
166 float (entity targ) visible;
167 float(entity targ) infront;
168 float(entity targ) range;
169
170 void(float d) ai_charge =
171 {
172         if (self.health < 1)
173                 return;
174         ai_face ();
175         movetogoal (d);         // done in C code...
176 }
177
178 void() ai_charge_side =
179 {
180         if (self.health < 1)
181                 return;
182         vector dtemp;
183         float heading;
184
185 // aim to the left of the enemy for a flyby
186
187         self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
188         ChangeYaw ();
189
190         makevectors (self.angles);
191         dtemp = self.enemy.origin - 30*v_right;
192         heading = vectoyaw(dtemp - self.origin);
193
194         walkmove(heading, 20);
195 }
196
197
198 /*
199 =============
200 ai_melee
201
202 =============
203 */
204 void() ai_melee =
205 {
206         vector delta;
207         float ldmg;
208
209         if (self.health < 1)
210                 return;
211         if (!self.enemy)
212                 return;         // removed before stroke
213
214         delta = self.enemy.origin - self.origin;
215
216         if (vlen(delta) > 60)
217                 return;
218
219         ldmg = DMG_KNIGHT_MELEE_BASE + DMG_KNIGHT_MELEE_RANDOM1 * random();
220         ldmg = ldmg + DMG_KNIGHT_MELEE_RANDOM2 * random();
221         ldmg = ldmg + DMG_KNIGHT_MELEE_RANDOM3 * random();
222         traceline(self.origin, self.enemy.origin, FALSE, self);
223
224         Damage (self.enemy, self, self, ldmg, self.projectiledeathtype, trace_endpos, '0 0 0'); // TODO add force to monster melee attacks?
225 }
226
227
228 void() ai_melee_side =
229 {
230         vector delta;
231         float ldmg;
232
233         if (self.health < 1)
234                 return;
235         if (!self.enemy)
236                 return;         // removed before stroke
237
238         ai_charge_side();
239
240         delta = self.enemy.origin - self.origin;
241
242         if (vlen(delta) > 60)
243                 return;
244         if (!CanDamage (self.enemy, self))
245                 return;
246         ldmg = DMG_KNIGHT_MELEE_BASE + DMG_KNIGHT_MELEE_RANDOM1 * random();
247         ldmg = ldmg + DMG_KNIGHT_MELEE_RANDOM2 * random();
248         ldmg = ldmg + DMG_KNIGHT_MELEE_RANDOM3 * random();
249         traceline(self.origin, self.enemy.origin, FALSE, self);
250         Damage (self.enemy, self, self, ldmg, self.projectiledeathtype, trace_endpos, '0 0 0');
251 }
252