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