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