Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
sv_instagib.qc
Go to the documentation of this file.
1#include "sv_instagib.qh"
2
3#include <server/client.qh>
8
14//int autocvar_g_instagib_ammo_drop;
20
25{
28 {
29 string cvar_name = sprintf("g_%s_%s_probability", prefix,
30 it.m_canonical_spawnfunc);
31 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
32 {
33 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
34 continue;
35 }
36 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
37 });
39}
40
44{
45 if (!e.instagib_needammo)
46 return;
47 Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO);
48 e.instagib_needammo = false;
49}
50
52{
53 float hp = GetResource(this, RES_HEALTH);
54
55 float dmg = (hp <= 10) ? 5 : 10;
56 Damage(this, this, this, dmg, DEATH_NOAMMO.m_id, DMG_NOWEP, this.origin, '0 0 0');
57
58 entity annce = (hp <= 5) ? ANNCE_INSTAGIB_TERMINATED : Announcer_PickNumber(CNT_NORMAL, ceil(hp / 10));
59 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, annce);
60
61 if (hp > 80)
62 {
63 if (hp <= 90)
64 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO);
65 else
66 Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO);
67 }
68}
69
71{
72 if(time < this.instagib_nextthink)
73 return;
74 if(!IS_PLAYER(this))
75 return; // not a player
76
77 if(IS_DEAD(this) || game_stopped)
79 else if (GetResource(this, RES_CELLS) > 0 || (this.items & IT_UNLIMITED_AMMO) || (this.flags & FL_GODMODE))
82 {
83 if(!this.instagib_needammo)
84 {
85 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE);
86 this.instagib_needammo = true;
87 }
88 }
89 else
90 {
91 this.instagib_needammo = true;
93 }
94 this.instagib_nextthink = time + 1;
95}
96
97MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd)
98{
100}
101
103{
105 M_ARGV(0, string));
106 return true;
107}
108
109MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem)
110{
111 M_ARGV(1, string) = "vaporizer_cells";
112}
113
114MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn)
115{
116 entity mon = M_ARGV(0, entity);
117
118 // always refill ammo
119 if(mon.monsterdef == MON_MAGE)
120 mon.skin = 1;
121}
122
123MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver)
124{
125 entity player = M_ARGV(0, entity);
126
128}
129
130MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidRandomStartWeapons)
131{
132 return true;
133}
134
135MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn)
136{
137 entity player = M_ARGV(0, entity);
138
139 player.effects |= EF_FULLBRIGHT;
140}
141
143{
144 entity player = M_ARGV(0, entity);
145
146 instagib_ammocheck(player);
147}
148
149MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen)
150{
151 // no regeneration in instagib
152 return true;
153}
154
155MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor)
156{
157 M_ARGV(4, float) = M_ARGV(7, float); // take = damage
158 M_ARGV(5, float) = 0; // save
159}
160
161MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon)
162{
163 // weapon dropping on death handled by FilterItem
164 return true;
165}
166
167MUTATOR_HOOKFUNCTION(mutator_instagib, Damage_Calculate)
168{
169 entity frag_attacker = M_ARGV(1, entity);
171 float frag_deathtype = M_ARGV(3, float);
172 float frag_damage = M_ARGV(4, float);
173 float frag_mirrordamage = M_ARGV(5, float);
175
176 if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
177 frag_damage = 0;
178
180 {
181 if(frag_deathtype == DEATH_FALL.m_id)
182 frag_damage = 0; // never count fall damage
183
185 switch(DEATH_ENT(frag_deathtype))
186 {
187 case DEATH_DROWN:
188 case DEATH_SLIME:
189 case DEATH_LAVA:
190 frag_damage = 0;
191 break;
192 }
193
194 if(IS_PLAYER(frag_attacker))
195 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
196 {
198 frag_force = '0 0 0';
199
200 float armor = GetResource(frag_target, RES_ARMOR);
201 if(armor)
202 {
203 armor -= 1;
205 frag_damage = 0;
206 frag_target.hitsound_damage_dealt += 1;
207 frag_attacker.hitsound_damage_dealt += 1;
208 Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, armor);
209 }
210 }
211
212 if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
213 {
215 {
216 frag_damage = 0;
218 frag_mirrordamage = 0; // never do mirror damage on enemies
219 }
220
221 if(frag_target != frag_attacker)
222 {
224 frag_force = '0 0 0';
225 }
226 }
227 }
228
229 if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring
230 if(IS_PLAYER(frag_attacker))
231 if(frag_mirrordamage > 0)
232 {
233 // just lose extra LIVES, don't kill the player for mirror damage
234 float armor = GetResource(frag_attacker, RES_ARMOR);
235 if(armor > 0)
236 {
237 armor -= 1;
238 SetResource(frag_attacker, RES_ARMOR, armor);
239 Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, armor);
240 frag_attacker.hitsound_damage_dealt += frag_mirrordamage;
241 }
242 frag_mirrordamage = 0;
243 }
244
245 if(frag_target.alpha && frag_target.alpha < 1)
247 yoda = 1;
248
249 M_ARGV(4, float) = frag_damage;
250 M_ARGV(5, float) = frag_mirrordamage;
252}
253
254MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems, CBC_ORDER_LAST)
255{
258
261 start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start");
263 //start_ammo_fuel = warmup_start_ammo_fuel = 0;
264
267}
268
269MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena)
270{
271 // turn weapon arena off
272 M_ARGV(0, string) = "off";
273}
274
276{
277 entity new_item = spawn();
278 switch (def)
279 {
280 case ITEM_Invisibility:
281 new_item.invisibility_finished = autocvar_g_instagib_invisibility_time;
282 break;
283 case ITEM_Speed:
284 new_item.speed_finished = autocvar_g_instagib_speed_time;
285 break;
286 }
287 Item_CopyFields(this, new_item);
288 StartItem(new_item, def);
289}
290
293
294// replaces item with a random powerup selected among the less spawned ones
296{
297 static int remaining_powerups_count = INSTAGIB_POWERUP_COUNT;
298 if (remaining_powerups_count == 0)
299 remaining_powerups_count = INSTAGIB_POWERUP_COUNT;
300 if (remaining_powerups_count == INSTAGIB_POWERUP_COUNT)
301 {
302 instagib_remaining_powerups[0] = ITEM_Invisibility;
303 instagib_remaining_powerups[1] = ITEM_ExtraLife;
304 instagib_remaining_powerups[2] = ITEM_Speed;
305 }
306
307 float r = floor(random() * remaining_powerups_count);
309 for(int i = r; i < INSTAGIB_POWERUP_COUNT - 1; ++i)
311 --remaining_powerups_count;
312}
313
314MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
315{
316 entity item = M_ARGV(0, entity);
317
318 switch (item.itemdef)
319 {
320 case ITEM_Strength: case ITEM_Shield: case ITEM_HealthMega: case ITEM_ArmorMega:
323 return true;
324 case ITEM_Invisibility: case ITEM_ExtraLife: case ITEM_Speed:
325 return false;
326 case ITEM_Cells:
328 instagib_replace_item_with(item, ITEM_VaporizerCells);
329 return true;
330 case ITEM_Rockets:
332 instagib_replace_item_with(item, ITEM_VaporizerCells);
333 return true;
334 case ITEM_Shells:
336 instagib_replace_item_with(item, ITEM_VaporizerCells);
337 return true;
338 case ITEM_Bullets:
340 instagib_replace_item_with(item, ITEM_VaporizerCells);
341 return true;
342 case ITEM_Jetpack:
343 case ITEM_FuelRegen:
345 }
346
347 switch (item.weapon)
348 {
349 case WEP_VAPORIZER.m_id:
350 if (ITEM_IS_LOOT(item))
351 {
353 return false;
354 }
355 break;
356 case WEP_DEVASTATOR.m_id: case WEP_VORTEX.m_id:
357 instagib_replace_item_with(item, ITEM_VaporizerCells);
358 return true;
359 }
360
361 float cells = GetResource(item, RES_CELLS);
362 if(cells > autocvar_g_instagib_ammo_drop && item.classname != "item_vaporizer_cells")
364
365 if(cells && !item.weapon)
366 return false;
367
368 return true;
369}
370
371MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies)
372{
373 float frag_deathtype = M_ARGV(3, float);
374
375 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
376 M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death
377}
378
379MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch)
380{
381 if(MUTATOR_RETURNVALUE) return false;
382
383 entity item = M_ARGV(0, entity);
385
386 if(GetResource(item, RES_CELLS))
387 {
388 // play some cool sounds ;)
389 float hp = GetResource(toucher, RES_HEALTH);
390 if (IS_CLIENT(toucher))
391 {
392 if(hp <= 5)
393 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND);
394 else if(hp < 50)
395 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY);
396 }
397
398 if(hp < 100)
399 SetResource(toucher, RES_HEALTH, 100);
400
402 }
403
404 if(item.itemdef == ITEM_ExtraLife)
405 {
407 Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES, autocvar_g_instagib_extralives);
408 Inventory_pickupitem(item.itemdef, toucher);
410 }
411
413}
414
415MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString)
416{
417 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib");
418}
419
420MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString)
421{
422 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", InstaGib");
423}
424
425MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname)
426{
427 M_ARGV(0, string) = "InstaGib";
428 return true;
429}
const int CBC_ORDER_LAST
Definition base.qh:11
#define MUTATOR_HOOKFUNCTION(...)
Definition base.qh:335
#define MUTATOR_RETURNVALUE
Definition base.qh:328
float dmg
Definition breakable.qc:12
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
void SetResource(entity e, Resource res_type, float amount)
Sets the current amount of resource the given entity will have.
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
const int IT_UNLIMITED_AMMO
Definition item.qh:23
const int IT_UNLIMITED_SUPERWEAPONS
Definition item.qh:24
#define M_ARGV(x, type)
Definition events.qh:17
int autocvar_g_instagib_ammo_drop
Definition items.qh:13
int items
Definition player.qh:227
#define IS_CLIENT(s)
Definition player.qh:242
#define IS_DEAD(s)
Definition player.qh:245
#define IS_PLAYER(s)
Definition player.qh:243
float game_stopped
Definition stats.qh:81
Notification Announcer_PickNumber(int type, int num)
Definition util.qc:1827
const int CNT_NORMAL
Definition util.qh:251
const int FL_GODMODE
Definition constants.qh:75
float flags
float CVAR_TYPEFLAG_EXISTS
const float EF_FULLBRIGHT
float time
#define spawn
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:503
float yoda
Definition damage.qh:48
#define DMG_NOWEP
Definition damage.qh:104
float autocvar_g_friendlyfire
Definition damage.qh:26
#define DEATH_ENT(t)
Definition all.qh:41
#define DEATH_ISWEAPON(t, w)
Definition all.qh:46
RES_ARMOR
Definition ent_cs.qc:130
#define IL_EACH(this, cond, body)
void Inventory_pickupitem(Pickup this, entity player)
Definition inventory.qh:161
bool Item_IsDefinitionAllowed(entity definition)
Checks whether the items with the specified definition are allowed to spawn.
Definition spawning.qc:17
#define ITEM_IS_LOOT(item)
Returns whether the item is loot.
Definition spawning.qh:39
#define PlayerPreThink
Definition _all.inc:254
#define LOG_WARNF(...)
Definition log.qh:62
float ceil(float f)
float cvar(string name)
float random(void)
float floor(float f)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_cpid)
Definition all.qc:1537
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
string RandomSelection_chosen_string
Definition random.qh:7
#define RandomSelection_AddString(s, weight, priority)
Definition random.qh:16
vector
Definition self.qh:92
entity entity toucher
Definition self.qh:72
void Item_CopyFields(entity this, entity to)
Definition items.qc:850
void StartItem(entity this, entity def)
Definition items.qc:1004
@ MUT_ITEMTOUCH_PICKUP
Definition events.qh:736
@ MUT_ITEMTOUCH_CONTINUE
Definition events.qh:734
float frag_damage
Definition sv_ctf.qc:2322
vector frag_force
Definition sv_ctf.qc:2323
entity frag_target
Definition sv_ctf.qc:2321
bool autocvar_g_instagib_ammo_convert_cells
bool autocvar_g_instagib_friendlypush
GameItem instagib_remaining_powerups[INSTAGIB_POWERUP_COUNT]
void instagib_countdown(entity this)
float instagib_needammo
const int INSTAGIB_POWERUP_COUNT
bool autocvar_g_instagib_damagedbycontents
Definition sv_instagib.qc:9
float instagib_nextthink
bool autocvar_g_instagib_ammo_convert_bullets
bool autocvar_g_instagib_mirrordamage
bool autocvar_g_instagib_blaster_keepdamage
bool autocvar_g_instagib_ammo_convert_shells
void instagib_stop_countdown(entity e)
bool autocvar_g_instagib_allow_jetpacks
bool autocvar_g_instagib_blaster_keepforce
void instagib_replace_item_with(entity this, GameItem def)
void instagib_ammocheck(entity this)
void instagib_replace_item_with_random_powerup(entity item)
string RandomItems_GetRandomInstagibItemClassName(string prefix)
Returns a random classname of the instagib item.
bool autocvar_g_instagib_ammo_convert_rockets
float autocvar_g_instagib_speed_time
float autocvar_g_rm
Definition sv_instagib.qh:8
int autocvar_g_instagib_extralives
float autocvar_g_instagib_invisibility_time
IntrusiveList g_instagib_items
float autocvar_g_rm_laser
int autocvar_g_powerups
Definition sv_powerups.qh:7
string RandomItems_GetRandomItemClassName(string prefix)
Returns a random classname of the item.
Header file that describes the random items mutator.
void GiveResource(entity receiver, Resource res_type, float amount)
Gives an entity some resource.
#define SAME_TEAM(a, b)
Definition teams.qh:241
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50
#define WEPSET(id)
Definition all.qh:45
WepSet start_weapons
Definition world.qh:80
float warmup_start_ammo_cells
Definition world.qh:105
float start_ammo_shells
Definition world.qh:84
float warmup_start_ammo_rockets
Definition world.qh:104
float warmup_start_ammo_shells
Definition world.qh:102
int start_items
Definition world.qh:83
float warmup_start_ammo_nails
Definition world.qh:103
float start_ammo_cells
Definition world.qh:87
float warmup_start_health
Definition world.qh:107
float start_ammo_rockets
Definition world.qh:86
float start_armorvalue
Definition world.qh:97
WepSet warmup_start_weapons
Definition world.qh:98
float start_health
Definition world.qh:96
float warmup_start_armorvalue
Definition world.qh:108
float start_ammo_nails
Definition world.qh:85