Rename sv_vq3compat to sv_q3defragcompat to clarify its purpose
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mapobjects / func / button.qc
1 #include "button.qh"
2 #ifdef SVQC
3 // button and multiple button
4
5 void button_wait(entity this);
6 void button_return(entity this);
7
8 // in case button is deactivated by a relay_deactivate while it pressed down
9 // set both fields to -1 in button_return!!
10 .float wait_remaining;
11 .float activation_time;
12
13 void button_setactive(entity this, int astate)
14 {
15         int oldstate = this.active;
16         if (astate == ACTIVE_TOGGLE)
17         {
18                 if (this.active == ACTIVE_ACTIVE)
19                         this.active = ACTIVE_NOT;
20                 else
21                         this.active = ACTIVE_ACTIVE;
22         }
23         else
24                 this.active = astate;
25
26         if (this.active == ACTIVE_ACTIVE && oldstate == ACTIVE_NOT)
27         {
28                 // button was deactivated while it was pressed
29                 if (this.wait_remaining >= 0)
30                 {
31                         this.nextthink =  this.wait_remaining + this.ltime;
32                         setthink(this, button_return);
33                 }
34         }
35         else if (this.active == ACTIVE_NOT && oldstate == ACTIVE_ACTIVE)
36         {
37                 // check if button is in pressed state
38                 if (this.activation_time >= 0)
39                 {
40                         this.wait_remaining = this.wait - (time - this.activation_time);
41                 }
42         }
43 }
44
45 void button_wait(entity this)
46 {
47         this.state = STATE_TOP;
48         if(this.wait >= 0)
49         {
50                 this.nextthink = this.ltime + this.wait;
51                 setthink(this, button_return);
52         }
53         SUB_UseTargets(this, this.enemy, NULL);
54         this.frame = 1;                 // use alternate textures
55 }
56
57 void button_done(entity this)
58 {
59         this.state = STATE_BOTTOM;
60 }
61
62 void button_return(entity this)
63 {
64         if (this.active != ACTIVE_ACTIVE)
65         {
66                 return;
67         }
68         this.state = STATE_DOWN;
69         SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, button_done);
70         this.frame = 0;                 // use normal textures
71         if (GetResource(this, RES_HEALTH))
72                 this.takedamage = DAMAGE_YES;   // can be shot again
73         this.wait_remaining = -1;
74         this.activation_time = -1;
75 }
76
77
78 void button_blocked(entity this, entity blocker)
79 {
80         // do nothing, just don't come all the way back out
81 }
82
83
84 void button_fire(entity this)
85 {
86         SetResourceExplicit(this, RES_HEALTH, this.max_health);
87         this.takedamage = DAMAGE_NO;    // will be reset upon return
88
89         if (this.state == STATE_UP || this.state == STATE_TOP)
90                 return;
91
92         this.activation_time = time;
93
94         if (this.noise != "")
95                 _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
96
97         this.state = STATE_UP;
98         SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, button_wait);
99 }
100
101 void button_reset(entity this)
102 {
103         SetResourceExplicit(this, RES_HEALTH, this.max_health);
104         setorigin(this, this.pos1);
105         this.frame = 0;                 // use normal textures
106         this.state = STATE_BOTTOM;
107         this.velocity = '0 0 0';
108         this.wait_remaining = -1;
109         this.activation_time = -1;
110         this.active = ACTIVE_ACTIVE;
111         setthink(this, func_null);
112         this.nextthink = 0;
113         if (GetResource(this, RES_HEALTH))
114                 this.takedamage = DAMAGE_YES;   // can be shot again
115 }
116
117 void button_use(entity this, entity actor, entity trigger)
118 {
119         if(this.active != ACTIVE_ACTIVE)
120                 return;
121
122         this.enemy = actor;
123         button_fire(this);
124 }
125
126 void button_touch(entity this, entity toucher)
127 {
128         if (this.active != ACTIVE_ACTIVE)
129                 return;
130         if (!toucher)
131                 return;
132         if (!toucher.iscreature)
133                 return;
134         if(toucher.velocity * this.movedir < 0)
135                 return;
136         this.enemy = toucher;
137         if (toucher.owner)
138                 this.enemy = toucher.owner;
139         button_fire (this);
140 }
141
142 void button_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
143 {
144         if (this.active != ACTIVE_ACTIVE)
145                 return;
146         if(this.spawnflags & NOSPLASH)
147                 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
148                         return;
149         if (this.spawnflags & BUTTON_DONTACCUMULATEDMG)
150         {
151                 if (GetResource(this, RES_HEALTH) <= damage)
152                 {
153                         this.enemy = attacker;
154                         button_fire(this);
155                 }
156         }
157         else
158         {
159                 TakeResource(this, RES_HEALTH, damage);
160                 if (GetResource(this, RES_HEALTH) <= 0)
161                 {
162                         this.enemy = attacker;
163                         button_fire(this);
164                 }
165         }
166 }
167
168
169 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
170 When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
171
172 "angle"         determines the opening direction
173 "target"        all entities with a matching targetname will be used
174 "speed"         override the default 40 speed
175 "wait"          override the default 1 second wait (-1 = never return)
176 "lip"           override the default 4 pixel lip remaining at end of move
177 "health"        if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser
178 "noise"         sound that is played when the button is activated
179 */
180 spawnfunc(func_button)
181 {
182         SetMovedir(this);
183
184         if (!InitMovingBrushTrigger(this))
185                 return;
186         this.effects |= EF_LOWPRECISION;
187
188         setblocked(this, button_blocked);
189         this.use = button_use;
190
191 //      if (this.health == 0) // all buttons are now shootable
192 //              this.health = 10;
193         if (GetResource(this, RES_HEALTH))
194         {
195                 this.max_health = GetResource(this, RES_HEALTH);
196                 this.event_damage = button_damage;
197                 this.takedamage = DAMAGE_YES;
198         }
199         else
200                 settouch(this, button_touch);
201
202         if (!this.speed)
203                 this.speed = 40;
204         if (!this.wait)
205                 this.wait = 1;
206         if (!this.lip)
207                 this.lip = 4;
208
209         if(this.wait == -1 && autocvar_sv_q3defragcompat)
210                 this.wait = 0.1; // compatibility for q3df: "instant" return
211
212     if(this.noise != "")
213         precache_sound(this.noise);
214
215         this.draggable = drag_undraggable;
216
217         this.setactive = button_setactive;
218
219         this.pos1 = this.origin;
220         this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip);
221     this.flags |= FL_NOTARGET;
222
223     this.reset = button_reset;
224
225         button_reset(this);
226 }
227 #endif