2 /// \brief Source file that contains implementation of the player templates
5 /// \copyright GNU GPLv3 or any later version.
7 #include <common/items/item.qh>
8 #include "autocvars.qh"
9 #include "resources.qh"
10 #include "mutators/events.qh"
12 const string playertemplate_cvar_prefix = "g_player_template_";
14 string PlayerTemplate_GetFullCvarName(string template, string variable)
16 return strcat(playertemplate_cvar_prefix, template, "_", variable);
19 string PlayerTemplate_GetDefaultCvarName(string variable)
25 return "g_balance_health_start";
29 return "g_balance_armor_start";
31 case "start_ammo_shells":
33 return "g_start_ammo_shells";
35 case "start_ammo_bullets":
37 return "g_start_ammo_nails";
39 case "start_ammo_rockets":
41 return "g_start_ammo_rockets";
43 case "start_ammo_cells":
45 return "g_start_ammo_cells";
47 case "start_ammo_plasma":
49 return "g_start_ammo_plasma";
51 case "start_ammo_fuel":
53 return "g_start_ammo_fuel";
55 case "random_start_weapons_count":
57 return "g_random_start_weapons_count";
59 case "random_start_weapons":
61 return "g_random_start_weapons";
63 case "random_start_shells":
65 return "g_random_start_shells";
67 case "random_start_bullets":
69 return "g_random_start_bullets";
71 case "random_start_rockets":
73 return "g_random_start_rockets";
75 case "random_start_cells":
77 return "g_random_start_cells";
79 case "random_start_plasma":
81 return "g_random_start_plasma";
83 case "start_ammo_vaporizer_cells":
85 return "g_instagib_ammo_start";
89 return "g_weapon_throwable";
91 case "health_regen_factor":
93 return "g_balance_health_regen";
95 case "health_regen_linear":
97 return "g_balance_health_regenlinear";
99 case "health_rot_factor":
101 return "g_balance_health_rot";
103 case "health_rot_linear":
105 return "g_balance_health_rotlinear";
107 case "health_regen_stable":
109 return "g_balance_health_regenstable";
111 case "health_rot_stable":
113 return "g_balance_health_rotstable";
117 // TODO: Report error.
123 float PlayerTemplate_GetDefaultFloatValue(string variable)
129 case "start_ammo_shells":
130 case "start_ammo_bullets":
131 case "start_ammo_rockets":
132 case "start_ammo_cells":
133 case "start_ammo_plasma":
134 case "start_ammo_fuel":
135 case "random_start_weapons_count":
136 case "random_start_shells":
137 case "random_start_bullets":
138 case "random_start_rockets":
139 case "random_start_cells":
140 case "random_start_plasma":
141 case "start_ammo_vaporizer_cells":
143 case "health_regen_factor":
144 case "health_regen_linear":
145 case "health_rot_factor":
146 case "health_rot_linear":
147 case "health_regen_stable":
148 case "health_rot_stable":
150 return cvar(PlayerTemplate_GetDefaultCvarName(variable));
152 case "unlimited_ammo":
154 return !autocvar_g_use_ammunition;
156 case "default_start_weapons":
160 case "start_extra_lives":
165 case "defense_scale":
169 case "blaster_self_damage":
175 // TODO: Report error.
181 string PlayerTemplate_GetDefaultStringValue(string variable)
185 case "random_start_weapons":
187 cvar_string(PlayerTemplate_GetDefaultCvarName(variable));
189 case "start_weapons":
195 // TODO: Report error.
201 float PlayerTemplate_GetFloatValue(string template, string variable)
203 if (template == "default")
205 return PlayerTemplate_GetDefaultFloatValue(variable);
207 string fullname = PlayerTemplate_GetFullCvarName(template, variable);
208 if (!(cvar_type(fullname) & CVAR_TYPEFLAG_EXISTS))
210 return PlayerTemplate_GetDefaultFloatValue(variable);
212 if (cvar_string(fullname) == "default")
214 return PlayerTemplate_GetDefaultFloatValue(variable);
216 return cvar(fullname);
219 string PlayerTemplate_GetStringValue(string template, string variable)
221 if (template == "default")
223 return PlayerTemplate_GetDefaultStringValue(variable);
225 string fullname = PlayerTemplate_GetFullCvarName(template, variable);
226 if (!(cvar_type(fullname) & CVAR_TYPEFLAG_EXISTS))
228 return PlayerTemplate_GetDefaultStringValue(variable);
230 if (cvar_string(fullname) == "default")
232 return PlayerTemplate_GetDefaultStringValue(variable);
234 return cvar_string(fullname);
237 float PlayerTemplate_GivePlayerItem(entity player, string template,
240 string value = PlayerTemplate_GetStringValue(template, variable);
241 if (value == "default")
243 return MUT_ITEMTOUCH_CONTINUE;
245 int numfields = tokenize_console(PlayerTemplate_GetStringValue(template,
249 return MUT_ITEMTOUCH_CONTINUE;
251 for (int i = 1; i < numfields; ++i)
257 GiveResource(player, RESOURCE_HEALTH, stof(argv(i)));
262 GiveResource(player, RESOURCE_ARMOR, stof(argv(i)));
267 GiveResource(player, RESOURCE_SHELLS, stof(argv(i)));
272 GiveResource(player, RESOURCE_BULLETS, stof(argv(i)));
277 GiveResource(player, RESOURCE_ROCKETS, stof(argv(i)));
282 GiveResource(player, RESOURCE_CELLS, stof(argv(i)));
287 GiveResource(player, RESOURCE_PLASMA, stof(argv(i)));
292 GiveResource(player, RESOURCE_FUEL, stof(argv(i)));
301 return MUT_ITEMTOUCH_CONTINUE;
305 return MUT_ITEMTOUCH_PICKUP;
309 return MUT_ITEMTOUCH_CONTINUE;
314 // =========================== Hook handlers =================================
316 void PlayerTemplateHook_PlayerSpawn(entity player, string template)
318 if (template == "default")
322 if (autocvar_g_instagib)
324 SetResourceAmount(player, RESOURCE_ARMOR,
325 PlayerTemplate_GetFloatValue(template, "start_extra_lives"));
326 SetResourceAmount(player, RESOURCE_CELLS,
327 PlayerTemplate_GetFloatValue(template,
328 "start_ammo_vaporizer_cells"));
329 if (PlayerTemplate_GetFloatValue(template, "unlimited_ammo"))
331 player.items |= IT_UNLIMITED_AMMO;
335 // Give health, armor and ammo.
336 SetResourceAmount(player, RESOURCE_HEALTH,
337 PlayerTemplate_GetFloatValue(template, "start_health"));
338 SetResourceAmount(player, RESOURCE_ARMOR,
339 PlayerTemplate_GetFloatValue(template, "start_armor"));
340 if (PlayerTemplate_GetFloatValue(template, "unlimited_ammo"))
342 player.items |= IT_UNLIMITED_AMMO;
346 SetResourceAmount(player, RESOURCE_SHELLS,
347 PlayerTemplate_GetFloatValue(template, "start_ammo_shells"));
348 SetResourceAmount(player, RESOURCE_BULLETS,
349 PlayerTemplate_GetFloatValue(template, "start_ammo_bullets"));
350 SetResourceAmount(player, RESOURCE_ROCKETS,
351 PlayerTemplate_GetFloatValue(template, "start_ammo_rockets"));
352 SetResourceAmount(player, RESOURCE_CELLS,
353 PlayerTemplate_GetFloatValue(template, "start_ammo_cells"));
354 SetResourceAmount(player, RESOURCE_PLASMA,
355 PlayerTemplate_GetFloatValue(template, "start_ammo_plasma"));
356 SetResourceAmount(player, RESOURCE_FUEL,
357 PlayerTemplate_GetFloatValue(template, "start_ammo_fuel"));
360 if (PlayerTemplate_GetFloatValue(template, "default_start_weapons"))
362 FOREACH(Weapons, it != WEP_Null,
366 player.weapons |= it.m_wepset;
370 int numweapons = tokenize_console(PlayerTemplate_GetStringValue(template,
372 for (int i = 0; i < numweapons; ++i)
374 string weapon = argv(i);
375 FOREACH(Weapons, it != WEP_Null,
377 if (it.netname == weapon)
379 player.weapons |= it.m_wepset;
386 entity ammo_entity = spawn();
387 SetResourceAmount(ammo_entity, RESOURCE_SHELLS,
388 PlayerTemplate_GetFloatValue(template, "random_start_shells"));
389 SetResourceAmount(ammo_entity, RESOURCE_BULLETS,
390 PlayerTemplate_GetFloatValue(template, "random_start_bullets"));
391 SetResourceAmount(ammo_entity, RESOURCE_ROCKETS,
392 PlayerTemplate_GetFloatValue(template, "random_start_rockets"));
393 SetResourceAmount(ammo_entity, RESOURCE_CELLS,
394 PlayerTemplate_GetFloatValue(template, "random_start_cells"));
395 SetResourceAmount(ammo_entity, RESOURCE_PLASMA,
396 PlayerTemplate_GetFloatValue(template, "random_start_plasma"));
397 GiveRandomWeapons(player, PlayerTemplate_GetFloatValue(template,
398 "random_start_weapons_count"),
399 PlayerTemplate_GetStringValue(template, "random_start_weapons"),
404 // Give random weapons.
405 numweapons = tokenize_console(PlayerTemplate_GetStringValue(template,
406 "random_start_weapons"));
407 // Give all weapons during warmup stage.
408 for (int i = 0; i < numweapons; ++i)
410 string weapon = argv(i);
411 FOREACH(Weapons, it != WEP_Null,
413 if (it.netname == weapon)
415 player.weapons |= it.m_wepset;
422 bool PlayerTemplateHook_ForbidThrowCurrentWeapon(string template)
424 return !PlayerTemplate_GetFloatValue(template, "drop_weapons");
427 float PlayerTemplateHook_PlayerRegen(entity player, string template)
429 if (template == "default")
433 M_ARGV(5, float) = PlayerTemplate_GetFloatValue(template,
434 "health_regen_factor");
435 M_ARGV(6, float) = PlayerTemplate_GetFloatValue(template,
436 "health_regen_linear");
437 M_ARGV(7, float) = PlayerTemplate_GetFloatValue(template,
438 "health_rot_factor");
439 M_ARGV(8, float) = PlayerTemplate_GetFloatValue(template,
440 "health_rot_linear");
441 M_ARGV(9, float) = PlayerTemplate_GetFloatValue(template,
442 "health_regen_stable");
443 M_ARGV(10, float) = PlayerTemplate_GetFloatValue(template,
444 "health_rot_stable");
448 float PlayerTemplateHook_ItemTouch(entity player, entity item, string template)
450 if (template == "default")
452 return MUT_ITEMTOUCH_CONTINUE;
454 switch (item.classname)
456 case "item_health_small":
458 return PlayerTemplate_GivePlayerItem(player, template,
459 "pickup_health_small");
461 case "item_health_medium":
463 return PlayerTemplate_GivePlayerItem(player, template,
464 "pickup_health_medium");
466 case "item_health_big":
468 return PlayerTemplate_GivePlayerItem(player, template,
469 "pickup_health_big");
471 case "item_health_mega":
473 return PlayerTemplate_GivePlayerItem(player, template,
474 "pickup_health_mega");
476 case "item_armor_small":
478 return PlayerTemplate_GivePlayerItem(player, template,
479 "pickup_armor_small");
481 case "item_armor_medium":
483 return PlayerTemplate_GivePlayerItem(player, template,
484 "pickup_armor_medium");
486 case "item_armor_big":
488 return PlayerTemplate_GivePlayerItem(player, template,
491 case "item_armor_mega":
493 return PlayerTemplate_GivePlayerItem(player, template,
494 "pickup_armor_mega");
498 return PlayerTemplate_GivePlayerItem(player, template,
499 "pickup_item_shells");
503 return PlayerTemplate_GivePlayerItem(player, template,
504 "pickup_item_bullets");
508 return PlayerTemplate_GivePlayerItem(player, template,
509 "pickup_item_rockets");
513 return PlayerTemplate_GivePlayerItem(player, template,
514 "pickup_item_cells");
518 return PlayerTemplate_GivePlayerItem(player, template,
519 "pickup_item_plasma");
523 return PlayerTemplate_GivePlayerItem(player, template,
526 case "weapon_blaster":
528 return PlayerTemplate_GivePlayerItem(player, template,
529 "pickup_weapon_blaster");
531 case "weapon_shotgun":
533 return PlayerTemplate_GivePlayerItem(player, template,
534 "pickup_weapon_shotgun");
536 case "weapon_machinegun":
538 return PlayerTemplate_GivePlayerItem(player, template,
539 "pickup_weapon_machinegun");
541 case "weapon_mortar":
543 return PlayerTemplate_GivePlayerItem(player, template,
544 "pickup_weapon_mortar");
546 case "weapon_electro":
548 return PlayerTemplate_GivePlayerItem(player, template,
549 "pickup_weapon_electro");
551 case "weapon_crylink":
553 return PlayerTemplate_GivePlayerItem(player, template,
554 "pickup_weapon_crylink");
556 case "weapon_vortex":
558 return PlayerTemplate_GivePlayerItem(player, template,
559 "pickup_weapon_vortex");
563 return PlayerTemplate_GivePlayerItem(player, template,
564 "pickup_weapon_hagar");
566 case "weapon_devastator":
568 return PlayerTemplate_GivePlayerItem(player, template,
569 "pickup_weapon_devastator");
571 case "weapon_shockwave":
573 return PlayerTemplate_GivePlayerItem(player, template,
574 "pickup_weapon_shockwave");
578 return PlayerTemplate_GivePlayerItem(player, template,
579 "pickup_weapon_arc");
583 return PlayerTemplate_GivePlayerItem(player, template,
584 "pickup_weapon_hook");
588 return PlayerTemplate_GivePlayerItem(player, template,
589 "pickup_weapon_tuba");
593 return PlayerTemplate_GivePlayerItem(player, template,
594 "pickup_weapon_porto");
596 case "weapon_fireball":
598 return PlayerTemplate_GivePlayerItem(player, template,
599 "pickup_weapon_fireball");
601 case "weapon_minelayer":
603 return PlayerTemplate_GivePlayerItem(player, template,
604 "pickup_weapon_minelayer");
608 return PlayerTemplate_GivePlayerItem(player, template,
609 "pickup_weapon_hlac");
613 return PlayerTemplate_GivePlayerItem(player, template,
614 "pickup_weapon_rifle");
616 case "weapon_seeker":
618 return PlayerTemplate_GivePlayerItem(player, template,
619 "pickup_weapon_seeker");
621 case "weapon_vaporizer":
623 return PlayerTemplate_GivePlayerItem(player, template,
624 "pickup_weapon_vaporizer");
628 return PlayerTemplate_GivePlayerItem(player, template,
629 "pickup_weapon_hmg");
633 return PlayerTemplate_GivePlayerItem(player, template,
634 "pickup_weapon_rpc");
636 case "droppedweapon":
640 case WEP_BLASTER.m_id:
642 return PlayerTemplate_GivePlayerItem(player, template,
643 "pickup_weapon_shotgun");
645 case WEP_SHOTGUN.m_id:
647 return PlayerTemplate_GivePlayerItem(player, template,
648 "pickup_weapon_shotgun");
650 case WEP_MACHINEGUN.m_id:
652 return PlayerTemplate_GivePlayerItem(player, template,
653 "pickup_weapon_machinegun");
655 case WEP_MORTAR.m_id:
657 return PlayerTemplate_GivePlayerItem(player, template,
658 "pickup_weapon_mortar");
660 case WEP_ELECTRO.m_id:
662 return PlayerTemplate_GivePlayerItem(player, template,
663 "pickup_weapon_electro");
665 case WEP_CRYLINK.m_id:
667 return PlayerTemplate_GivePlayerItem(player, template,
668 "pickup_weapon_crylink");
670 case WEP_VORTEX.m_id:
672 return PlayerTemplate_GivePlayerItem(player, template,
673 "pickup_weapon_vortex");
677 return PlayerTemplate_GivePlayerItem(player, template,
678 "pickup_weapon_hagar");
680 case WEP_DEVASTATOR.m_id:
682 return PlayerTemplate_GivePlayerItem(player, template,
683 "pickup_weapon_devastator");
685 case WEP_SHOCKWAVE.m_id:
687 return PlayerTemplate_GivePlayerItem(player, template,
688 "pickup_weapon_shockwave");
692 return PlayerTemplate_GivePlayerItem(player, template,
693 "pickup_weapon_arc");
697 return PlayerTemplate_GivePlayerItem(player, template,
698 "pickup_weapon_hook");
702 return PlayerTemplate_GivePlayerItem(player, template,
703 "pickup_weapon_tuba");
707 return PlayerTemplate_GivePlayerItem(player, template,
708 "pickup_weapon_porto");
710 case WEP_FIREBALL.m_id:
712 return PlayerTemplate_GivePlayerItem(player, template,
713 "pickup_weapon_fireball");
715 case WEP_MINE_LAYER.m_id:
717 return PlayerTemplate_GivePlayerItem(player, template,
718 "pickup_weapon_minelayer");
722 return PlayerTemplate_GivePlayerItem(player, template,
723 "pickup_weapon_hlac");
727 return PlayerTemplate_GivePlayerItem(player, template,
728 "pickup_weapon_rifle");
730 case WEP_SEEKER.m_id:
732 return PlayerTemplate_GivePlayerItem(player, template,
733 "pickup_weapon_seeker");
735 case WEP_VAPORIZER.m_id:
737 return PlayerTemplate_GivePlayerItem(player, template,
738 "pickup_weapon_vaporizer");
742 return PlayerTemplate_GivePlayerItem(player, template,
743 "pickup_weapon_hmg");
747 return PlayerTemplate_GivePlayerItem(player, template,
748 "pickup_weapon_rpc");
752 PrintToChatAll(strcat("Unrecognized item, classname: ",
753 item.classname, " netname: ", item.netname));
754 return MUT_ITEMTOUCH_CONTINUE;
758 case "replacedweapon":
762 case WEP_MINE_LAYER.m_id:
764 return PlayerTemplate_GivePlayerItem(player, template,
765 "pickup_weapon_minelayer");
769 return PlayerTemplate_GivePlayerItem(player, template,
770 "pickup_weapon_hlac");
774 return PlayerTemplate_GivePlayerItem(player, template,
775 "pickup_weapon_rifle");
777 case WEP_SEEKER.m_id:
779 return PlayerTemplate_GivePlayerItem(player, template,
780 "pickup_weapon_seeker");
784 PrintToChatAll(strcat("Unrecognized item, classname: ",
785 item.classname, " netname: ", item.netname));
786 return MUT_ITEMTOUCH_CONTINUE;
790 case "item_strength":
792 return PlayerTemplate_GivePlayerItem(player, template,
793 "pickup_item_strength");
795 case "item_invincible":
797 return PlayerTemplate_GivePlayerItem(player, template,
798 "pickup_item_shield");
800 case "item_fuel_regen":
802 return PlayerTemplate_GivePlayerItem(player, template,
803 "pickup_item_fuel_regen");
807 return PlayerTemplate_GivePlayerItem(player, template,
808 "pickup_item_jetpack");
810 case "item_vaporizer_cells":
812 return PlayerTemplate_GivePlayerItem(player, template,
813 "pickup_item_vaporizer_cells");
815 case "item_invisibility":
817 return PlayerTemplate_GivePlayerItem(player, template,
818 "pickup_item_invisibility");
820 case "item_extralife":
822 return PlayerTemplate_GivePlayerItem(player, template,
823 "pickup_item_extralife");
827 return PlayerTemplate_GivePlayerItem(player, template,
828 "pickup_item_speed");
832 switch (item.netname)
836 PrintToChatAll(strcat("Unrecognized item, classname: ",
837 item.classname, " netname: ", item.netname));
838 return MUT_ITEMTOUCH_CONTINUE;
843 return MUT_ITEMTOUCH_CONTINUE;
846 float PlayerTemplateHook_Damage_Calculate(entity attacker,
847 string attackertemplate, entity victim, string victimtemplate,
848 float deathtype, float damage)
850 if (autocvar_g_instagib == 1)
854 if ((attacker == victim) && (DEATH_ISWEAPON(deathtype, WEP_BLASTER)) &&
855 (PlayerTemplate_GetFloatValue(victimtemplate, "blaster_self_damage") ==
860 damage *= PlayerTemplate_GetFloatValue(attackertemplate, "attack_scale");
861 damage /= PlayerTemplate_GetFloatValue(victimtemplate, "defense_scale");
865 void PlayerTemplateHook_PlayerDies(entity player, string template)
867 if (template == "default")
871 if (PlayerTemplate_GetFloatValue(template, "drop_weapons"))
875 player.weapons = WEPSET(Null);