]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/weapons/throwing.qc
a979b7b0c65f9689e33be33270414fca26d37ba7
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / weapons / throwing.qc
1 #include "throwing.qh"
2
3 #include <common/items/item.qh>
4 #include <common/mapinfo.qh>
5 #include <common/mapobjects/subs.qh>
6 #include <common/mutators/mutator/status_effects/_mod.qh>
7 #include <common/notifications/all.qh>
8 #include <common/resources/sv_resources.qh>
9 #include <common/state.qh>
10 #include <common/util.qh>
11 #include <common/weapons/_all.qh>
12 #include <common/wepent.qh>
13 #include <server/damage.qh>
14 #include <server/items/items.qh>
15 #include <server/items/spawning.qh>
16 #include <server/mutators/_mod.qh>
17 #include <server/weapons/selection.qh>
18 #include <server/weapons/weaponsystem.qh>
19 #include <server/world.qh>
20
21 // returns amount of ammo used, or -1 for failure, or 0 for no ammo count
22 float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity)
23 {
24         Weapon info = REGISTRY_GET(Weapons, wpn);
25         Resource ammotype = info.ammo_type;
26
27         entity wep = spawn();
28         Item_SetLoot(wep, true);
29         setorigin(wep, org);
30         wep.velocity = velo;
31         wep.owner = wep.enemy = own;
32         wep.flags |= FL_TOSSED;
33         wep.colormap = own.colormap;
34         // wep.glowmod will be set in weapon_defaultspawnfunc
35         navigation_dynamicgoal_init(wep, false);
36
37         W_DropEvent(wr_drop,own,wpn,wep,weaponentity);
38
39         if(WepSet_FromWeapon(REGISTRY_GET(Weapons, wpn)) & WEPSET_SUPERWEAPONS)
40         {
41                 Item_SetExpiring(wep, true);
42                 if(own.items & IT_UNLIMITED_SUPERWEAPONS)
43                 {
44                         wep.superweapons_finished = time + autocvar_g_balance_superweapons_time;
45                 }
46                 else
47                 {
48                         int superweapons = 1;
49                         FOREACH(Weapons, it != WEP_Null, {
50                                 WepSet set = it.m_wepset;
51                                 if((set & WEPSET_SUPERWEAPONS) && (STAT(WEAPONS, own) & set)) ++superweapons;
52                         });
53                         if(superweapons <= 1)
54                         {
55                                 wep.superweapons_finished = StatusEffects_gettime(STATUSEFFECT_Superweapons, own);
56                                 StatusEffects_remove(STATUSEFFECT_Superweapons, own, STATUSEFFECT_REMOVE_CLEAR);
57                         }
58                         else
59                         {
60                                 float timeleft = StatusEffects_gettime(STATUSEFFECT_Superweapons, own) - time;
61                                 float weptimeleft = timeleft / superweapons;
62                                 wep.superweapons_finished = time + weptimeleft;
63                                 if(own.statuseffects)
64                                 {
65                                         // TODO: this doesn't explicitly enable the effect, use apply here?
66                                         own.statuseffects.statuseffect_time[STATUSEFFECT_Superweapons.m_id] -= weptimeleft;
67                                         StatusEffects_update(own);
68                                 }
69                         }
70                 }
71         }
72
73         weapon_defaultspawnfunc(wep, info);
74         if(startitem_failed)
75                 return -1;
76
77         wep.pickup_anyway = true; // these are ALWAYS pickable
78
79         //wa = W_AmmoItemCode(wpn);
80         if(ammotype == RES_NONE)
81         {
82                 return 0;
83         }
84         else
85         {
86                 if(doreduce && g_weapon_stay == 2)
87                 {
88                         // if our weapon is loaded, give its load back to the player
89                         int i = own.(weaponentity).m_weapon.m_id;
90                         if(own.(weaponentity).(weapon_load[i]) > 0)
91                         {
92                                 GiveResource(own, ammotype, own.(weaponentity).(weapon_load[i]));
93                                 own.(weaponentity).(weapon_load[i]) = -1; // schedule the weapon for reloading
94                         }
95                         SetResource(wep, ammotype, 0);
96                 }
97                 else if(doreduce)
98                 {
99                         // if our weapon is loaded, give its load back to the player
100                         int i = own.(weaponentity).m_weapon.m_id;
101                         if(own.(weaponentity).(weapon_load[i]) > 0)
102                         {
103                                 GiveResource(own, ammotype, own.(weaponentity).(weapon_load[i]));
104                                 own.(weaponentity).(weapon_load[i]) = -1; // schedule the weapon for reloading
105                         }
106
107                         float ownderammo = GetResource(own, ammotype);
108                         float thisammo = min(ownderammo, GetResource(wep, ammotype));
109                         SetResource(wep, ammotype, thisammo);
110                         SetResource(own, ammotype, ownderammo - thisammo);
111
112                         return thisammo;
113                 }
114                 return 0;
115         }
116 }
117
118 bool W_IsWeaponThrowable(entity this, int w)
119 {
120         if (MUTATOR_CALLHOOK(ForbidDropCurrentWeapon, this, w))
121                 return false;
122         if (!autocvar_g_pickup_items)
123                 return false;
124         if (g_weaponarena)
125                 return false;
126         if (w == WEP_Null.m_id)
127                 return false;
128
129         return (REGISTRY_GET(Weapons, w)).weaponthrowable;
130 }
131
132 // toss current weapon
133 void W_ThrowWeapon(entity this, .entity weaponentity, vector velo, vector delta, float doreduce)
134 {
135         Weapon w = this.(weaponentity).m_weapon;
136         if (w == WEP_Null)
137                 return; // just in case
138         if (time < game_starttime)
139                 return;
140         if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon, this, this.(weaponentity)))
141                 return;
142         if(!autocvar_g_weapon_throwable)
143                 return;
144         if(this.(weaponentity).state != WS_READY)
145                 return;
146         if(!W_IsWeaponThrowable(this, w.m_id))
147                 return;
148
149         WepSet set = WepSet_FromWeapon(w);
150         if(!(STAT(WEAPONS, this) & set)) return;
151         STAT(WEAPONS, this) &= ~set;
152
153         W_SwitchWeapon_Force(this, w_getbestweapon(this, weaponentity), weaponentity);
154         float a = W_ThrowNewWeapon(this, w.m_id, doreduce, this.origin + delta, velo, weaponentity);
155
156         if(a < 0) return;
157         Send_Notification(NOTIF_ONE, this, MSG_MULTI, ITEM_WEAPON_DROP, w.m_id, a);
158 }
159
160 void SpawnThrownWeapon(entity this, vector org, Weapon wep, .entity weaponentity)
161 {
162         //entity wep = this.(weaponentity).m_weapon;
163
164         if(STAT(WEAPONS, this) & WepSet_FromWeapon(wep))
165                 if(W_IsWeaponThrowable(this, wep.m_id))
166                         W_ThrowNewWeapon(this, wep.m_id, false, org, randomvec() * 125 + '0 0 200', weaponentity);
167 }