/* ============================================================================ Half-Life world emulation 0.1alpha To provide a working half-life world for deathmatch and for user created maps to take advantage of. Created by Kryten (kryten@inside3d.com) If you add to this file place your name, email and the functions you create at the bottom. Top reserved for the creators functions. If you make changes to the creators functions then you should comment out the original code. =============================================================================== Copyright (C) 2000 Kryten. may contain code Copyright (C) 1996 Id Software, Inc. may contain code Copyright (C) 2000 Valve LLC This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =============================================================================== */ /* =============================================================================== Half-Life emulated world objects Rotation =============================================================================== */ .float fanfriction; .vector spawnorigin; /*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS You need to have an origin brush as part of this entity. The center of that brush will be the point around which it is rotated. It will rotate around the Z axis by default. You can check either the X_AXIS or Y_AXIS box to change that. "speed" determines how fast it moves; default value is 100. "dmg" damage to inflict when blocked (2 default) REVERSE will cause the it to rotate in the opposite direction. */ void () func_rotating_Touch; void () func_rotating_SpinUp; void () func_rotating_SpinDown; void () func_rotating_Rotate; void () func_rotating_Use; void () func_rotating = { BSP_Basic (); // so it can be lit first then moved into place if (self.spawnorigin) setorigin (self, self.spawnorigin); // prevent divide by zero if level designer forgets friction! if (!self.fanfriction) self.fanfriction = 100; self.fanfriction = self.fanfriction / 100; if (self.spawnflags & 4) self.movedir = '0 0 1'; else if (self.spawnflags & 8) self.movedir = '1 0 0'; else self.movedir = '0 1 0'; // check for reverse rotation if (self.spawnflags & 2) // reverse direction self.movedir = self.movedir * -1; // some rotating objects like fake volumetric lights will not be solid. if (self.spawnflags & 64) // not solid { self.movetype = MOVETYPE_NONE; self.solid = SOLID_NOT; } // did level designer forget to assign speed? if (self.speed < 0) self.speed = 0; // instant-use brush? if (self.spawnflags & 1) { self.think = func_rotating_Use; self.nextthink = time + 1.5; // leave a magic delay for client to start up } // can this brush inflict pain? if (self.spawnflags & 32) self.touch = func_rotating_Touch; self.use = func_rotating_Use; self.ltime = time; }; // // Touch - will hurt others based on how fast the brush is spinning // void () func_rotating_Touch = { // we can't hurt this thing, so we're not concerned with it if (!other.takedamage ) return; // calculate damage based on rotation speed self.dmg = vlen(self.avelocity) / 10; T_Damage(other, self, self, self.dmg); other.velocity = normalize(other.origin - (0.5 * (self.absmin + self.absmax))) * self.dmg; }; // // SpinUp - accelerates a non-moving func_rotating up to it's speed // void () func_rotating_SpinUp = { self.nextthink = self.ltime + 0.1; self.avelocity = self.avelocity + (self.movedir * (self.speed * self.fanfriction)); // if we've met or exceeded target speed, set target speed and stop thinking if (self.avelocity_x >= self.movedir_x * self.speed && self.avelocity_y >= self.movedir_y * self.speed && self.avelocity_z >= self.movedir_z * self.speed) { self.avelocity = self.movedir * self.speed;// set speed in case we overshot self.think = func_rotating_Rotate; func_rotating_Rotate (); } }; // // SpinDown - decelerates a moving func_rotating to a standstill. // void () func_rotating_SpinDown = { local float vecdir; self.nextthink = self.ltime + 0.1; self.avelocity = self.avelocity - (self.movedir * (self.speed * self.fanfriction)); if (self.movedir_x) vecdir = self.movedir_x; else if (self.movedir_y) vecdir = self.movedir_y; else vecdir = self.movedir_z; // if we've met or exceeded target speed, set target speed and stop thinking // (note: must check for movedir > 0 or < 0) if (((vecdir > 0) && (self.avelocity_x <= 0 && self.avelocity_y <= 0 && self.avelocity_z <= 0)) || ((vecdir < 0) && (self.avelocity_x >= 0 && self.avelocity_y >= 0 && self.avelocity_z >= 0))) { self.avelocity = '0 0 0'; // set speed in case we overshot self.think = func_rotating_Rotate; func_rotating_Rotate (); } }; void () func_rotating_Rotate = { self.nextthink = self.ltime + 10; }; // // Rotating Use - when a rotating brush is triggered // void () func_rotating_Use = { // is this a brush that should accelerate and decelerate when turned on/off (fan)? if (self.spawnflags & 16) { // fan is spinning, so stop it. if (self.avelocity) { self.think = func_rotating_SpinDown; self.nextthink = self.ltime + 0.1; } else // fan is not moving, so start it { self.think = func_rotating_SpinUp; self.nextthink = self.ltime + 0.1; } } else //this is a normal start/stop brush. { if (self.avelocity) self.avelocity = '0 0 0'; else { self.avelocity = self.movedir * self.speed; self.think = func_rotating_Rotate; func_rotating_Rotate(); } } }; /*QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE DOOR_DONT_LINK TOGGLE X_AXIS Y_AXIS if two doors touch, they are assumed to be connected and operate as a unit. TOGGLE causes the door to wait in both the start and end states for a trigger event. START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors). You need to have an origin brush as part of this entity. The center of that brush will be the point around which it is rotated. It will rotate around the Z axis by default. You can check either the X_AXIS or Y_AXIS box to change that. "distance" is how many degrees the door will be rotated. "speed" determines how fast the door moves; default value is 100. REVERSE will cause the door to rotate in the opposite direction. "angle" determines the opening direction "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. "health" if set, door must be shot open "speed" movement speed (100 default) "wait" wait before returning (3 default, -1 = never return) "dmg" damage to inflict when blocked (2 default) "sounds" 0) no sound 1) stone 2) base 3) stone chain 4) screechy metal */ void () door_rotating_Blocked; void () door_rotating_HitBottom; void () door_rotating_GoDown; void () door_rotating_HitTop; void () door_rotating_GoUp; float () door_rotating_Activate; void () door_rotating_Touch; void () func_door_rotating = { BSP_Basic (); // i think spawnflags and angles are used, but they both cant be used the same if (self.spawnflags & 64) self.movedir = '0 0 1'; else if (self.spawnflags & 128) self.movedir = '1 0 0'; else self.movedir = '0 1 0'; if (self.angles) SetMovedir (); if (self.spawnflags & 2) // reverse direction self.movedir = self.movedir * -1; self.pos1 = self.angles; self.pos2 = self.angles + self.movedir * self.distance; if (!self.speed) self.speed = 100; // DOOR_START_OPEN is to allow an entity to be lighted in the closed position // but spawn in the open position if (self.spawnflags & 1) { // swap pos1 and pos2, put door at pos2, invert movement direction self.angles = self.pos2; self.pos2 = self.pos1; self.pos1 = self.angles; self.movedir = self.movedir * -1; } self.state = STATE_BOTTOM; if (!(self.spawnflags & 256)) self.touch = door_rotating_Touch; // touchable button }; // // Doors not tied to anything (e.g. button, another door) can be touched, to make them activate. // void () door_rotating_Touch = { // Ignore touches by anything but players if (other.classname != "player") return; // If door is somebody's target, then touching does nothing. // You have to activate the owner (e.g. button). if (self.targetname) return; activator = other;// remember who activated the door if (door_rotating_Activate()) self.touch = SUB_Null; // Temporarily disable the touch function, until movement is finished. }; // // Causes the door to "do its thing", i.e. start moving, and cascade activation. // float () door_rotating_Activate = { // if (!UTIL_IsMasterTriggered(m_sMaster, activator)) // return 0; if (self.spawnflags & 32 && self.state == STATE_TOP) door_rotating_GoDown();// door should close else door_rotating_GoUp();// door should open return 1; }; // // Starts the door going to its "up" position (simply ToggleData->vecPosition2). // void () door_rotating_GoUp = { self.state = STATE_UP; local float sign; sign = 1.0; if (activator) { if (!(self.spawnflags & 16) && self.movedir_y) // Y axis rotation, move away from the player { local vector vec, vnext; vec = activator.origin - self.origin; makevectors (activator.angles); vnext = (activator.origin + (v_forward * 10)) - self.origin; if ((vec_x*vnext_y - vec_y*vnext_x) < 0 ) sign = -1.0; } } SUB_CalcAngleMove (self.pos2*sign, self.speed, door_rotating_HitTop); }; // // The door has reached the "up" position. Either go back down, or wait for another activation. // void () door_rotating_HitTop = { self.state = STATE_TOP; // toggle-doors don't come down automatically, they wait for refire. if (self.spawnflags & 32) { // Re-instate touch method, movement is complete if (!(self.spawnflags & 256)) self.touch = door_rotating_Touch; } else { // In flWait seconds, DoorGoDown will fire, unless wait is -1, then door stays open self.nextthink = self.ltime + self.wait; self.think = door_rotating_GoDown; if (self.wait == -1) self.nextthink = -1; } // Fire the close target (if startopen is set, then "top" is closed) - netname is the close target // if (self.netname && !(self.spawnflags & 1)) // FireTargets(self.netname, activator, this, USE_TOGGLE, 0); SUB_UseTargets(); }; // // Starts the door going to its "down" position (simply ToggleData->vecPosition1). // void () door_rotating_GoDown = { self.state = STATE_DOWN; SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_HitBottom); }; // // The door has reached the "down" position. Back to quiescence. // void () door_rotating_HitBottom = { self.state = STATE_BOTTOM; // Re-instate touch method, cycle is complete if (self.spawnflags & 256) // use only door self.touch = SUB_Null; else // touchable door self.touch = door_rotating_Touch; // Fire the close target (if startopen is set, then "top" is closed) - netname is the close target // if (self.netname && !(self.spawnflags & 1)) // FireTargets(self.netname, activator, this, USE_TOGGLE, 0); SUB_UseTargets(); }; void () door_rotating_Blocked = { // Hurt the blocker a little. if (self.dmg) T_Damage(other, self, self, self.dmg); // if a door has a negative wait, it would never come back if blocked, // so let it just squash the object to death real fast if (self.wait >= 0) { if (self.state == STATE_DOWN) door_rotating_GoUp(); else door_rotating_GoDown(); } // Block all door pieces with the same targetname here. /* if (self.targetname) { for (;;) { pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->targetname)); if ( VARS( pentTarget ) != pev ) { if (FNullEnt(pentTarget)) break; if ( FClassnameIs ( pentTarget, "func_door" ) || FClassnameIs ( pentTarget, "func_door_rotating" ) ) { pDoor = GetClassPtr( (CBaseDoor *) VARS(pentTarget) ); if ( pDoor->m_flWait >= 0) { if (pDoor->pev->velocity == pev->velocity && pDoor->pev->avelocity == pev->velocity) { // this is the most hacked, evil, bastardized thing I've ever seen. kjb if ( FClassnameIs ( pentTarget, "func_door" ) ) {// set origin to realign normal doors pDoor->pev->origin = pev->origin; pDoor->pev->velocity = g_vecZero;// stop! } else {// set angles to realign rotating doors pDoor->pev->angles = pev->angles; pDoor->pev->avelocity = g_vecZero; } } if ( pDoor->m_toggle_state == TS_GOING_DOWN) pDoor->DoorGoUp(); else pDoor->DoorGoDown(); } } } } } */ }; // // Rotating button (aka "lever") // void () rot_button_Use = {}; void () rot_button_Touch = {}; void () func_rot_button = { BSP_Basic (); SetMovedir (); if (self.spawnflags & 64) self.movedir = '0 0 1'; else if (self.spawnflags & 128) self.movedir = '1 0 0'; else self.movedir = '0 1 0'; if (self.spawnflags & 2) self.movedir = self.movedir * -1; if (!self.speed) self.speed = 40; if (!self.wait) self.wait = 1; if (self.health > 0) self.takedamage = DAMAGE_YES; self.state = STATE_BOTTOM; self.pos1 = self.angles; self.pos2 = self.angles + self.movedir * self.distance; // m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE); // m_fRotating = TRUE; // if the button is flagged for USE button activation only, take away it's touch function and add a use function if (self.spawnflags & 256) self.use = rot_button_Use; else // touchable button self.touch = rot_button_Touch; };