Merge branch 'master' into DefaultUser/func_button_relay
[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 (GetResourceAmount(this, RESOURCE_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         SetResourceAmountExplicit(this, RESOURCE_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         SetResourceAmountExplicit(this, RESOURCE_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         setthink(this, func_null);
109         this.nextthink = 0;
110         if (GetResourceAmount(this, RESOURCE_HEALTH))
111                 this.takedamage = DAMAGE_YES;   // can be shot again
112 }
113
114 void button_use(entity this, entity actor, entity trigger)
115 {
116         if(this.active != ACTIVE_ACTIVE)
117                 return;
118
119         this.enemy = actor;
120         button_fire(this);
121 }
122
123 void button_touch(entity this, entity toucher)
124 {
125         if (this.active != ACTIVE_ACTIVE)
126                 return;
127         if (!toucher)
128                 return;
129         if (!toucher.iscreature)
130                 return;
131         if(toucher.velocity * this.movedir < 0)
132                 return;
133         this.enemy = toucher;
134         if (toucher.owner)
135                 this.enemy = toucher.owner;
136         button_fire (this);
137 }
138
139 void button_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
140 {
141         if (this.active != ACTIVE_ACTIVE)
142                 return;
143         if(this.spawnflags & NOSPLASH)
144                 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
145                         return;
146         if (this.spawnflags & BUTTON_DONTACCUMULATEDMG)
147         {
148                 if (GetResourceAmount(this, RESOURCE_HEALTH) <= damage)
149                 {
150                         this.enemy = attacker;
151                         button_fire(this);
152                 }
153         }
154         else
155         {
156                 TakeResource(this, RESOURCE_HEALTH, damage);
157                 if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
158                 {
159                         this.enemy = attacker;
160                         button_fire(this);
161                 }
162         }
163 }
164
165
166 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
167 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.
168
169 "angle"         determines the opening direction
170 "target"        all entities with a matching targetname will be used
171 "speed"         override the default 40 speed
172 "wait"          override the default 1 second wait (-1 = never return)
173 "lip"           override the default 4 pixel lip remaining at end of move
174 "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
175 "noise"         sound that is played when the button is activated
176 */
177 spawnfunc(func_button)
178 {
179         SetMovedir(this);
180
181         if (!InitMovingBrushTrigger(this))
182                 return;
183         this.effects |= EF_LOWPRECISION;
184
185         setblocked(this, button_blocked);
186         this.use = button_use;
187
188 //      if (this.health == 0) // all buttons are now shootable
189 //              this.health = 10;
190         if (GetResourceAmount(this, RESOURCE_HEALTH))
191         {
192                 this.max_health = GetResourceAmount(this, RESOURCE_HEALTH);
193                 this.event_damage = button_damage;
194                 this.takedamage = DAMAGE_YES;
195         }
196         else
197                 settouch(this, button_touch);
198
199         if (!this.speed)
200                 this.speed = 40;
201         if (!this.wait)
202                 this.wait = 1;
203         if (!this.lip)
204                 this.lip = 4;
205
206         this.wait_remaining = -1;
207         this.activation_time = -1;
208
209     if(this.noise != "")
210         precache_sound(this.noise);
211
212         this.active = ACTIVE_ACTIVE;
213         this.draggable = drag_undraggable;
214
215         this.setactive = button_setactive;
216
217         this.pos1 = this.origin;
218         this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip);
219     this.flags |= FL_NOTARGET;
220
221     this.reset = button_reset;
222
223         button_reset(this);
224 }
225 #endif