]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/t_teleporters.qc
handle self/oself CORRECTLY this time
[divverent/nexuiz.git] / data / qcsrc / server / t_teleporters.qc
1 void Damage (entity targ, entity inflictor, entity attacker, float
2 damage, float deathtype, vector hitloc, vector force);
3
4 void() tdeath_touch =
5 {
6         if (other == self.owner)
7                 return;
8         // so teleporting shots etc can't telefrag
9         if (!self.owner.takedamage)
10                 return;
11         if (!other.takedamage)
12                 return;
13
14         if ((self.owner.classname == "player") && (self.owner.health >= 1))
15                 Damage (other, self, self.owner, 10000, DEATH_TELEFRAG, other.origin, '0 0 0');
16         else if (other.health < 1) // corpses gib
17                 Damage (other, self, self.owner, 10000, DEATH_TELEFRAG, other.origin, '0 0 0');
18         else // dead bodies and monsters gib themselves instead of telefragging
19                 Damage (self.owner, self, self.owner, 10000, DEATH_TELEFRAG, self.owner.origin, '0 0 0');
20 };
21
22 void tdeath_remove()
23 {
24         if (self.owner)
25         {
26                 self.owner.effects = self.owner.effects - (self.owner.effects & EF_NODRAW);
27                 if (self.owner.weaponentity)
28                         self.owner.weaponentity.flags = self.owner.weaponentity.flags - (self.owner.weaponentity.flags & FL_FLY);
29         }
30         remove(self);
31 }
32
33 // org2 is where they will return to if the teleport fails
34 void(vector org, entity death_owner, vector org2) spawn_tdeath =
35 {
36         local entity death;
37
38         death = spawn();
39 //      death.classname = "teledeath";
40         death.movetype = MOVETYPE_NONE;
41         death.solid = SOLID_TRIGGER;
42         death.angles = '0 0 0';
43         death.dest2 = org2;
44         setsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1');
45         setorigin (death, org);
46         death.touch = tdeath_touch;
47         death.nextthink = time + 0.2;
48         death.think = tdeath_remove;
49         death.owner = death_owner;
50
51         // hide entity to avoid "ghosts" between teleporter and destination caused by clientside interpolation
52         death.owner.effects = death.owner.effects | EF_NODRAW;
53         if (death.owner.weaponentity) // misuse FL_FLY to avoid EF_NODRAW on viewmodel
54                 death.owner.weaponentity.flags = death.owner.weaponentity.flags | FL_FLY;
55
56         force_retouch = 2;              // make sure even still objects get hit
57 };
58
59 void Teleport_Touch (void)
60 {
61         entity head;
62
63         if (other.health < 1)
64                 return;
65         if (!other.flags & FL_CLIENT)   // FIXME: Make missiles firable through the teleport too
66                 return;
67
68         // Make teleport effect where the player left
69         sound (other, CHAN_ITEM, "misc/teleport.wav", 1, ATTN_NORM);
70         te_teleport (other.origin);
71
72         // Make teleport effect where the player arrived
73         sound (other, CHAN_ITEM, "misc/teleport.wav", 1, ATTN_NORM);
74         makevectors (self.enemy.mangle);
75         te_teleport (self.enemy.origin + v_forward * 32);
76
77         // Relocate the player
78         setorigin (other, self.enemy.origin + '0 0 1' * (1 - other.mins_z - 24));
79         other.angles = self.enemy.mangle;
80         other.fixangle = TRUE;
81         other.velocity = v_forward * vlen(other.velocity);
82
83         // Kill anyone else in the teleporter box (NO MORE TDEATH)
84         if(other.takedamage)
85         {
86                 vector deathmin;
87                 vector deathmax;
88                 float deathradius;
89                 deathmin = other.absmin;
90                 deathmax = other.absmax;
91                 deathradius = max(vlen(deathmin), vlen(deathmax));
92                 for(head = findradius(other.origin, deathradius); head; head = head.chain)
93                         if(head != other)
94                                 if(head.takedamage)
95                                         if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax))
96                                         {
97                                                 if ((other.classname == "player") && (other.health >= 1))
98                                                         Damage (head, self, other, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
99                                                 else if (other.health < 1) // corpses gib
100                                                         Damage (head, self, other, 10000, DEATH_TELEFRAG, head.origin, '0 0 0');
101                                                 else // dead bodies and monsters gib themselves instead of telefragging
102                                                         Damage (other, self, other, 10000, DEATH_TELEFRAG, other.origin, '0 0 0');
103                                         }
104         }
105
106         // hide myself a tic
107         other.effects = other.effects | EF_NODRAW;
108         if (other.weaponentity) // misuse FL_FLY to avoid EF_NODRAW on viewmodel
109                 other.weaponentity.flags = other.weaponentity.flags | FL_FLY;
110         other.teleport_time = time + cvar("sys_ticrate");
111
112         other.flags = other.flags - (other.flags & FL_ONGROUND);
113         // reset tracking of oldvelocity for impact damage (sudden velocity changes)
114         other.oldvelocity = other.velocity;
115         // reset tracking of who pushed you into a hazard (for kill credit)
116         other.pushltime = 0;
117
118         // stop player name display
119         {
120                 entity oldself;
121                 oldself = self;
122                 self = other;
123                 ClearSelectedPlayer();
124                 self = oldself;
125         }
126 }
127
128 void info_teleport_destination (void)
129 {
130         self.mangle = self.angles;
131         self.angles = '0 0 0';
132
133         //setorigin (self, self.origin + '0 0 27');     // To fix a mappers' habit as old as Quake
134         setorigin (self, self.origin);
135
136         if (!self.targetname)
137                 objerror ("Teleport destination without a targetname");
138 }
139
140 void misc_teleporter_dest (void)
141 {
142         info_teleport_destination();
143 }
144
145 void target_teleporter (void)
146 {
147         info_teleport_destination();
148 }
149
150 void teleport_findtarget (void)
151 {
152         // now enable touch
153         self.touch = Teleport_Touch;
154
155         self.enemy = find (world, targetname, self.target);
156         if (!self.enemy)
157         {
158                 objerror ("Teleporter with nonexistant target");
159                 remove(self);
160                 return;
161         }
162
163         self.dest = self.enemy.origin;
164         waypoint_spawnforteleporter(self, self.dest, 0);
165 }
166
167 void trigger_teleport (void)
168 {
169         self.angles = '0 0 0';
170
171         self.solid = SOLID_TRIGGER;
172         self.movetype = MOVETYPE_NONE;
173
174         setmodel (self, self.model); // no precision needed
175
176         self.model = "";
177         self.modelindex = 0;
178
179         self.think = teleport_findtarget;
180         self.nextthink = time + 0.2;
181
182         if (!self.target)
183                 objerror ("Teleporter with no target");
184 }