Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
multi.qc
Go to the documentation of this file.
1#include "multi.qh"
2// NOTE: also contains trigger_once at bottom
3
4#ifdef SVQC
6
7// the wait time has passed, so set back up for another activation
8void multi_wait(entity this)
9{
10 if (this.max_health)
11 {
12 SetResourceExplicit(this, RES_HEALTH, this.max_health);
14 this.solid = SOLID_BBOX;
15 }
16}
17
18
19// the trigger was just touched/killed/used
20// this.enemy should be set to the activator so it can be held through a delay
21// so wait for the delay time before firing
22void multi_trigger(entity this, bool exacttrigger)
23{
24 if((this.spawnflags & ONLY_PLAYERS) && !IS_PLAYER(this.enemy))
25 {
26 return; // only players
27 }
28
29 // In Q3 .wait <= 0 is forever,
30 // in Xonotic .wait == -1 means forever and == -2 means don't wait at all.
31 if (IS_GAMETYPE(CTS) && IS_CLIENT(this.enemy))
32 {
33 // check the client-specific trigger time
34 int cnum = etof(this.enemy);
35 if (buf_getsize(this.triggertimes) >= cnum) // buffer slot exists
36 {
37 float triggertime = stof(bufstr_get(this.triggertimes, cnum - 1));
38 if (this.enemy.spawn_time <= triggertime) // havn't respawned since triggering
39 {
40 if ((this.wait <= 0 && (q3compat || this.wait >= -1)) // wait forever (until respawn in CTS)
41 || triggertime + this.wait > time) // too soon
42 return;
43 }
44 }
45 }
46 else if (this.nextthink > time)
47 return; // allready been triggered
48
49 if (exacttrigger)
50 EXACTTRIGGER_TOUCH(this, this.enemy);
51
52 if (this.noise && this.noise != "")
53 {
55 }
56
57 // don't trigger again until reset
58 this.takedamage = DAMAGE_NO;
59
60 SUB_UseTargets(this, this.enemy, this.goalentity);
61
62 if (IS_GAMETYPE(CTS) && IS_CLIENT(this.enemy))
63 {
64 // store the client-specific trigger time
65 string triggertime = sprintf("%15.14g", time); // 16b, perfect precision until timelimit_max @ <= 512hz (32768 sec)
66 int cnum = etof(this.enemy);
67 int bsize = buf_getsize(this.triggertimes);
68 if (bsize >= cnum) // already got a slot
69 bufstr_set(this.triggertimes, cnum - 1, triggertime);
70 else while (bsize < cnum) // create any lower numbered slots too
71 bufstr_add(this.triggertimes, (++bsize == cnum ? triggertime : "0"), true);
72 }
73 else if (this.wait > 0)
74 {
75 setthink(this, multi_wait);
76 this.nextthink = time + this.wait;
77 }
78 else if (this.wait < -1 && !q3compat) // xon maps only: no waiting
79 {
80 multi_wait(this); // waiting finished
81 }
82 else
83 {
84 if (IS_GAMETYPE(CTS)) // client-specific .wait timers
85 this.nextthink = stof("inf"); // clients can trigger again after respawning, disabled for other touchers
86 else
87 {
88 // we can't just delete(this) here, because this is a touch function
89 // called while C code is looping through area links...
90 settouch(this, func_null);
91 this.use = func_null;
92 }
93 }
94}
95
96void multi_use(entity this, entity actor, entity trigger)
97{
98 this.goalentity = trigger;
99 this.enemy = actor;
100 multi_trigger(this, false);
101}
102
104{
105 if(!q3compat && !(this.spawnflags & ALL_ENTITIES) && !toucher.iscreature)
106 {
107 return;
108 }
109
110 if (q3compat && AVAILABLE_TEAMS == 2)
111 {
112 // This feature isn't mentioned in entities.def but it's in the source.
113 // Xonotic has given these spawnflags other meanings.
114 if(((this.spawnflags & 1) && toucher.team != NUM_TEAM_1) // not on red
115 || ((this.spawnflags & 2) && toucher.team != NUM_TEAM_2)) // not on blue
116 return;
117 }
118 else if (this.team)
119 {
120 if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team))
121 {
122 return;
123 }
124 }
125
126 // if the trigger has an angles field, check player's facing direction
127 if (this.movedir != '0 0 0')
128 {
129 makevectors (toucher.angles);
130 if (v_forward * this.movedir < 0)
131 return; // not facing the right way
132 }
133
134 // if the trigger has pressed keys, check that the player is pressing those keys
135 if(this.pressedkeys && IS_PLAYER(toucher)) // only for players
136 {
137 if(!(CS(toucher).pressedkeys & this.pressedkeys))
138 {
139 return;
140 }
141 }
142
143 this.enemy = toucher;
144 this.goalentity = toucher;
145 multi_trigger(this, true);
146}
147
148void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
149{
150 if(!this.takedamage)
151 return;
152 if(this.spawnflags & NOSPLASH)
153 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
154 return;
155 if(this.team)
156 if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != attacker.team))
157 return;
158 TakeResource(this, RES_HEALTH, damage);
159 if (GetResource(this, RES_HEALTH) <= 0)
160 {
161 this.enemy = attacker;
162 this.goalentity = inflictor;
163 multi_trigger(this, false);
164 }
165}
166
168{
169 if (q3compat || !(this.spawnflags & SPAWNFLAG_NOTOUCH))
170 settouch(this, multi_touch);
171 if (this.max_health)
172 {
173 SetResourceExplicit(this, RES_HEALTH, this.max_health);
174 this.takedamage = DAMAGE_YES;
175 this.solid = SOLID_BBOX;
176 }
177 setthink(this, func_null);
178 this.nextthink = 0;
179 this.team = this.team_saved;
180 this.use = multi_use;
181}
182
183/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
184Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time.
185If "delay" is set, the trigger waits some time after activating before firing.
186"wait" : Seconds between triggerings. (.2 default)
187If notouch is set, the trigger is only fired by other entities, not by touching.
188NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
189sounds
1901) secret
1912) beep beep
1923) large switch
1934)
194set "message" to text string
195*/
196spawnfunc(trigger_multiple)
197{
198 this.reset = multi_reset;
199 if (this.sounds == 1)
200 this.noise = "misc/secret.wav";
201 else if (this.sounds == 2)
202 this.noise = strzone(SND(TALK));
203 else if (this.sounds == 3)
204 this.noise = "misc/trigger1.wav";
205
206 if(this.noise && this.noise != "")
207 precache_sound(this.noise);
208
209 if (q3compat)
210 {
211 if (!this.wait)
212 {
213 string s = GetField_fullspawndata(this, "wait", false);
214 if (!s || s == "") // it's really not set ("0" or "foo" waits forever)
215 this.wait = 0.5;
216 }
217 }
218 else if (!this.wait)
219 this.wait = 0.2;
220
221 this.use = multi_use;
222
224
225 // TEAMNUMBERS_THAT_ARENT_STUPID TODO: need to convert this.team to index, maybe add .teamindex
226 this.team_saved = this.team;
227 IL_PUSH(g_saved_team, this);
228
229 // health/damage mode isn't supported in Q3 or with CTS client-specific .wait timers
230 if (GetResource(this, RES_HEALTH) && !q3compat && !IS_GAMETYPE(CTS))
231 {
232 if (this.spawnflags & SPAWNFLAG_NOTOUCH)
233 objerror (this, "health and notouch don't make sense\n");
234 this.canteamdamage = true;
235 this.max_health = GetResource(this, RES_HEALTH);
236 this.event_damage = multi_eventdamage;
237 this.takedamage = DAMAGE_YES;
238 this.solid = SOLID_BBOX;
239 setorigin(this, this.origin); // make sure it links into the world
240 }
241 else
242 {
243 if (q3compat || !(this.spawnflags & SPAWNFLAG_NOTOUCH))
244 settouch(this, multi_touch);
245
246 if (IS_GAMETYPE(CTS)) // client-specific .wait timers
247 if ((this.triggertimes = buf_create()) < 0)
248 LOG_FATAL("trigger_multiple: wait timer buffer creation failed somehow!\n");
249 }
250}
251
252
253/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
254Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching
255"targetname". If "health" is set, the trigger must be killed to activate.
256If notouch is set, the trigger is only fired by other entities, not by touching.
257if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
258if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0.
259sounds
2601) secret
2612) beep beep
2623) large switch
2634)
264set "message" to text string
265*/
266spawnfunc(trigger_once)
267{
268 this.wait = -1;
269 spawnfunc_trigger_multiple(this);
270}
271#endif
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float max_health
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
void TakeResource(entity receiver, Resource res_type, float amount)
Takes an entity some resource.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
float wait
Definition items.qc:17
int team
Definition main.qh:188
int spawnflags
Definition ammo.qh:15
#define IS_CLIENT(s)
Definition player.qh:241
#define IS_PLAYER(s)
Definition player.qh:242
const float SOLID_BBOX
float time
float nextthink
vector v_forward
vector origin
#define use
bool canteamdamage
Definition damage.qh:65
#define DEATH_ISSPECIAL(t)
Definition all.qh:39
const int HITTYPE_SPLASH
Definition all.qh:30
const int INVERT_TEAMS
Definition defs.qh:10
const int ALL_ENTITIES
Definition defs.qh:8
const int ONLY_PLAYERS
Definition defs.qh:13
const int NOSPLASH
Definition defs.qh:12
const int SPAWNFLAG_NOTOUCH
Definition defs.qh:17
#define buf_create
solid
Definition ent_cs.qc:165
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition triggers.qc:344
#define EXACTTRIGGER_TOUCH(e, t)
Definition common.qh:115
#define EXACTTRIGGER_INIT
Definition common.qh:116
#define LOG_FATAL(...)
Definition log.qh:53
entity goalentity
Definition viewloc.qh:16
vector movedir
Definition viewloc.qh:18
float stof(string val,...)
string precache_sound(string sample)
string strzone(string s)
#define etof(e)
Definition misc.qh:25
void multi_use(entity this, entity actor, entity trigger)
Definition multi.qc:96
void multi_trigger(entity this, bool exacttrigger)
Definition multi.qc:22
int triggertimes
Definition multi.qc:5
void multi_reset(entity this)
Definition multi.qc:167
void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition multi.qc:148
void multi_touch(entity this, entity toucher)
Definition multi.qc:103
void multi_wait(entity this)
Definition multi.qc:8
var void func_null()
#define makevectors
Definition post.qh:21
#define objerror
Definition pre.qh:8
q3compat
Definition quake3.qc:59
#define AVAILABLE_TEAMS
#define setthink(e, f)
vector
Definition self.qh:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
IntrusiveList g_saved_team
Definition vote.qh:79
int team_saved
Definition vote.qh:70
string GetField_fullspawndata(entity e, string fieldname, bool vfspath)
Retrieves the value of a map entity field from fullspawndata.
Definition main.qc:451
const int CH_TRIGGER
Definition sound.qh:12
const float VOL_BASE
Definition sound.qh:36
#define _sound(e, c, s, v, a)
Definition sound.qh:43
const float ATTEN_NORM
Definition sound.qh:30
#define SND(id)
Definition all.qh:35
#define spawnfunc(id)
Definition spawnfunc.qh:96
ClientState CS(Client this)
Definition state.qh:47
string noise
Definition subs.qh:83
const int DAMAGE_YES
Definition subs.qh:80
const int DAMAGE_NO
Definition subs.qh:79
float sounds
Definition subs.qh:42
float takedamage
Definition subs.qh:78
entity enemy
Definition sv_ctf.qh:153
int pressedkeys
const int NUM_TEAM_2
Definition teams.qh:14
const int NUM_TEAM_1
Definition teams.qh:13