/* server/entities/map_entities.qc misc map entity spawn and logic Copyright (C) 2021-2022 NZ:P Team 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: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ string(float wep, float gorvmodel) GetWeaponModel; void() ReturnWeaponModel; void() flame_update = { #ifndef FTE particle (self.origin, v_up, 111, 0); #else te_flamejet(self.origin, v_up, 10); #endif // FTE self.think = flame_update; self.nextthink = time + random()+0.1; } void() place_fire = { #ifndef FTE particle (self.origin, v_up*8, self.frame, 0); #else te_flamejet(self.origin, v_up*8, 10); #endif // FTE self.think = flame_update; self.nextthink = time + random()+0.1; }; .float radioState; .float length; .string tune; void() radioPlay = { self.health = 1; if (self.radioState == 1) { sound (self, CHAN_ITEM, self.tune, 1, ATTN_NORM); #ifndef FTE self.nextthink = time + GetSoundLen(self.tune); #else self.nextthink = time + self.length; #endif // FTE self.think = radioPlay; } else if (self.radioState == 0) { sound (self, CHAN_ITEM, "sounds/null.wav", 1, ATTN_NONE); self.nextthink = 0; self.think = SUB_Null; } } void() radio_hit = { self.health = 1; sound (self, CHAN_ITEM, "sounds/misc/radio.wav", 1, ATTN_NORM); self.nextthink = time + 1; self.think = radioPlay; if (self.radioState == 0) self.radioState = 1; else if (self.radioState == 1) self.radioState = 0; } void() item_radio = { precache_model ("models/props/radio.mdl"); precache_sound ("sounds/misc/radio.wav"); // Backwards compatibility if (!self.tune) { self.tune = "sounds/music/tune1.wav"; self.length = 10.1; } precache_sound (self.tune); self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything self.solid=SOLID_BBOX; self.classname = "item_radio"; setmodel (self, "models/props/radio.mdl"); setsize (self, '-8 -8 -4', '8 8 4'); self.takedamage = DAMAGE_YES; self.health = 1; self.radioState = 0; self.th_die = radio_hit; self.th_diewunder = SUB_Null; }; /* ================ Custom Teddy Code ================*/ void() teddy_spawn = { precache_model ("models/props/teddy.mdl"); self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything self.solid=SOLID_BBOX; self.classname = "teddy_spawn"; setmodel (self, "models/props/teddy.mdl"); setsize (self, '-8 -8 -4', '8 8 4'); self.takedamage = DAMAGE_YES; self.health = 0; self.th_die = teddy_react; if (self.noise) { // FIXME: make a convert_old_sound_path function like we do for models. if (self.noise == "misc/buy.wav") self.noise = "sounds/misc/buy.wav"; precache_sound (self.noise); } }; /* ================== Custom Song Code ================== */ void() game_songtriggered = { songegg(self.aistatus); music_override = time + self.cost; } void() game_songplay = { self.use = game_songtriggered; }; // Old gross ent.. void() trigger_song = { remove(self); } // // ============================================================ // misc_model() // Entity for prop/other model placement, derived from // progs_dump. Deprecates place_model. // ============================================================ // Modifications from stock: // - Added new spawnflag 64 for fullbright. // - 'model' is now the preffered model field instead of 'mdl'. // // Spawnflags #define MISC_MODEL_GRAVITY 1 #define MISC_MODEL_SOLID 2 #define MISC_MODEL_BACK_AND_FORTH 4 #define MISC_MODEL_ONLY_ONCE 8 #define MISC_MODEL_PLAY_COUNT 16 #define MISC_MODEL_STARTOFF 32 #define MISC_MODEL_FULLBRIGHT 64 // States #define STATE_ACTIVE 0 #define STATE_INACTIVE 1 #define STATE_INVISIBLE 8 // Entity fields .float first_frame; // The starting frame of the animation .float last_frame; // The ending frame of the animation .string mdl; .vector mdlsz; .vector centeroffset; .float count; // for counting triggers .float cnt; // misc flag void() misc_model_use = { // Make invisible if (self.state == STATE_ACTIVE) { if (self.spawnflags & MISC_MODEL_SOLID) self.solid = SOLID_NOT; self.oldmodel = self.model; self.model = ""; self.state = STATE_INVISIBLE; setorigin(self, self.origin); } // Have it appear again else { if (self.spawnflags & MISC_MODEL_SOLID) self.solid = SOLID_BBOX; self.model = self.oldmodel; self.state = STATE_ACTIVE; setorigin(self, self.origin); } }; void() misc_model_think = { self.nextthink = time + fabs(self.speed); if (self.state != STATE_ACTIVE) return; self.frame = self.frame + sign(self.speed); if (self.spawnflags & MISC_MODEL_BACK_AND_FORTH && self.frame < self.first_frame) { self.speed = -1 * self.speed; self.frame += 2; } else if (self.spawnflags & MISC_MODEL_BACK_AND_FORTH && self.frame > self.last_frame) { self.speed = -1 * self.speed; self.frame-=2; } else self.frame = wrap(self.frame, self.first_frame, self.last_frame); if (self.spawnflags & MISC_MODEL_ONLY_ONCE && self.frame == self.last_frame && self.last_frame != self.first_frame) self.nextthink = -1; if (self.spawnflags & MISC_MODEL_PLAY_COUNT && self.frame == self.last_frame && self.last_frame != self.first_frame) { if (!self.count) objerror ("Error: set count to the number of animation cycles!"); self.cnt = self.cnt +1; dprint (ftos(self.cnt)); dprint ("\n"); if (self.cnt != self.count) return FALSE; else self.nextthink = -1; } }; void() misc_model = { if (!self.mdl || self.mdl == "") { // NZP: Check for .model instead if (!self.model || self.model == "") objerror("Model not defined"); } else { // Convert to .model instead of .mdl self.model = self.mdl; } // // Set default stats. // // Center offset. if(!self.centeroffset) self.centeroffset = '0 0 0'; // Custom Bounding Box. if(!self.mdlsz) self.mdlsz = '32 32 32'; // Generate Proper Bounding Box size. vector vmin, vmax; vmin_x = self.centeroffset_x - (self.mdlsz_x / 2); vmin_y = self.centeroffset_y - (self.mdlsz_y / 2); vmin_z = self.centeroffset_z - (self.mdlsz_z / 2); vmax_x = self.centeroffset_x + (self.mdlsz_x / 2); vmax_y = self.centeroffset_y + (self.mdlsz_y / 2); vmax_z = self.centeroffset_z + (self.mdlsz_z / 2); setsize (self, vmin, vmax); // Set our model precache_model(self.model); setmodel(self, self.model); // Model has collision box if (self.spawnflags & MISC_MODEL_SOLID) self.solid = SOLID_BBOX; else self.solid = SOLID_NOT; // Model has gravity if (self.spawnflags & MISC_MODEL_GRAVITY) self.movetype = MOVETYPE_TOSS; else self.movetype = MOVETYPE_NONE; // Model is fullbright if (self.spawnflags & MISC_MODEL_FULLBRIGHT) self.effects = self.effects | EF_FULLBRIGHT; self.use = misc_model_use; if (!self.frame) self.frame = self.first_frame; // Make static (not animate) if not given a frame range, and not affected by gravity // also remains active if it has a targetname (so it can be killtargeted/toggled) if (!self.last_frame && !(self.spawnflags & 1) && !(self.spawnflags & MISC_MODEL_SOLID) && !self.targetname) makestatic(self); // if it as a custom animation range if (self.last_frame) { // Default animation speed to 10 fps if (!self.speed) { self.speed = 0.1; } self.nextthink = time + self.speed; self.think = misc_model_think; } // Start hidden if (self.spawnflags & MISC_MODEL_STARTOFF) self.state = STATE_ACTIVE; else self.state = STATE_INVISIBLE; misc_model_use(); }; // // place_model() // Converts old place_model entities to use misc_model instead. // void() place_model = { // Grab an updated model path. self.model = convert_old_asset_path(self.model); // Convert the VEC_HULL bounds to match mdlsz. self.mdlsz = '64 64 88'; // misc_model just uses frame plainly. self.frame = self.sequence; // Move fullbright spawnflag to the new param. if (self.spawnflags & 1) self.spawnflags = 64; else self.spawnflags = 0; // Now just execute the misc_model spawn function. misc_model(); }; // // game_counter() // Quick and dirty game_counter implementation, referenced // from TWHL docs (https://twhl.info/index.php/wiki/page/game_counter) // #define SPAWNFLAG_COUNTER_REMOVEONFIRE 1 #define SPAWNFLAG_COUNTER_RESETONFIRE 2 void() game_counter_increment = { self.frags++; if (self.frags == self.health) { SUB_UseTargets(); if (self.spawnflags & SPAWNFLAG_COUNTER_REMOVEONFIRE) { remove(self); } else if (self.spawnflags & SPAWNFLAG_COUNTER_RESETONFIRE) { self.frags = self.cost; } } } void() game_counter = { // TODO: master checking.. // Store the initial value in case RESET ON FIRE is set. self.cost = self.frags; // Every time its triggered, increment. self.use = game_counter_increment; }