Nexball: move weapon code to weapon file
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / gamemodes / gamemode / nexball / sv_weapon.qc
1 #include "sv_weapon.qh"
2
3 void W_Nexball_Attack(entity actor, .entity weaponentity, float t);
4 void W_Nexball_Attack2(entity actor, .entity weaponentity);
5 vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
6
7 METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity weaponentity, int fire))
8 {
9     TC(BallStealer, thiswep);
10     if(fire & 1)
11         if(weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_balance_nexball_primary_refire))
12             if(autocvar_g_nexball_basketball_meter)
13             {
14                 if(actor.ballcarried && !actor.metertime)
15                     actor.metertime = time;
16                 else
17                     weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
18             }
19             else
20             {
21                 W_Nexball_Attack(actor, weaponentity, -1);
22                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
23             }
24     if(fire & 2)
25         if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_nexball_secondary_refire))
26         {
27             W_Nexball_Attack2(actor, weaponentity);
28             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
29         }
30
31     if(!(fire & 1) && actor.metertime && actor.ballcarried)
32     {
33         W_Nexball_Attack(actor, weaponentity, time - actor.metertime);
34         // DropBall or stealing will set metertime back to 0
35         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
36     }
37 }
38
39 METHOD(BallStealer, wr_setup, void(BallStealer this, entity actor, .entity weaponentity))
40 {
41     TC(BallStealer, this);
42     //weapon_setup(WEP_PORTO.m_id);
43 }
44
45 METHOD(BallStealer, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
46 {
47     TC(BallStealer, this);
48 }
49
50 METHOD(BallStealer, wr_checkammo1, bool(BallStealer this, entity actor, .entity weaponentity))
51 {
52     TC(BallStealer, this);
53     return true;
54 }
55
56 METHOD(BallStealer, wr_checkammo2, bool(BallStealer this, entity actor, .entity weaponentity))
57 {
58     TC(BallStealer, this);
59     return true;
60 }
61
62 void W_Nexball_Think(entity this)
63 {
64         //dprint("W_Nexball_Think\n");
65         //vector new_dir = steerlib_arrive(this.enemy.origin, 2500);
66         vector new_dir = normalize(this.enemy.origin + '0 0 50' - this.origin);
67         vector old_dir = normalize(this.velocity);
68         float _speed = vlen(this.velocity);
69         vector new_vel = normalize(old_dir + (new_dir * autocvar_g_nexball_safepass_turnrate)) * _speed;
70         //vector new_vel = (new_dir * autocvar_g_nexball_safepass_turnrate
71
72         this.velocity = new_vel;
73
74         this.nextthink = time;
75 }
76
77 void W_Nexball_Touch(entity this, entity toucher)
78 {
79         entity ball, attacker;
80         attacker = this.owner;
81         //this.think = func_null;
82         //this.enemy = NULL;
83
84         PROJECTILE_TOUCH(this, toucher);
85         if(attacker.team != toucher.team || autocvar_g_nexball_basketball_teamsteal)
86                 if((ball = toucher.ballcarried) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (IS_PLAYER(attacker)))
87                 {
88                         toucher.velocity = toucher.velocity + normalize(this.velocity) * toucher.damageforcescale * autocvar_g_balance_nexball_secondary_force;
89                         UNSET_ONGROUND(toucher);
90                         if(!attacker.ballcarried)
91                         {
92                                 LogNB("stole", attacker);
93                                 _sound(toucher, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM);
94
95                                 if(SAME_TEAM(attacker, toucher) && time > CS(attacker).teamkill_complain)
96                                 {
97                                         CS(attacker).teamkill_complain = time + 5;
98                                         CS(attacker).teamkill_soundtime = time + 0.4;
99                                         CS(attacker).teamkill_soundsource = toucher;
100                                 }
101
102                                 GiveBall(attacker, toucher.ballcarried);
103                         }
104                 }
105         delete(this);
106 }
107
108 void W_Nexball_Attack(entity actor, .entity weaponentity, float t)
109 {
110         entity ball;
111         float mul, mi, ma;
112         if(!(ball = actor.ballcarried))
113                 return;
114
115         W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
116         tracebox(w_shotorg, BALL_MINS, BALL_MAXS, w_shotorg, MOVE_WORLDONLY, NULL);
117         if(trace_startsolid)
118         {
119                 if(actor.metertime)
120                         actor.metertime = 0; // Shot failed, hide the power meter
121                 return;
122         }
123
124         //Calculate multiplier
125         if(t < 0)
126                 mul = 1;
127         else
128         {
129                 mi = autocvar_g_nexball_basketball_meter_minpower;
130                 ma = max(mi, autocvar_g_nexball_basketball_meter_maxpower); // avoid confusion
131                 //One triangle wave period with 1 as max
132                 mul = 2 * (t % g_nexball_meter_period) / g_nexball_meter_period;
133                 if(mul > 1)
134                         mul = 2 - mul;
135                 mul = mi + (ma - mi) * mul; // range from the minimal power to the maximal power
136         }
137
138         DropBall(ball, w_shotorg, W_CalculateProjectileVelocity(actor, actor.velocity, w_shotdir * autocvar_g_balance_nexball_primary_speed * mul, false));
139
140
141         //TODO: use the speed_up cvar too ??
142 }
143
144 void W_Nexball_Attack2(entity actor, .entity weaponentity)
145 {
146         if(actor.ballcarried.enemy)
147         {
148                 entity _ball = actor.ballcarried;
149                 W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
150                 DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
151                 setthink(_ball, W_Nexball_Think);
152                 _ball.nextthink = time;
153                 return;
154         }
155
156         if(!autocvar_g_nexball_tackling)
157                 return;
158
159         W_SetupShot(actor, weaponentity, false, 2, SND_NB_SHOOT2, CH_WEAPON_A, 0);
160         entity missile = new(ballstealer);
161
162         missile.owner = actor;
163
164         set_movetype(missile, MOVETYPE_FLY);
165         PROJECTILE_MAKETRIGGER(missile);
166
167         //setmodel(missile, "models/elaser.mdl");  // precision set below
168         setsize(missile, '0 0 0', '0 0 0');
169         setorigin(missile, w_shotorg);
170
171         W_SetupProjVelocity_Basic(missile, autocvar_g_balance_nexball_secondary_speed, 0);
172         missile.angles = vectoangles(missile.velocity);
173         settouch(missile, W_Nexball_Touch);
174         setthink(missile, SUB_Remove);
175         missile.nextthink = time + autocvar_g_balance_nexball_secondary_lifetime; //FIXME: use a distance instead?
176
177         missile.effects = EF_BRIGHTFIELD | EF_LOWPRECISION;
178         missile.flags = FL_PROJECTILE;
179         IL_PUSH(g_projectiles, missile);
180         IL_PUSH(g_bot_dodge, missile);
181
182         CSQCProjectile(missile, true, PROJECTILE_ELECTRO, true);
183 }