From: Mario Date: Tue, 17 Feb 2015 19:45:21 +0000 (+1100) Subject: Merge branch 'TimePath/experiments/csqc_prediction' into Mario/qc_physics X-Git-Tag: xonotic-v0.8.1~38^2~37 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=6dc9591eba337374f2b4348a6a6deaa1cb6887d0;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'TimePath/experiments/csqc_prediction' into Mario/qc_physics Conflicts: qcsrc/client/bgmscript.qh qcsrc/client/command/cl_cmd.qc qcsrc/client/damage.qc qcsrc/client/main.qc qcsrc/client/progs.src qcsrc/client/waypointsprites.qc qcsrc/common/constants.qh qcsrc/common/physics.qh qcsrc/common/triggers/func/breakable.qc qcsrc/common/triggers/target/music.qc qcsrc/common/triggers/target/spawn.qc qcsrc/common/triggers/trigger/jumppads.qc qcsrc/common/triggers/trigger/secret.qc qcsrc/common/triggers/trigger/secret.qh qcsrc/common/triggers/trigger/swamp.qc qcsrc/server/constants.qh qcsrc/server/g_subs.qc qcsrc/server/g_triggers.qc qcsrc/server/miscfunctions.qc qcsrc/server/progs.src qcsrc/server/t_halflife.qc qcsrc/server/t_plats.qc --- 6dc9591eba337374f2b4348a6a6deaa1cb6887d0 diff --cc qcsrc/client/bgmscript.qc index 9d024941e,b567190ca..615b57169 --- a/qcsrc/client/bgmscript.qc +++ b/qcsrc/client/bgmscript.qc @@@ -1,3 -1,14 +1,15 @@@ + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "defs.qh" ++ #include "../common/triggers/triggers.qh" + #include "../common/util.qh" + #include "autocvars.qh" + #include "bgmscript.qh" + #include "main.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #endif + #define CONSTANT_SPEED_DECAY float bgmscriptbuf; diff --cc qcsrc/client/bgmscript.qh index e31830552,910ef6b5d..fba28762a --- a/qcsrc/client/bgmscript.qh +++ b/qcsrc/client/bgmscript.qh @@@ -1,3 -1,12 +1,6 @@@ + #ifndef BGMSCRIPT_H + #define BGMSCRIPT_H + -.string bgmscript; -.float bgmscriptattack; -.float bgmscriptdecay; -.float bgmscriptsustain; -.float bgmscriptrelease; - .float just_toggled; void BGMScript_InitEntity(entity e); diff --cc qcsrc/client/command/cl_cmd.qc index 477fe2b2e,7b74d2dd7..0748483c5 --- a/qcsrc/client/command/cl_cmd.qc +++ b/qcsrc/client/command/cl_cmd.qc @@@ -337,32 -339,7 +339,32 @@@ void LocalCommand_mv_download(int reque } } - void LocalCommand_find(float request, float argc) ++void LocalCommand_find(int request, int argc) +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + entity client; + + for(client = world; (client = find(client, classname, argv(1))); ) + print(etos(client), "\n"); + + return; + } + + default: + print("Incorrect parameters for ^2find^7\n"); + case CMD_REQUEST_USAGE: + { + print("\nUsage:^3 cl_cmd find classname\n"); + print(" Where 'classname' is the classname to search for.\n"); + return; + } + } +} + - void LocalCommand_sendcvar(float request, float argc) + void LocalCommand_sendcvar(int request, int argc) { switch(request) { diff --cc qcsrc/client/damage.qc index 9966c760e,000ef8bcc..c3d260863 --- a/qcsrc/client/damage.qc +++ b/qcsrc/client/damage.qc @@@ -31,7 -47,7 +47,7 @@@ void DamageEffect_Think( pointparticles(self.team, org, '0 0 0', 1); } - void DamageEffect(vector hitorg, float thedamage, float type, float specnum) -void DamageEffect(vector hitorg, float dmg, int type, int specnum) ++void DamageEffect(vector hitorg, float thedamage, int type, int specnum) { // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets) @@@ -104,7 -121,9 +121,9 @@@ void Ent_DamageInfo(float isNew) { - float thedamage, rad, edge, thisdmg, forcemul, species, hitplayer = FALSE; - float dmg, rad, edge, thisdmg; ++ float thedamage, rad, edge, thisdmg; + bool hitplayer = false; + int species, forcemul; vector force, thisforce; entity oldself; @@@ -114,11 -133,11 +133,11 @@@ w_issilent = (w_deathtype & 0x8000); w_deathtype = (w_deathtype & 0x7FFF); - w_org_x = ReadCoord(); - w_org_y = ReadCoord(); - w_org_z = ReadCoord(); + w_org.x = ReadCoord(); + w_org.y = ReadCoord(); + w_org.z = ReadCoord(); - dmg = ReadByte(); + thedamage = ReadByte(); rad = ReadByte(); edge = ReadByte(); force = decompressShortVector(ReadShort()); diff --cc qcsrc/client/main.qc index 000000000,e5c875b37..3903cb427 mode 000000,100644..100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@@ -1,0 -1,1350 +1,1331 @@@ + #include "mapvoting.qh" + #include "modeleffects.qh" + #include "particles.qh" + #include "scoreboard.qh" + #include "shownames.qh" + #include "target_music.qh" + #include "tturrets.qh" + #include "tuba.qh" + #include "wall.qh" + #include "waypointsprites.qh" + + #include "vehicles/vehicles.qh" + + #include "../server/vehicles/bumblebee.qh" + + #include "../common/net_notice.qh" + + #include "../common/monsters/monsters.qh" + ++#include "../common/triggers/include.qh" ++ + #include "../warpzonelib/client.qh" + + // -------------------------------------------------------------------------- + // BEGIN REQUIRED CSQC FUNCTIONS + //include "main.qh" + + entity clearentity_ent; + void clearentity(entity e) + { + if (!clearentity_ent) + { + clearentity_ent = spawn(); + clearentity_ent.classname = "clearentity"; + } + int n = e.entnum; + copyentity(clearentity_ent, e); + e.entnum = n; + } + + #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED + void menu_show_error() + { + drawstring('0 200 0', _("ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!"), '8 8 0', '1 0 0', 1, 0); + } + + // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load) + // Useful for precaching things + + void menu_sub_null() + { + } + + string forcefog; + void WaypointSprite_Load(); + void ConsoleCommand_macro_init(); + void CSQC_Init(void) + { + prvm_language = cvar_string("prvm_language"); + + #ifdef WATERMARK + dprintf("^4CSQC Build information: ^1%s\n", WATERMARK); + #endif + + int i; + + binddb = db_create(); + tempdb = db_create(); + ClientProgsDB = db_load("client.db"); + compressShortVector_init(); + + draw_endBoldFont(); + menu_visible = false; + menu_show = menu_show_error; + menu_action = func_null; + + for(i = 0; i < 255; ++i) + if(getplayerkeyvalue(i, "viewentity") == "") + break; + maxclients = i; + + //registercommand("hud_configure"); + //registercommand("hud_save"); + //registercommand("menu_action"); + + ConsoleCommand_macro_init(); + + registercvar("hud_usecsqc", "1"); + registercvar("scoreboard_columns", "default"); + + registercvar("cl_nade_type", "3"); + registercvar("cl_pokenade_type", "zombie"); + + gametype = 0; + + // hud_fields uses strunzone on the titles! + for(i = 0; i < MAX_HUD_FIELDS; ++i) + hud_title[i] = strzone("(null)"); + + Cmd_HUD_SetFields(0); + + postinit = false; + + calledhooks = 0; + + teams = Sort_Spawn(); + players = Sort_Spawn(); + + GetTeam(NUM_SPECTATOR, true); // add specs first + + // needs to be done so early because of the constants they create + CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterMonsters); + CALL_ACCUMULATED_FUNCTION(RegisterGametypes); + CALL_ACCUMULATED_FUNCTION(RegisterNotifications); + CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); + CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels); + CALL_ACCUMULATED_FUNCTION(RegisterBuffs); + + WaypointSprite_Load(); + + // precaches + precache_model("null"); + precache_sound("misc/hit.wav"); + precache_sound("misc/typehit.wav"); + + Projectile_Precache(); + Hook_Precache(); + GibSplash_Precache(); + Casings_Precache(); + Vehicles_Precache(); + turrets_precache(); + Tuba_Precache(); + CSQCPlayer_Precache(); + + if(autocvar_cl_reticle) + { + precache_pic("gfx/reticle_normal"); + // weapon reticles are precached in weapon files + } + + get_mi_min_max_texcoords(1); // try the CLEVER way first + minimapname = strcat("gfx/", mi_shortname, "_radar.tga"); + shortmapname = mi_shortname; + + if(precache_pic(minimapname) == "") + { + // but maybe we have a non-clever minimap + minimapname = strcat("gfx/", mi_shortname, "_mini.tga"); + if(precache_pic(minimapname) == "") + minimapname = ""; // FAIL + else + get_mi_min_max_texcoords(0); // load new texcoords + } + + mi_center = (mi_min + mi_max) * 0.5; + mi_scale = mi_max - mi_min; + minimapname = strzone(minimapname); + + WarpZone_Init(); + + hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin)); + hud_configure_prev = -1; + + draw_currentSkin = strzone(strcat("gfx/menu/", cvar_string("menu_skin"))); + } + + // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) + void Shutdown(void) + { + WarpZone_Shutdown(); + + remove(teams); + remove(players); + db_close(binddb); + db_close(tempdb); + if(autocvar_cl_db_saveasdump) + db_dump(ClientProgsDB, "client.db"); + else + db_save(ClientProgsDB, "client.db"); + db_close(ClientProgsDB); + + if(camera_active) + cvar_set("chase_active",ftos(chase_active_backup)); + + // unset the event chasecam's chase_active + if(autocvar_chase_active < 0) + cvar_set("chase_active", "0"); + + if (!isdemo()) + { + if (!(calledhooks & HOOK_START)) + localcmd("\n_cl_hook_gamestart nop\n"); + if (!(calledhooks & HOOK_END)) + localcmd("\ncl_hook_gameend\n"); + } + } + + .float has_team; + float SetTeam(entity o, int Team) + { + entity tm; + if(teamplay) + { + switch(Team) + { + case -1: + case NUM_TEAM_1: + case NUM_TEAM_2: + case NUM_TEAM_3: + case NUM_TEAM_4: + break; + default: + if(GetTeam(Team, false) == world) + { + dprintf("trying to switch to unsupported team %d\n", Team); + Team = NUM_SPECTATOR; + } + break; + } + } + else + { + switch(Team) + { + case -1: + case 0: + break; + default: + if(GetTeam(Team, false) == world) + { + dprintf("trying to switch to unsupported team %d\n", Team); + Team = NUM_SPECTATOR; + } + break; + } + } + if(Team == -1) // leave + { + if(o.has_team) + { + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.has_team = 0; + return true; + } + } + else + { + if (!o.has_team) + { + o.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + o.has_team = 1; + return true; + } + else if(Team != o.team) + { + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + return true; + } + } + return false; + } + + void Playerchecker_Think() + { + int i; + entity e; + for(i = 0; i < maxclients; ++i) + { + e = playerslots[i]; + if(GetPlayerName(i) == "") + { + if(e.sort_prev) + { + // player disconnected + SetTeam(e, -1); + RemovePlayer(e); + e.sort_prev = world; + //e.gotscores = 0; + } + } + else + { + if (!e.sort_prev) + { + // player connected + if (!e) + playerslots[i] = e = spawn(); + e.sv_entnum = i; + e.ping = 0; + e.ping_packetloss = 0; + e.ping_movementloss = 0; + //e.gotscores = 0; // we might already have the scores... + SetTeam(e, GetPlayerColor(i)); // will not hurt; later updates come with HUD_UpdatePlayerTeams + RegisterPlayer(e); + HUD_UpdatePlayerPos(e); + } + } + } + self.nextthink = time + 0.2; + } + + void Porto_Init(); + void TrueAim_Init(); + void PostInit(void) + { + entity playerchecker; + playerchecker = spawn(); + playerchecker.think = Playerchecker_Think; + playerchecker.nextthink = time + 0.2; + + Porto_Init(); + TrueAim_Init(); + + postinit = true; + } + + // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client. + // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine. + // All keys are in ascii. + // bInputType = 0 is key pressed, 1 is key released, 2 and 3 are mouse input. + // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0. + // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta. + // In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos. + float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) + { + float bSkipKey; + bSkipKey = false; + + if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary)) + return true; + + if (MapVote_InputEvent(bInputType, nPrimary, nSecondary)) + return true; + + if(menu_visible && menu_action) + if(menu_action(bInputType, nPrimary, nSecondary)) + return true; + + return bSkipKey; + } + + // END REQUIRED CSQC FUNCTIONS + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // BEGIN OPTIONAL CSQC FUNCTIONS -void trigger_touch_generic(void() touchfunc) -{ - entity e; - for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain) - if(e.isplayermodel) - { - vector emin = e.absmin, emax = e.absmax; - if(self.solid == SOLID_BSP) - { - emin -= '1 1 1'; - emax += '1 1 1'; - } - if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick - if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate - { - other = e; - touchfunc(); - } - } -} + + void Ent_RemoveEntCS() + { + entcs_receiver[self.sv_entnum] = world; + } + void Ent_ReadEntCS() + { + int sf; + InterpolateOrigin_Undo(); + + self.classname = "entcs_receiver"; + sf = ReadByte(); + + if(sf & 1) + self.sv_entnum = ReadByte(); + if(sf & 2) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + setorigin(self, self.origin); + } + if(sf & 4) + { + self.angles_y = ReadByte() * 360.0 / 256; + self.angles_x = self.angles_z = 0; + } + if(sf & 8) + self.healthvalue = ReadByte() * 10; + if(sf & 16) + self.armorvalue = ReadByte() * 10; + + entcs_receiver[self.sv_entnum] = self; + self.entremove = Ent_RemoveEntCS; + self.iflags |= IFLAG_ORIGIN; + + InterpolateOrigin_Note(); + } + + void Ent_Remove(); + + void Ent_RemovePlayerScore() + { + if(self.owner) { + SetTeam(self.owner, -1); + self.owner.gotscores = 0; + for(int i = 0; i < MAX_SCORE; ++i) { + self.owner.(scores[i]) = 0; // clear all scores + } + } + } + + void Ent_ReadPlayerScore() + { + int i, n; + bool isNew; + entity o; + + // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN + // (no I've never heard of M-x replace-string, sed, or anything like that) + isNew = !self.owner; // workaround for DP bug + n = ReadByte()-1; + + #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED + if(!isNew && n != self.sv_entnum) + { + //print("A CSQC entity changed its owner!\n"); + printf("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(self), self.classname); + isNew = true; + Ent_Remove(); + self.enttype = ENT_CLIENT_SCORES; + } + #endif + + self.sv_entnum = n; + + if (!(playerslots[self.sv_entnum])) + playerslots[self.sv_entnum] = spawn(); + o = self.owner = playerslots[self.sv_entnum]; + o.sv_entnum = self.sv_entnum; + o.gotscores = 1; + + //if (!o.sort_prev) + // RegisterPlayer(o); + //playerchecker will do this for us later, if it has not already done so + + int sf, lf; + #if MAX_SCORE <= 8 + sf = ReadByte(); + lf = ReadByte(); + #else + sf = ReadShort(); + lf = ReadShort(); + #endif + int p; + for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) + if(sf & p) + { + if(lf & p) + o.(scores[i]) = ReadInt24_t(); + else + o.(scores[i]) = ReadChar(); + } + + if(o.sort_prev) + HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet! + + self.entremove = Ent_RemovePlayerScore; + } + + void Ent_ReadTeamScore() + { + int i; + entity o; + + self.team = ReadByte(); + o = self.owner = GetTeam(self.team, true); // these team numbers can always be trusted + + int sf, lf; + #if MAX_TEAMSCORE <= 8 + sf = ReadByte(); + lf = ReadByte(); + #else + sf = ReadShort(); + lf = ReadShort(); + #endif + int p; + for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2) + if(sf & p) + { + if(lf & p) + o.(teamscores[i]) = ReadInt24_t(); + else + o.(teamscores[i]) = ReadChar(); + } + + HUD_UpdateTeamPos(o); + } + + void Ent_ClientData() + { + float newspectatee_status; + + int f = ReadByte(); + + scoreboard_showscores_force = (f & 1); + + if(f & 2) + { + newspectatee_status = ReadByte(); + if(newspectatee_status == player_localnum + 1) + newspectatee_status = -1; // observing + } + else + newspectatee_status = 0; + + spectatorbutton_zoom = (f & 4); + + if(f & 8) + { + angles_held_status = 1; + angles_held.x = ReadAngle(); + angles_held.y = ReadAngle(); + angles_held.z = 0; + } + else + angles_held_status = 0; + + if(newspectatee_status != spectatee_status) + { + // clear race stuff + race_laptime = 0; + race_checkpointtime = 0; + } + if (autocvar_hud_panel_healtharmor_progressbar_gfx) + { + if ( (spectatee_status == -1 && newspectatee_status > 0) //before observing, now spectating + || (spectatee_status > 0 && newspectatee_status > 0 && spectatee_status != newspectatee_status) //changed spectated player + ) + prev_p_health = -1; + else if(spectatee_status && !newspectatee_status) //before observing/spectating, now playing + prev_health = -1; + } + spectatee_status = newspectatee_status; + + // we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum + } + + void Ent_Nagger() + { + int i, j, b, f; + + int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS + + if(!(nags & 4)) + { + if(vote_called_vote) + strunzone(vote_called_vote); + vote_called_vote = string_null; + vote_active = 0; + } + else + { + vote_active = 1; + } + + if(nags & 64) + { + vote_yescount = ReadByte(); + vote_nocount = ReadByte(); + vote_needed = ReadByte(); + vote_highlighted = ReadChar(); + } + + if(nags & 128) + { + if(vote_called_vote) + strunzone(vote_called_vote); + vote_called_vote = strzone(ColorTranslateRGB(ReadString())); + } + + if(nags & 1) + { + for(j = 0; j < maxclients; ++j) + if(playerslots[j]) + playerslots[j].ready = 1; + for(i = 1; i <= maxclients; i += 8) + { + f = ReadByte(); + for(j = i-1, b = 1; b < 256; b *= 2, ++j) + if (!(f & b)) + if(playerslots[j]) + playerslots[j].ready = 0; + } + } + + ready_waiting = (nags & 1); + ready_waiting_for_me = (nags & 2); + vote_waiting = (nags & 4); + vote_waiting_for_me = (nags & 8); + warmup_stage = (nags & 16); + } + + void Ent_EliminatedPlayers() + { + int i, j, b, f; + + int sf = ReadByte(); + if(sf & 1) + { + for(j = 0; j < maxclients; ++j) + if(playerslots[j]) + playerslots[j].eliminated = 1; + for(i = 1; i <= maxclients; i += 8) + { + f = ReadByte(); + for(j = i-1, b = 1; b < 256; b *= 2, ++j) + if (!(f & b)) + if(playerslots[j]) + playerslots[j].eliminated = 0; + } + } + } + + void Ent_RandomSeed() + { + float s; + prandom_debug(); + s = ReadShort(); + psrandom(s); + } + + void Ent_ReadAccuracy(void) + { + int f, w; + int sf = ReadInt24_t(); + if(sf == 0) + { + for(w = 0; w <= WEP_LAST - WEP_FIRST; ++w) + weapon_accuracy[w] = -1; + return; + } + + for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) + { + if(sf & f) + { + int b = ReadByte(); + if(b == 0) + weapon_accuracy[w] = -1; + else if(b == 255) + weapon_accuracy[w] = 1.0; // no better error handling yet, sorry + else + weapon_accuracy[w] = (b - 1.0) / 100.0; + } + if(f == 0x800000) + f = 1; + else + f *= 2; + } + } + + void Spawn_Draw(void) + { + pointparticles(self.cnt, self.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1)); + } + + void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint + { + float teamnum = (ReadByte() - 1); + vector spn_origin; + spn_origin.x = ReadShort(); + spn_origin.y = ReadShort(); + spn_origin.z = ReadShort(); + + if(is_new) + { + self.origin = spn_origin; + setsize(self, PL_MIN, PL_MAX); + droptofloor(); + + /*if(autocvar_cl_spawn_point_model) // needs a model first + { + self.mdl = "models/spawnpoint.md3"; + self.colormod = Team_ColorRGB(teamnum); + precache_model(self.mdl); + setmodel(self, self.mdl); + self.drawmask = MASK_NORMAL; + //self.movetype = MOVETYPE_NOCLIP; + //self.draw = Spawn_Draw; + }*/ + if(autocvar_cl_spawn_point_particles) + { + if((serverflags & SERVERFLAG_TEAMPLAY)) + { + switch(teamnum) + { + case NUM_TEAM_1: self.cnt = particleeffectnum("spawn_point_red"); break; + case NUM_TEAM_2: self.cnt = particleeffectnum("spawn_point_blue"); break; + case NUM_TEAM_3: self.cnt = particleeffectnum("spawn_point_yellow"); break; + case NUM_TEAM_4: self.cnt = particleeffectnum("spawn_point_pink"); break; + default: self.cnt = particleeffectnum("spawn_point_neutral"); break; + } + } + else { self.cnt = particleeffectnum("spawn_point_neutral"); } + + self.draw = Spawn_Draw; + } + } + + //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(self.origin), teamnum, self.cnt); + } + + void Ent_ReadSpawnEvent(float is_new) + { + // If entnum is 0, ONLY do the local spawn actions + // this way the server can disable the sending of + // spawn origin or such to clients if wanted. + float entnum = ReadByte(); + + if(entnum) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + + if(is_new) + { + float teamnum = GetPlayerColor(entnum - 1); + + if(autocvar_cl_spawn_event_particles) + { + switch(teamnum) + { + case NUM_TEAM_1: pointparticles(particleeffectnum("spawn_event_red"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_2: pointparticles(particleeffectnum("spawn_event_blue"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_3: pointparticles(particleeffectnum("spawn_event_yellow"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_4: pointparticles(particleeffectnum("spawn_event_pink"), self.origin, '0 0 0', 1); break; + default: pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); break; + } + } + if(autocvar_cl_spawn_event_sound) + { + sound(self, CH_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTEN_NORM); + } + } + } + + // local spawn actions + if(is_new && (!entnum || (entnum == player_localentnum))) + { + zoomin_effect = 1; + current_viewzoom = (1 / bound(1, autocvar_cl_spawnzoom_factor, 16)); + + if(autocvar_cl_unpress_zoom_on_spawn) + { + localcmd("-zoom\n"); + button_zoom = false; + } + } + + //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum); + } + + // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. + // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. + void Ent_RadarLink(); + void Ent_Init(); + void Ent_ScoresInfo(); -#ifdef CSQC -void ent_func_ladder(); -void ent_trigger_push(); -void ent_target_push(); -void ent_conveyor(); -#endif + void CSQC_Ent_Update(float bIsNewEntity) + { + float t; + float savetime; + t = ReadByte(); + + if(autocvar_developer_csqcentities) + printf("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t); + + // set up the "time" global for received entities to be correct for interpolation purposes + savetime = time; + if(servertime) + { + time = servertime; + } + else + { + serverprevtime = time; + serverdeltatime = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE); + time = serverprevtime + serverdeltatime; + } + + #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED + if(self.enttype) + { + if(t != self.enttype || bIsNewEntity) + { + //print("A CSQC entity changed its type!\n"); + printf("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t); + Ent_Remove(); + clearentity(self); + bIsNewEntity = 1; + } + } + else + { + if(!bIsNewEntity) + { + printf("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(self), self.entnum, t); + bIsNewEntity = 1; + } + } + #endif + self.enttype = t; + switch(t) + { + case ENT_CLIENT_ENTCS: Ent_ReadEntCS(); break; + case ENT_CLIENT_SCORES: Ent_ReadPlayerScore(); break; + case ENT_CLIENT_TEAMSCORES: Ent_ReadTeamScore(); break; + case ENT_CLIENT_POINTPARTICLES: Ent_PointParticles(); break; + case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break; + case ENT_CLIENT_LASER: Ent_Laser(); break; + case ENT_CLIENT_NAGGER: Ent_Nagger(); break; + case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break; + case ENT_CLIENT_WAYPOINT: Ent_WaypointSprite(); break; + case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break; + case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break; + case ENT_CLIENT_GIBSPLASH: Ent_GibSplash(bIsNewEntity); break; + case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(bIsNewEntity); break; + case ENT_CLIENT_CASING: Ent_Casing(bIsNewEntity); break; + case ENT_CLIENT_INIT: Ent_Init(); break; + case ENT_CLIENT_SCORES_INFO: Ent_ScoresInfo(); break; + case ENT_CLIENT_MAPVOTE: Ent_MapVote(); break; + case ENT_CLIENT_CLIENTDATA: Ent_ClientData(); break; + case ENT_CLIENT_RANDOMSEED: Ent_RandomSeed(); break; + case ENT_CLIENT_WALL: Ent_Wall(); break; + case ENT_CLIENT_MODELEFFECT: Ent_ModelEffect(bIsNewEntity); break; + case ENT_CLIENT_TUBANOTE: Ent_TubaNote(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE: WarpZone_Read(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE_CAMERA: WarpZone_Camera_Read(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break; + case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break; + case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break; + case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break; + case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break; + case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break; + case ENT_CLIENT_TURRET: ent_turret(); break; + case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break; + case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break; + case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break; + case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break; + case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break; + case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break; + case ENT_CLIENT_HEALING_ORB: ent_healer(); break; + case ENT_CLIENT_LADDER: ent_func_ladder(); break; + case ENT_CLIENT_TRIGGER_PUSH: ent_trigger_push(); break; + case ENT_CLIENT_TARGET_PUSH: ent_target_push(); break; + case ENT_CLIENT_CONVEYOR: ent_conveyor(); break; ++ case ENT_CLIENT_DOOR: ent_door(); break; ++ case ENT_CLIENT_DOOR_TRIGGER: ent_door_trigger(); break; ++ case ENT_CLIENT_PLAT: ent_plat(); break; ++ case ENT_CLIENT_PLAT_TRIGGER: ent_plat_trigger(); break; ++ case ENT_CLIENT_SWAMP: ent_swamp(); break; + + default: + //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype)); + error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", self.enttype, num_for_edict(self), self.classname)); + break; + } + + time = savetime; + } + // Destructor, but does NOT deallocate the entity by calling remove(). Also + // used when an entity changes its type. For an entity that someone interacts + // with others, make sure it can no longer do so. + void Ent_Remove() + { + if(self.entremove) + self.entremove(); + + if(self.skeletonindex) + { + skel_delete(self.skeletonindex); + self.skeletonindex = 0; + } + + if(self.snd_looping > 0) + { + sound(self, self.snd_looping, "misc/null.wav", VOL_BASE, autocvar_g_jetpack_attenuation); + self.snd_looping = 0; + } + + self.enttype = 0; + self.classname = ""; + self.draw = menu_sub_null; + self.entremove = menu_sub_null; + // TODO possibly set more stuff to defaults + } + // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(self) as well. + void CSQC_Ent_Remove() + { + if(autocvar_developer_csqcentities) + printf("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype); + + if(wasfreed(self)) + { + print("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n"); + return; + } + if(self.enttype) + Ent_Remove(); + remove(self); + } + + void Gamemode_Init() + { + if (!isdemo()) + { + if(!(calledhooks & HOOK_START)) + localcmd("\n_cl_hook_gamestart ", MapInfo_Type_ToString(gametype), "\n"); + calledhooks |= HOOK_START; + } + } + // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided. To execute standard behavior, simply execute localcmd with the string. + void CSQC_Parse_StuffCmd(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage); + + localcmd(strMessage); + } + // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided. To execute standard behavior, simply execute print with the string. + void CSQC_Parse_Print(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_Print(\"%s\")\n", strMessage); + + print(ColorTranslateRGB(strMessage)); + } + + // CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided. + void CSQC_Parse_CenterPrint(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage); + + centerprint_hud(strMessage); + } + + string notranslate_fogcmd1 = "\nfog "; + string notranslate_fogcmd2 = "\nr_fog_exp2 0\nr_drawfog 1\n"; + void Fog_Force() + { + // TODO somehow thwart prvm_globalset client ... + + if(autocvar_cl_orthoview && autocvar_cl_orthoview_nofog) + { localcmd("\nr_drawfog 0\n"); } + else if(forcefog != "") + { localcmd(strcat(notranslate_fogcmd1, forcefog, notranslate_fogcmd2)); } + } + + void Gamemode_Init(); + void Ent_ScoresInfo() + { + int i; + self.classname = "ent_client_scores_info"; + gametype = ReadInt24_t(); + HUD_ModIcons_SetFunc(); + for(i = 0; i < MAX_SCORE; ++i) + { + if(scores_label[i]) + strunzone(scores_label[i]); + scores_label[i] = strzone(ReadString()); + scores_flags[i] = ReadByte(); + } + for(i = 0; i < MAX_TEAMSCORE; ++i) + { + if(teamscores_label[i]) + strunzone(teamscores_label[i]); + teamscores_label[i] = strzone(ReadString()); + teamscores_flags[i] = ReadByte(); + } + HUD_InitScores(); + Gamemode_Init(); + } + + void Ent_Init() + { + self.classname = "ent_client_init"; + + nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th + + hook_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); + + if(forcefog) + strunzone(forcefog); + forcefog = strzone(ReadString()); + + armorblockpercent = ReadByte() / 255.0; + + g_balance_mortar_bouncefactor = ReadCoord(); + g_balance_mortar_bouncestop = ReadCoord(); + g_balance_electro_secondary_bouncefactor = ReadCoord(); + g_balance_electro_secondary_bouncestop = ReadCoord(); + + vortex_scope = !ReadByte(); + rifle_scope = !ReadByte(); + + serverflags = ReadByte(); + + minelayer_maxmines = ReadByte(); + + hagar_maxrockets = ReadByte(); + + g_trueaim_minrange = ReadCoord(); + g_balance_porto_secondary = ReadByte(); + + if(!postinit) + PostInit(); + } + + void Net_ReadRace() + { + float b; + + b = ReadByte(); + + switch(b) + { + case RACE_NET_CHECKPOINT_HIT_QUALIFYING: + race_checkpoint = ReadByte(); + race_time = ReadInt24_t(); + race_previousbesttime = ReadInt24_t(); + if(race_previousbestname) + strunzone(race_previousbestname); + race_previousbestname = strzone(ColorTranslateRGB(ReadString())); + + race_checkpointtime = time; + + if(race_checkpoint == 0 || race_checkpoint == 254) + { + race_penaltyaccumulator = 0; + race_laptime = time; // valid + } + + break; + + case RACE_NET_CHECKPOINT_CLEAR: + race_laptime = 0; + race_checkpointtime = 0; + break; + + case RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING: + race_laptime = ReadCoord(); + race_checkpointtime = -99999; + // fall through + case RACE_NET_CHECKPOINT_NEXT_QUALIFYING: + race_nextcheckpoint = ReadByte(); + + race_nextbesttime = ReadInt24_t(); + if(race_nextbestname) + strunzone(race_nextbestname); + race_nextbestname = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_CHECKPOINT_HIT_RACE: + race_mycheckpoint = ReadByte(); + race_mycheckpointtime = time; + race_mycheckpointdelta = ReadInt24_t(); + race_mycheckpointlapsdelta = ReadByte(); + if(race_mycheckpointlapsdelta >= 128) + race_mycheckpointlapsdelta -= 256; + if(race_mycheckpointenemy) + strunzone(race_mycheckpointenemy); + race_mycheckpointenemy = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT: + race_othercheckpoint = ReadByte(); + race_othercheckpointtime = time; + race_othercheckpointdelta = ReadInt24_t(); + race_othercheckpointlapsdelta = ReadByte(); + if(race_othercheckpointlapsdelta >= 128) + race_othercheckpointlapsdelta -= 256; + if(race_othercheckpointenemy) + strunzone(race_othercheckpointenemy); + race_othercheckpointenemy = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_PENALTY_RACE: + race_penaltyeventtime = time; + race_penaltytime = ReadShort(); + //race_penaltyaccumulator += race_penaltytime; + if(race_penaltyreason) + strunzone(race_penaltyreason); + race_penaltyreason = strzone(ReadString()); + break; + + case RACE_NET_PENALTY_QUALIFYING: + race_penaltyeventtime = time; + race_penaltytime = ReadShort(); + race_penaltyaccumulator += race_penaltytime; + if(race_penaltyreason) + strunzone(race_penaltyreason); + race_penaltyreason = strzone(ReadString()); + break; + + case RACE_NET_SERVER_RECORD: + race_server_record = ReadInt24_t(); + break; + case RACE_NET_SPEED_AWARD: + race_speedaward = ReadInt24_t(); + if(race_speedaward_holder) + strunzone(race_speedaward_holder); + race_speedaward_holder = strzone(ReadString()); + break; + case RACE_NET_SPEED_AWARD_BEST: + race_speedaward_alltimebest = ReadInt24_t(); + if(race_speedaward_alltimebest_holder) + strunzone(race_speedaward_alltimebest_holder); + race_speedaward_alltimebest_holder = strzone(ReadString()); + break; + case RACE_NET_SERVER_RANKINGS: + float prevpos, del; + int pos = ReadShort(); + prevpos = ReadShort(); + del = ReadShort(); + + // move other rankings out of the way + int i; + if (prevpos) { + for (i=prevpos-1;i>pos-1;--i) { + grecordtime[i] = grecordtime[i-1]; + if(grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i-1]); + } + } else if (del) { // a record has been deleted by the admin + for (i=pos-1; i<= RANKINGS_CNT-1; ++i) { + if (i == RANKINGS_CNT-1) { // clear out last record + grecordtime[i] = 0; + if (grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = string_null; + } + else { + grecordtime[i] = grecordtime[i+1]; + if (grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i+1]); + } + } + } else { // player has no ranked record yet + for (i=RANKINGS_CNT-1;i>pos-1;--i) { + grecordtime[i] = grecordtime[i-1]; + if(grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i-1]); + } + } + + // store new ranking + if(grecordholder[pos-1] != "") + strunzone(grecordholder[pos-1]); + grecordholder[pos-1] = strzone(ReadString()); + grecordtime[pos-1] = ReadInt24_t(); + if(grecordholder[pos-1] == GetPlayerName(player_localnum)) + race_myrank = pos; + break; + case RACE_NET_SERVER_STATUS: + race_status = ReadShort(); + if(race_status_name) + strunzone(race_status_name); + race_status_name = strzone(ReadString()); + } + } + + void Net_TeamNagger() + { + teamnagger = 1; + } + + void Net_ReadPingPLReport() + { + int e, pi, pl, ml; + e = ReadByte(); + pi = ReadShort(); + pl = ReadByte(); + ml = ReadByte(); + if (!(playerslots[e])) + return; + playerslots[e].ping = pi; + playerslots[e].ping_packetloss = pl / 255.0; + playerslots[e].ping_movementloss = ml / 255.0; + } + + void Net_WeaponComplain() + { + complain_weapon = ReadByte(); + + if(complain_weapon_name) + strunzone(complain_weapon_name); + complain_weapon_name = strzone(WEP_NAME(complain_weapon)); + + complain_weapon_type = ReadByte(); + + complain_weapon_time = time; + weapontime = time; // ping the weapon panel + + switch(complain_weapon_type) + { + case 0: Local_Notification(MSG_MULTI, ITEM_WEAPON_NOAMMO, complain_weapon); break; + case 1: Local_Notification(MSG_MULTI, ITEM_WEAPON_DONTHAVE, complain_weapon); break; + default: Local_Notification(MSG_MULTI, ITEM_WEAPON_UNAVAILABLE, complain_weapon); break; + } + } + + // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer. + // You must ALWAYS first acquire the temporary ID, which is sent as a byte. + // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event. + float CSQC_Parse_TempEntity() + { + float bHandled; + bHandled = true; + // Acquire TE ID + float nTEID; + nTEID = ReadByte(); + + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID); + + // NOTE: Could just do return instead of break... + switch(nTEID) + { + case TE_CSQC_TARGET_MUSIC: + Net_TargetMusic(); + bHandled = true; + break; + case TE_CSQC_PICTURE: + Net_MapVote_Picture(); + bHandled = true; + break; + case TE_CSQC_RACE: + Net_ReadRace(); + bHandled = true; + break; + case TE_CSQC_VORTEXBEAMPARTICLE: + Net_ReadVortexBeamParticle(); + bHandled = true; + break; + case TE_CSQC_TEAMNAGGER: + Net_TeamNagger(); + bHandled = true; + break; + case TE_CSQC_ARC: + Net_ReadArc(); + bHandled = true; + break; + case TE_CSQC_PINGPLREPORT: + Net_ReadPingPLReport(); + bHandled = true; + break; + case TE_CSQC_WEAPONCOMPLAIN: + Net_WeaponComplain(); + bHandled = true; + break; + case TE_CSQC_VEHICLESETUP: + Net_VehicleSetup(); + bHandled = true; + break; + case TE_CSQC_SVNOTICE: + cl_notice_read(); + bHandled = true; + break; + case TE_CSQC_SHOCKWAVEPARTICLE: + Net_ReadShockwaveParticle(); + bHandled = true; + break; + default: + // No special logic for this temporary entity; return 0 so the engine can handle it + bHandled = false; + break; + } + + return bHandled; + } + + string getcommandkey(string text, string command) + { + string keys; + float n, j, k, l = 0; + + if (!autocvar_hud_showbinds) + return text; + + keys = db_get(binddb, command); + if (keys == "") + { + n = tokenize(findkeysforcommand(command, 0)); // uses '...' strings + for(j = 0; j < n; ++j) + { + k = stof(argv(j)); + if(k != -1) + { + if ("" == keys) + keys = keynumtostring(k); + else + keys = strcat(keys, ", ", keynumtostring(k)); + + ++l; + if (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit <= l) + break; + } + + } + if (keys == "") + keys = "NO_KEY"; + db_put(binddb, command, keys); + } + + if (keys == "NO_KEY") { + if (autocvar_hud_showbinds > 1) + return sprintf(_("%s (not bound)"), text); + else + return text; + } + else if (autocvar_hud_showbinds > 1) + return sprintf("%s (%s)", text, keys); + else + return keys; + } diff --cc qcsrc/client/progs.src index 71c0de988,640a6d6e9..e1b469734 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@@ -117,26 -60,23 +60,23 @@@ weapons/projectile.qc // TOD ../common/monsters/monsters.qc - ../common/physics.qh - ../server/mutators/mutator_dodging.qc + ../common/weapons/weapons.qc // TODO + ++../common/triggers/include.qc ++ + ../csqcmodellib/cl_model.qc + ../csqcmodellib/cl_player.qc + ../csqcmodellib/interpolate.qc + + ../server/movelib.qc -../server/t_halflife.qc -../server/t_items.qc -../server/t_jumppads.qc -../server/t_plats.qc + ../server/mutators/mutator_multijump.qc - ../common/nades.qc - ../common/buffs.qc - ../common/physics.qc + ../server/vehicles/bumblebee.qc - ../common/triggers/include.qc ++../server/t_items.qc + ../warpzonelib/anglestransform.qc - ../warpzonelib/mathlib.qc - ../warpzonelib/common.qc ../warpzonelib/client.qc - tturrets.qc - - player_skeleton.qc - ../common/animdecide.qc + ../warpzonelib/common.qc + ../warpzonelib/mathlib.qc diff --cc qcsrc/client/waypointsprites.qc index 1e16f925c,788dd871e..4a8b15679 --- a/qcsrc/client/waypointsprites.qc +++ b/qcsrc/client/waypointsprites.qc @@@ -103,13 -56,13 +56,13 @@@ void drawhealthbar(vector org, float ro up = rotate(up, rot); owidth = width + 2 * border; - o = o - up * (margin + border + theheight) + ri * (sz_x - owidth) * 0.5; - o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5; ++ o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5; drawquad(o - up * border, ri * owidth, up * border, "", rgb, a, f); - drawquad(o + up * height, ri * owidth, up * border, "", rgb, a, f); - drawquad(o, ri * border, up * height, "", rgb, a, f); - drawquad(o + ri * (owidth - border), ri * border, up * height, "", rgb, a, f); - drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f); + drawquad(o + up * theheight, ri * owidth, up * border, "", rgb, a, f); + drawquad(o, ri * border, up * theheight, "", rgb, a, f); + drawquad(o + ri * (owidth - border), ri * border, up * theheight, "", rgb, a, f); + drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f); } // returns location of sprite text diff --cc qcsrc/common/constants.qh index f296e81f4,37f80863a..7f594be06 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@@ -23,111 -26,106 +26,111 @@@ // Revision 22: hook shot origin #define CSQC_REVISION 22 - const float AS_STRING = 1; - const float AS_INT = 2; - const float AS_FLOAT_TRUNCATED = 2; - const float AS_FLOAT = 8; - - const float TE_CSQC_PICTURE = 100; - const float TE_CSQC_RACE = 101; - const float TE_CSQC_VORTEXBEAMPARTICLE = 103; - const float TE_CSQC_ARC = 104; - const float TE_CSQC_TEAMNAGGER = 105; - const float TE_CSQC_PINGPLREPORT = 106; - const float TE_CSQC_TARGET_MUSIC = 107; - const float TE_CSQC_WEAPONCOMPLAIN = 108; - const float TE_CSQC_VORTEX_SCOPE = 109; - const float TE_CSQC_MINELAYER_MAXMINES = 110; - const float TE_CSQC_HAGAR_MAXROCKETS = 111; - const float TE_CSQC_VEHICLESETUP = 112; - const float TE_CSQC_SVNOTICE = 113; - const float TE_CSQC_SHOCKWAVEPARTICLE = 114; - - const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder - const float RACE_NET_CHECKPOINT_CLEAR = 1; - const float RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder - const float RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent - const float RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent - const float RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder - const float RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason - const float RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason - const float RACE_NET_SERVER_RECORD = 8; // server record, sent to client - const float RACE_NET_SPEED_AWARD = 9; // speed award, sent to client - const float RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client - const float RACE_NET_SERVER_RANKINGS = 11; - const float RACE_NET_SERVER_STATUS = 12; - const float RANKINGS_CNT = 15; - - const float ENT_CLIENT = 0; - const float ENT_CLIENT_DEAD = 1; - const float ENT_CLIENT_ENTCS = 2; - const float ENT_CLIENT_SCORES_INFO = 3; - const float ENT_CLIENT_SCORES = 4; - const float ENT_CLIENT_TEAMSCORES = 5; - const float ENT_CLIENT_POINTPARTICLES = 6; - const float ENT_CLIENT_RAINSNOW = 7; - const float ENT_CLIENT_LASER = 8; - const float ENT_CLIENT_NAGGER = 9; // flags [votecalledvote] - const float ENT_CLIENT_WAYPOINT = 10; // flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] - const float ENT_CLIENT_RADARLINK = 11; // flags [startorigin] [endorigin] [startcolor+16*endcolor] - const float ENT_CLIENT_PROJECTILE = 12; - const float ENT_CLIENT_GIBSPLASH = 13; - const float ENT_CLIENT_DAMAGEINFO = 14; - const float ENT_CLIENT_CASING = 15; - const float ENT_CLIENT_INIT = 16; - const float ENT_CLIENT_MAPVOTE = 17; - const float ENT_CLIENT_CLIENTDATA = 18; - const float ENT_CLIENT_RANDOMSEED = 19; - const float ENT_CLIENT_WALL = 20; - const float ENT_CLIENT_SPIDERBOT = 21; - const float ENT_CLIENT_MODELEFFECT = 22; - const float ENT_CLIENT_TUBANOTE = 23; - const float ENT_CLIENT_WARPZONE = 24; - const float ENT_CLIENT_WARPZONE_CAMERA = 25; - const float ENT_CLIENT_TRIGGER_MUSIC = 26; - const float ENT_CLIENT_HOOK = 27; - const float ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers - const float ENT_CLIENT_ACCURACY = 30; - const float ENT_CLIENT_SHOWNAMES = 31; - const float ENT_CLIENT_WARPZONE_TELEPORTED = 32; - const float ENT_CLIENT_MODEL = 33; - const float ENT_CLIENT_ITEM = 34; - const float ENT_CLIENT_BUMBLE_RAYGUN = 35; - const float ENT_CLIENT_SPAWNPOINT = 36; - const float ENT_CLIENT_SPAWNEVENT = 37; - const float ENT_CLIENT_NOTIFICATION = 38; - const float ENT_CLIENT_ELIMINATEDPLAYERS = 39; - const float ENT_CLIENT_TURRET = 40; - const float ENT_CLIENT_AUXILIARYXHAIR = 50; - const float ENT_CLIENT_VEHICLE = 60; - const float ENT_CLIENT_LADDER = 61; - const float ENT_CLIENT_TRIGGER_PUSH = 62; - const float ENT_CLIENT_TARGET_PUSH = 63; - const float ENT_CLIENT_CONVEYOR = 64; - const float ENT_CLIENT_DOOR = 65; - const float ENT_CLIENT_DOOR_TRIGGER = 66; - const float ENT_CLIENT_PLAT = 67; - const float ENT_CLIENT_PLAT_TRIGGER = 68; - const float ENT_CLIENT_SWAMP = 69; - - const float ENT_CLIENT_HEALING_ORB = 80; - - const float SPRITERULE_DEFAULT = 0; - const float SPRITERULE_TEAMPLAY = 1; - - const float RADARICON_NONE = 0; - const float RADARICON_FLAG = 1; - const float RADARICON_FLAGCARRIER = 1; - const float RADARICON_HERE = 1; // TODO make these 3 and 4, and make images for them - const float RADARICON_DANGER = 1; - const float RADARICON_WAYPOINT = 1; - const float RADARICON_HELPME = 1; - const float RADARICON_CONTROLPOINT = 1; - const float RADARICON_GENERATOR = 1; - const float RADARICON_OBJECTIVE = 1; - const float RADARICON_DOMPOINT = 1; - const float RADARICON_POWERUP = 1; - const float RADARICON_TAGGED = 1; + const int AS_STRING = 1; + const int AS_INT = 2; + const int AS_FLOAT_TRUNCATED = 2; + const int AS_FLOAT = 8; + + const int TE_CSQC_PICTURE = 100; + const int TE_CSQC_RACE = 101; + const int TE_CSQC_VORTEXBEAMPARTICLE = 103; + const int TE_CSQC_ARC = 104; + const int TE_CSQC_TEAMNAGGER = 105; + const int TE_CSQC_PINGPLREPORT = 106; + const int TE_CSQC_TARGET_MUSIC = 107; + const int TE_CSQC_WEAPONCOMPLAIN = 108; + const int TE_CSQC_VORTEX_SCOPE = 109; + const int TE_CSQC_MINELAYER_MAXMINES = 110; + const int TE_CSQC_HAGAR_MAXROCKETS = 111; + const int TE_CSQC_VEHICLESETUP = 112; + const int TE_CSQC_SVNOTICE = 113; + const int TE_CSQC_SHOCKWAVEPARTICLE = 114; + + const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder + const int RACE_NET_CHECKPOINT_CLEAR = 1; + const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder + const int RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent + const int RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent + const int RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder + const int RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason + const int RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason + const int RACE_NET_SERVER_RECORD = 8; // server record, sent to client + const int RACE_NET_SPEED_AWARD = 9; // speed award, sent to client + const int RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client + const int RACE_NET_SERVER_RANKINGS = 11; + const int RACE_NET_SERVER_STATUS = 12; + const int RANKINGS_CNT = 15; + + const int ENT_CLIENT = 0; + const int ENT_CLIENT_DEAD = 1; + const int ENT_CLIENT_ENTCS = 2; + const int ENT_CLIENT_SCORES_INFO = 3; + const int ENT_CLIENT_SCORES = 4; + const int ENT_CLIENT_TEAMSCORES = 5; + const int ENT_CLIENT_POINTPARTICLES = 6; + const int ENT_CLIENT_RAINSNOW = 7; + const int ENT_CLIENT_LASER = 8; + const int ENT_CLIENT_NAGGER = 9; // flags [votecalledvote] + const int ENT_CLIENT_WAYPOINT = 10; // flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] + const int ENT_CLIENT_RADARLINK = 11; // flags [startorigin] [endorigin] [startcolor+16*endcolor] + const int ENT_CLIENT_PROJECTILE = 12; + const int ENT_CLIENT_GIBSPLASH = 13; + const int ENT_CLIENT_DAMAGEINFO = 14; + const int ENT_CLIENT_CASING = 15; + const int ENT_CLIENT_INIT = 16; + const int ENT_CLIENT_MAPVOTE = 17; + const int ENT_CLIENT_CLIENTDATA = 18; + const int ENT_CLIENT_RANDOMSEED = 19; + const int ENT_CLIENT_WALL = 20; + const int ENT_CLIENT_SPIDERBOT = 21; + const int ENT_CLIENT_MODELEFFECT = 22; + const int ENT_CLIENT_TUBANOTE = 23; + const int ENT_CLIENT_WARPZONE = 24; + const int ENT_CLIENT_WARPZONE_CAMERA = 25; + const int ENT_CLIENT_TRIGGER_MUSIC = 26; + const int ENT_CLIENT_HOOK = 27; + const int ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers + const int ENT_CLIENT_ACCURACY = 30; + const int ENT_CLIENT_SHOWNAMES = 31; + const int ENT_CLIENT_WARPZONE_TELEPORTED = 32; + const int ENT_CLIENT_MODEL = 33; + const int ENT_CLIENT_ITEM = 34; + const int ENT_CLIENT_BUMBLE_RAYGUN = 35; + const int ENT_CLIENT_SPAWNPOINT = 36; + const int ENT_CLIENT_SPAWNEVENT = 37; + const int ENT_CLIENT_NOTIFICATION = 38; + const int ENT_CLIENT_ELIMINATEDPLAYERS = 39; + const int ENT_CLIENT_TURRET = 40; + const int ENT_CLIENT_AUXILIARYXHAIR = 50; + const int ENT_CLIENT_VEHICLE = 60; + const int ENT_CLIENT_LADDER = 61; + const int ENT_CLIENT_TRIGGER_PUSH = 62; + const int ENT_CLIENT_TARGET_PUSH = 63; + const int ENT_CLIENT_CONVEYOR = 64; ++const int ENT_CLIENT_DOOR = 65; ++const int ENT_CLIENT_DOOR_TRIGGER = 66; ++const int ENT_CLIENT_PLAT = 67; ++const int ENT_CLIENT_PLAT_TRIGGER = 68; ++const int ENT_CLIENT_SWAMP = 69; + + const int ENT_CLIENT_HEALING_ORB = 80; + + const int SPRITERULE_DEFAULT = 0; + const int SPRITERULE_TEAMPLAY = 1; + + const int RADARICON_NONE = 0; + const int RADARICON_FLAG = 1; + const int RADARICON_FLAGCARRIER = 1; + const int RADARICON_HERE = 1; // TODO make these 3 and 4, and make images for them + const int RADARICON_DANGER = 1; + const int RADARICON_WAYPOINT = 1; + const int RADARICON_HELPME = 1; + const int RADARICON_CONTROLPOINT = 1; + const int RADARICON_GENERATOR = 1; + const int RADARICON_OBJECTIVE = 1; + const int RADARICON_DOMPOINT = 1; + const int RADARICON_POWERUP = 1; + const int RADARICON_TAGGED = 1; /////////////////////////// // keys pressed diff --cc qcsrc/common/monsters/sv_monsters.qc index fd966f5d2,28430e961..3526a1f18 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@@ -1,3 -1,29 +1,30 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/common.qh" + #include "../constants.qh" + #include "../teams.qh" + #include "../util.qh" + #include "monsters.qh" + #include "sv_monsters.qh" + #include "../weapons/weapons.qh" + #include "../../server/autocvars.qh" + #include "../../server/defs.qh" + #include "../deathtypes.qh" + #include "../../server/mutators/mutators_include.qh" + #include "../../server/tturrets/include/turrets_early.qh" + #include "../../server/vehicles/vehicles_def.qh" + #include "../../server/campaign.qh" + #include "../../server/command/common.qh" + #include "../../server/command/cmd.qh" ++ #include "../triggers/triggers.qh" + #include "../../csqcmodellib/sv_model.qh" + #include "../../server/round_handler.qh" + #include "../../server/tturrets/include/turrets.qh" + #endif + // ========================= // SVQC Monster Properties // ========================= diff --cc qcsrc/common/physics.qc index 26e14e4de,b8fdb1a6c..e77184750 --- a/qcsrc/common/physics.qc +++ b/qcsrc/common/physics.qc @@@ -1,46 -1,6 +1,8 @@@ - .float race_penalty; + #include "physics.qh" ++#include "triggers/trigger/swamp.qh" ++#include "triggers/trigger/jumppads.qh" - .float gravity; - .float swamp_slowdown; - .float lastflags; - .float lastground; - .float wasFlying; - .float spectatorspeed; - - .vector movement_old; - .float buttons_old; - .vector v_angle_old; - .string lastclassname; - - .float() PlayerPhysplug; - float AdjustAirAccelQW(float accelqw, float factor); - - #ifdef CSQC - - .float watertype; - - #elif defined(SVQC) - .float stat_sv_airaccel_qw; - .float stat_sv_airstrafeaccel_qw; - .float stat_sv_airspeedlimit_nonqw; - .float stat_sv_maxspeed; - .float stat_movement_highspeed; - - .float stat_sv_friction_on_land; - .float stat_sv_friction_slick; - - .float stat_doublejump; - - .float stat_jumpspeedcap_min; - .float stat_jumpspeedcap_max; - .float stat_jumpspeedcap_disable_onramps; - - .float stat_jetpack_accel_side; - .float stat_jetpack_accel_up; - .float stat_jetpack_antigravity; - .float stat_jetpack_fuel; - .float stat_jetpack_maxspeed_up; - .float stat_jetpack_maxspeed_side; + #ifdef SVQC void Physics_AddStats() { diff --cc qcsrc/common/physics.qh index 1d8ffe864,9612f0cfb..c256a9eb1 --- a/qcsrc/common/physics.qh +++ b/qcsrc/common/physics.qh @@@ -2,12 -5,34 +5,34 @@@ .entity conveyor; + .float race_penalty; + + .float gravity; + .float swamp_slowdown; + .float lastflags; + .float lastground; + .float wasFlying; + .float spectatorspeed; + + .vector movement_old; + .float buttons_old; + .vector v_angle_old; + .string lastclassname; + + .float() PlayerPhysplug; + float AdjustAirAccelQW(float accelqw, float factor); + #ifdef CSQC - #include "../server/t_jumppads.qh" - + float PM_multijump_checkjump(); + void PM_multijump(); - .float speed; - .float watertype; - .float jumppadcount; - .float ladder_time; - .entity ladder_entity; ++ ++ .float watertype; ++ +// TODO + #define IS_CLIENT(s) (s).isplayermodel + #define IS_PLAYER(s) (s).isplayermodel + #define isPushable(s) (s).isplayermodel float player_multijump; float player_jumpheight; diff --cc qcsrc/common/triggers/func/breakable.qc index c3890d142,000000000..eac14a4ea mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/breakable.qc +++ b/qcsrc/common/triggers/func/breakable.qc @@@ -1,288 -1,0 +1,288 @@@ +#ifdef SVQC ++#include "../../../server/weapons/common.qh" ++ +.entity sprite; + +.float dmg; +.float dmg_edge; +.float dmg_radius; +.float dmg_force; +.float debrismovetype; +.float debrissolid; +.vector debrisvelocity; +.vector debrisvelocityjitter; +.vector debrisavelocityjitter; +.float debristime; +.float debristimejitter; +.float debrisfadetime; +.float debrisdamageforcescale; +.float debrisskin; + +.string mdl_dead; // or "" to hide when broken +.string debris; // space separated list of debris models +// other fields: +// mdl = particle effect name +// count = particle effect multiplier +// targetname = target to trigger to unbreak the model +// target = targets to trigger when broken +// health = amount of damage it can take +// spawnflags: +// 1 = start disabled (needs to be triggered to activate) +// 2 = indicate damage +// notes: +// for mdl_dead to work, origin must be set (using a common/origin brush). +// Otherwise mdl_dead will be displayed at the map origin, and nobody would +// want that! + - void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); ++void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + +// +// func_breakable +// - basically func_assault_destructible for general gameplay use +// +void LaunchDebris (string debrisname, vector force) +{ - local entity dbr; - - dbr = spawn(); ++ entity dbr = spawn(); + setorigin(dbr, self.absmin - + '1 0 0' * random() * (self.absmax_x - self.absmin_x) - + '0 1 0' * random() * (self.absmax_y - self.absmin_y) - + '0 0 1' * random() * (self.absmax_z - self.absmin_z)); ++ + '1 0 0' * random() * (self.absmax.x - self.absmin.x) ++ + '0 1 0' * random() * (self.absmax.y - self.absmin.y) ++ + '0 0 1' * random() * (self.absmax.z - self.absmin.z)); + setmodel (dbr, debrisname ); + dbr.skin = self.debrisskin; + dbr.colormap = self.colormap; // inherit team colors + dbr.owner = self; // do not be affected by our own explosion + dbr.movetype = self.debrismovetype; + dbr.solid = self.debrissolid; + if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out + setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it - dbr.velocity_x = self.debrisvelocity_x + self.debrisvelocityjitter_x * crandom(); - dbr.velocity_y = self.debrisvelocity_y + self.debrisvelocityjitter_y * crandom(); - dbr.velocity_z = self.debrisvelocity_z + self.debrisvelocityjitter_z * crandom(); ++ dbr.velocity_x = self.debrisvelocity.x + self.debrisvelocityjitter.x * crandom(); ++ dbr.velocity_y = self.debrisvelocity.y + self.debrisvelocityjitter.y * crandom(); ++ dbr.velocity_z = self.debrisvelocity.z + self.debrisvelocityjitter.z * crandom(); + self.velocity = self.velocity + force * self.debrisdamageforcescale; - dbr.avelocity_x = random()*self.debrisavelocityjitter_x; - dbr.avelocity_y = random()*self.debrisavelocityjitter_y; - dbr.avelocity_z = random()*self.debrisavelocityjitter_z; ++ dbr.avelocity_x = random()*self.debrisavelocityjitter.x; ++ dbr.avelocity_y = random()*self.debrisavelocityjitter.y; ++ dbr.avelocity_z = random()*self.debrisavelocityjitter.z; + dbr.damageforcescale = self.debrisdamageforcescale; + if(dbr.damageforcescale) + dbr.takedamage = DAMAGE_YES; + SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime); +} + +void func_breakable_colormod() +{ + float h; + if (!(self.spawnflags & 2)) + return; + h = self.health / self.max_health; + if(h < 0.25) + self.colormod = '1 0 0'; + else if(h <= 0.75) + self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5); + else + self.colormod = '1 1 1'; - ++ + CSQCMODEL_AUTOUPDATE(); +} + +void func_breakable_look_destroyed() +{ - float floor_z; ++ float floorZ; + + if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first + self.dropped_origin = self.origin; + + if(self.mdl_dead == "") + self.model = ""; + else { + if (self.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map.. - floor_z = self.absmin_z; ++ floorZ = self.absmin.z; + setorigin(self,((self.absmax+self.absmin)*.5)); - self.origin_z = floor_z; ++ self.origin_z = floorZ; + } + setmodel(self, self.mdl_dead); + } + + self.solid = SOLID_NOT; +} + +void func_breakable_look_restore() +{ + setmodel(self, self.mdl); + if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow + setorigin(self, self.dropped_origin); + self.solid = SOLID_BSP; +} + +void func_breakable_behave_destroyed() +{ + self.health = self.max_health; + self.takedamage = DAMAGE_NO; - self.bot_attack = FALSE; ++ self.bot_attack = false; + self.event_damage = func_null; + self.state = 1; + func_breakable_colormod(); +} + +void func_breakable_behave_restore() +{ + self.health = self.max_health; + if(self.sprite) + { + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + } + self.takedamage = DAMAGE_AIM; - self.bot_attack = TRUE; ++ self.bot_attack = true; + self.event_damage = func_breakable_damage; + self.state = 0; + self.nextthink = 0; // cancel auto respawn + func_breakable_colormod(); +} + +void func_breakable_destroyed() +{ + func_breakable_look_destroyed(); + func_breakable_behave_destroyed(); - ++ + CSQCMODEL_AUTOUPDATE(); +} + +void func_breakable_restore() +{ + func_breakable_look_restore(); + func_breakable_behave_restore(); - ++ + CSQCMODEL_AUTOUPDATE(); +} + +vector debrisforce; // global, set before calling this +void func_breakable_destroy() { + float n, i; + string oldmsg; + + activator = self.owner; + self.owner = world; // set by W_PrepareExplosionByDamage + + // now throw around the debris + n = tokenize_console(self.debris); + for(i = 0; i < n; ++i) + LaunchDebris(argv(i), debrisforce); + + func_breakable_destroyed(); + + if(self.noise) + sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM); + + if(self.dmg) + RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world); + + if(self.cnt) + pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count); + + if(self.respawntime) + { + self.think = func_breakable_restore; + self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter; + } + + oldmsg = self.message; + self.message = ""; + SUB_UseTargets(); + self.message = oldmsg; +} + - void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) ++void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(self.state == 1) + return; + if(self.spawnflags & DOOR_NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if(self.team) + if(attacker.team == self.team) + return; + self.health = self.health - damage; + if(self.sprite) + { + WaypointSprite_Ping(self.sprite); + WaypointSprite_UpdateHealth(self.sprite, self.health); + } + func_breakable_colormod(); + + if(self.health <= 0) + { + debrisforce = force; + W_PrepareExplosionByDamage(attacker, func_breakable_destroy); + } +} + +void func_breakable_reset() +{ + self.team = self.team_saved; + func_breakable_look_restore(); + if(self.spawnflags & 1) + func_breakable_behave_destroyed(); + else + func_breakable_behave_restore(); - ++ + CSQCMODEL_AUTOUPDATE(); +} + +// destructible walls that can be used to trigger target_objective_decrease +void spawnfunc_func_breakable() { + float n, i; + if(!self.health) + self.health = 100; + self.max_health = self.health; + + // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway + if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE; + if(!self.debrissolid) self.debrissolid = SOLID_NOT; + if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140'; + if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70'; + if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600'; + if(!self.debristime) self.debristime = 3.5; + if(!self.debristimejitter) self.debristime = 2.5; + + if(self.mdl != "") + self.cnt = particleeffectnum(self.mdl); + if(self.count == 0) + self.count = 1; + + if(self.message == "") + self.message = "got too close to an explosion"; + if(self.message2 == "") + self.message2 = "was pushed into an explosion by"; + if(!self.dmg_radius) + self.dmg_radius = 150; + if(!self.dmg_force) + self.dmg_force = 200; + + self.mdl = self.model; + SetBrushEntityModel(); + + self.use = func_breakable_restore; + + // precache all the models + if (self.mdl_dead) + precache_model(self.mdl_dead); + n = tokenize_console(self.debris); + for(i = 0; i < n; ++i) + precache_model(argv(i)); + if(self.noise) + precache_sound(self.noise); + + self.team_saved = self.team; + self.dropped_origin = self.origin; + + self.reset = func_breakable_reset; + func_breakable_reset(); - ++ + CSQCMODEL_AUTOINIT(); +} + +// for use in maps with a "model" key set +void spawnfunc_misc_breakablemodel() { + spawnfunc_func_breakable(); +} +#endif diff --cc qcsrc/common/triggers/func/conveyor.qc index 62b2d6848,000000000..05f0fa093 mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/conveyor.qc +++ b/qcsrc/common/triggers/func/conveyor.qc @@@ -1,197 -1,0 +1,197 @@@ +void conveyor_think() +{ +#ifdef CSQC + // TODO: check if this is what is causing the glitchiness when switching between them + float dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) { return; } +#endif + entity e; + + // set myself as current conveyor where possible + for(e = world; (e = findentity(e, conveyor, self)); ) + e.conveyor = world; + + if(self.state) + { + for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain) + if(!e.conveyor.state) + if(isPushable(e)) + { + vector emin = e.absmin; + vector emax = e.absmax; + if(self.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick + if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate + e.conveyor = self; + } + + for(e = world; (e = findentity(e, conveyor, self)); ) + { + if(IS_CLIENT(e)) // doing it via velocity has quite some advantages + continue; // done in SV_PlayerPhysics continue; + + setorigin(e, e.origin + self.movedir * PHYS_INPUT_FRAMETIME); + move_out_of_solid(e); +#ifdef SVQC + UpdateCSQCProjectile(e); +#endif + /* + // stupid conveyor code + tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e); + if(trace_fraction > 0) + setorigin(e, trace_endpos); + */ + } + } + +#ifdef SVQC + self.nextthink = time; +#endif +} + +#ifdef SVQC + +void conveyor_use() +{ + self.state = !self.state; + + self.SendFlags |= 2; +} + +void conveyor_reset() +{ + self.state = (self.spawnflags & 1); + + self.SendFlags |= 2; +} + +float conveyor_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_CONVEYOR); + WriteByte(MSG_ENTITY, sf); + + if(sf & 1) + { + WriteByte(MSG_ENTITY, self.warpzone_isboxy); + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + + WriteCoord(MSG_ENTITY, self.movedir_x); + WriteCoord(MSG_ENTITY, self.movedir_y); + WriteCoord(MSG_ENTITY, self.movedir_z); + + WriteByte(MSG_ENTITY, self.speed); + WriteByte(MSG_ENTITY, self.state); + + WriteString(MSG_ENTITY, self.targetname); + WriteString(MSG_ENTITY, self.target); + } + + if(sf & 2) + WriteByte(MSG_ENTITY, self.state); + - return TRUE; ++ return true; +} + +void conveyor_init() +{ + if (!self.speed) + self.speed = 200; + self.movedir = self.movedir * self.speed; + self.think = conveyor_think; + self.nextthink = time; + IFTARGETED + { + self.use = conveyor_use; + self.reset = conveyor_reset; + conveyor_reset(); + } + else + self.state = 1; + + FixSize(self); + - Net_LinkEntity(self, 0, FALSE, conveyor_send); ++ Net_LinkEntity(self, 0, false, conveyor_send); + + self.SendFlags |= 1; +} + +void spawnfunc_trigger_conveyor() +{ + SetMovedir(); + EXACTTRIGGER_INIT; + conveyor_init(); +} + +void spawnfunc_func_conveyor() +{ + SetMovedir(); + InitMovingBrushTrigger(); + self.movetype = MOVETYPE_NONE; + conveyor_init(); +} + +#elif defined(CSQC) + +void conveyor_init() +{ + self.draw = conveyor_think; + self.drawmask = MASK_NORMAL; + + self.movetype = MOVETYPE_NONE; + self.model = ""; + self.solid = SOLID_TRIGGER; + self.move_origin = self.origin; + self.move_time = time; +} + +void ent_conveyor() +{ + float sf = ReadByte(); + + if(sf & 1) + { + self.warpzone_isboxy = ReadByte(); + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + + self.movedir_x = ReadCoord(); + self.movedir_y = ReadCoord(); + self.movedir_z = ReadCoord(); + + self.speed = ReadByte(); + self.state = ReadByte(); + + self.targetname = strzone(ReadString()); + self.target = strzone(ReadString()); + + conveyor_init(); + } + + if(sf & 2) + self.state = ReadByte(); +} +#endif diff --cc qcsrc/common/triggers/func/door.qc index 8ce8e1fd7,000000000..79277c7d7 mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/door.qc +++ b/qcsrc/common/triggers/func/door.qc @@@ -1,960 -1,0 +1,960 @@@ +/* + +Doors are similar to buttons, but can spawn a fat trigger field around them +to open without a touch, and they link together to form simultanious +double/quad doors. + +Door.owner is the master door. If there is only one door, it points to itself. +If multiple doors, all will point to a single one. + +Door.enemy chains from the master door through all doors linked in the chain. + +*/ + + +/* +============================================================================= + +THINK FUNCTIONS + +============================================================================= +*/ + +void() door_go_down; +void() door_go_up; +void() door_rotating_go_down; +void() door_rotating_go_up; + +void door_blocked() +{ + if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) + { // KIll Kill Kill!! +#ifdef SVQC + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); +#endif + } + else + { +#ifdef SVQC + if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); +#endif + + //Dont chamge direction for dead or dying stuff + if(PHYS_DEAD(other) && (other.takedamage == DAMAGE_NO)) + { + if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + if (self.classname == "door") + { + door_go_up (); + } else + { + door_rotating_go_up (); + } + else + if (self.classname == "door") + { + door_go_down (); + } else + { + door_rotating_go_down (); + } + } + } +#ifdef SVQC + else + { + //gib dying stuff just to make sure + if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } +#endif + } +} + +void door_hit_top() +{ + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = STATE_TOP; + if (self.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + if (self.classname == "door") + { + self.think = door_go_down; + } else + { + self.think = door_rotating_go_down; + } + self.nextthink = self.ltime + self.wait; +} + +void door_hit_bottom() +{ + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = STATE_BOTTOM; +} + +void door_go_down() +{ + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + if (self.max_health) + { + self.takedamage = DAMAGE_YES; + self.health = self.max_health; + } + print( +#ifdef SVQC + "Server ", +#elif defined(CSQC) + "Client ", +#endif + "going down at time ", ftos(time), "\n"); + + self.state = STATE_DOWN; + SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom); +} + +void door_go_up() +{ + if (self.state == STATE_UP) + return; // already going up + + if (self.state == STATE_TOP) + { // reset top wait time + self.nextthink = self.ltime + self.wait; + return; + } + + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + self.state = STATE_UP; + SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top); + + string oldmessage; + oldmessage = self.message; + self.message = ""; + SUB_UseTargets(); + self.message = oldmessage; +} + + +/* +============================================================================= + +ACTIVATION FUNCTIONS + +============================================================================= +*/ + +float door_check_keys(void) +{ + local entity door; + + + if (self.owner) + door = self.owner; + else + door = self; + + // no key needed + if (!door.itemkeys) - return TRUE; ++ return true; + + // this door require a key + // only a player can have a key + if (!IS_PLAYER(other)) - return FALSE; ++ return false; + +#ifdef SVQC + if (item_keys_usekey(door, other)) + { + // some keys were used + if (other.key_door_messagetime <= time) + { + + play2(other, "misc/talk.wav"); + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys)); + other.key_door_messagetime = time + 2; + } + } + else + { + // no keys were used + if (other.key_door_messagetime <= time) + { + play2(other, "misc/talk.wav"); + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys)); + + other.key_door_messagetime = time + 2; + } + } +#endif + + if (door.itemkeys) + { +#ifdef SVQC + // door is now unlocked + play2(other, "misc/talk.wav"); + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_UNLOCKED); +#endif - return TRUE; ++ return true; + } + else - return FALSE; ++ return false; +} + +void door_fire() +{ + entity oself; + entity starte; + + if (self.owner != self) + objerror ("door_fire: self.owner != self"); + + oself = self; + + if (self.spawnflags & DOOR_TOGGLE) + { + if (self.state == STATE_UP || self.state == STATE_TOP) + { + starte = self; + do + { + if (self.classname == "door") + { + door_go_down (); + } + else + { + door_rotating_go_down (); + } + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; + return; + } + } + +// trigger all paired doors + starte = self; + do + { + if (self.classname == "door") + { + door_go_up (); + } else + { + // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction + if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM) + { + self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating + self.pos2 = '0 0 0' - self.pos2; + } + // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side + if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN + && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0))))) + { + door_rotating_go_up (); + } + } + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; +} + +void door_use() +{ + entity oself; + + //dprint("door_use (model: ");dprint(self.model);dprint(")\n"); + + if (self.owner) + { + oself = self; + self = self.owner; + door_fire (); + self = oself; + } +} + +void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + entity oself; + if(self.spawnflags & DOOR_NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + self.health = self.health - damage; + + if (self.itemkeys) + { + // don't allow opening doors through damage if keys are required + return; + } + + if (self.health <= 0) + { + oself = self; + self = self.owner; + self.health = self.max_health; + self.takedamage = DAMAGE_NO; // wil be reset upon return + door_use (); + self = oself; + } +} + + +/* +================ +door_touch + +Prints messages +================ +*/ + +void door_touch() +{ + if (!IS_PLAYER(other)) + return; + if (self.owner.attack_finished_single > time) + return; + + self.owner.attack_finished_single = time + 2; + +#ifdef SVQC + if (!(self.owner.dmg) && (self.owner.message != "")) + { + if (IS_CLIENT(other)) + centerprint(other, self.owner.message); + play2(other, "misc/talk.wav"); + } +#endif +} + +void door_generic_plat_blocked() +{ + + if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!! +#ifdef SVQC + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); +#endif + } + else + { + +#ifdef SVQC + if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); +#endif + + //Dont chamge direction for dead or dying stuff + if(PHYS_DEAD(other) && (other.takedamage == DAMAGE_NO)) + { + if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + door_rotating_go_up (); + else + door_rotating_go_down (); + } + } +#ifdef SVQC + else + { + //gib dying stuff just to make sure + if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } +#endif + } +} + +void door_rotating_hit_top() +{ + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = STATE_TOP; + if (self.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + self.think = door_rotating_go_down; + self.nextthink = self.ltime + self.wait; +} + +void door_rotating_hit_bottom() +{ + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating + { + self.pos2 = '0 0 0' - self.pos2; + self.lip = 0; + } + self.state = STATE_BOTTOM; +} + +void door_rotating_go_down() +{ + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + if (self.max_health) + { + self.takedamage = DAMAGE_YES; + self.health = self.max_health; + } + + self.state = STATE_DOWN; + SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom); +} + +void door_rotating_go_up() +{ + if (self.state == STATE_UP) + return; // already going up + + if (self.state == STATE_TOP) + { // reset top wait time + self.nextthink = self.ltime + self.wait; + return; + } + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + self.state = STATE_UP; + SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top); + + string oldmessage; + oldmessage = self.message; + self.message = ""; + SUB_UseTargets(); + self.message = oldmessage; +} + + +/* +========================================= +door trigger + +Spawned if a door lacks a real activator +========================================= +*/ + +void door_trigger_touch() +{ + if (other.health < 1) +#ifdef SVQC + if (!(other.iscreature && !PHYS_DEAD(other))) +#elif defined(CSQC) + if(!(IS_CLIENT(other) && !PHYS_DEAD(other))) + return; +#endif + + if (time < self.attack_finished_single) + return; + + // check if door is locked + if (!door_check_keys()) + return; + + self.attack_finished_single = time + 1; + + activator = other; + + self = self.owner; + door_use (); +} + +#ifdef SVQC + +float door_trigger_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR_TRIGGER); + + WriteShort(MSG_ENTITY, num_for_edict(self.owner)); + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + - return TRUE; ++ return true; +} + +void door_trigger_link(entity trig) +{ - Net_LinkEntity(trig, FALSE, 0, door_trigger_send); ++ Net_LinkEntity(trig, false, 0, door_trigger_send); +} + +void spawn_field(vector fmins, vector fmaxs) +{ + entity trigger; + vector t1 = fmins, t2 = fmaxs; + + trigger = spawn(); + trigger.classname = "doortriggerfield"; + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.owner = self; + trigger.touch = door_trigger_touch; + + setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); + door_trigger_link(trigger); +} + +#elif defined(CSQC) + +void ent_door_trigger() +{ + float entnum = ReadShort(); + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + + self.owner = findfloat(world, sv_entnum, entnum); // if owner doesn't exist, it shouldn't matter much + self.classname = "doortriggerfield"; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_TRIGGER; + self.trigger_touch = door_trigger_touch; + self.draw = trigger_draw_generic; + self.drawmask = MASK_NORMAL; + self.move_time = time; +} + +#endif +#ifdef SVQC +/* +============= +LinkDoors + + +============= +*/ + +entity LinkDoors_nextent(entity cur, entity near, entity pass) +{ + while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy)) + { + } + return cur; +} + +float LinkDoors_isconnected(entity e1, entity e2, entity pass) +{ + float DELTA = 4; + if((e1.absmin_x > e2.absmax_x + DELTA) + || (e1.absmin_y > e2.absmax_y + DELTA) + || (e1.absmin_z > e2.absmax_z + DELTA) + || (e2.absmin_x > e1.absmax_x + DELTA) + || (e2.absmin_y > e1.absmax_y + DELTA) + || (e2.absmin_z > e1.absmax_z + DELTA) - ) { return FALSE; } - return TRUE; ++ ) { return false; } ++ return true; +} + +void door_link(); +void LinkDoors() +{ + entity t; + vector cmins, cmaxs; + + door_link(); + + if (self.enemy) + return; // already linked by another door + if (self.spawnflags & 4) + { + self.owner = self.enemy = self; + + if (self.health) + return; + IFTARGETED + return; + if (self.items) + return; + spawn_field(self.absmin, self.absmax); + + return; // don't want to link this door + } + + FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world); + + // set owner, and make a loop of the chain + dprint("LinkDoors: linking doors:"); + for(t = self; ; t = t.enemy) + { + dprint(" ", etos(t)); + t.owner = self; + if(t.enemy == world) + { + t.enemy = self; + break; + } + } + dprint("\n"); + + // collect health, targetname, message, size + cmins = self.absmin; + cmaxs = self.absmax; + for(t = self; ; t = t.enemy) + { + if(t.health && !self.health) + self.health = t.health; + if((t.targetname != "") && (self.targetname == "")) + self.targetname = t.targetname; + if((t.message != "") && (self.message == "")) + self.message = t.message; + if (t.absmin_x < cmins_x) + cmins_x = t.absmin_x; + if (t.absmin_y < cmins_y) + cmins_y = t.absmin_y; + if (t.absmin_z < cmins_z) + cmins_z = t.absmin_z; + if (t.absmax_x > cmaxs_x) + cmaxs_x = t.absmax_x; + if (t.absmax_y > cmaxs_y) + cmaxs_y = t.absmax_y; + if (t.absmax_z > cmaxs_z) + cmaxs_z = t.absmax_z; + if(t.enemy == self) + break; + } + + // distribute health, targetname, message + for(t = self; t; t = t.enemy) + { + t.health = self.health; + t.targetname = self.targetname; + t.message = self.message; + if(t.enemy == self) + break; + } + + // shootable, or triggered doors just needed the owner/enemy links, + // they don't spawn a field + + if (self.health) + return; + IFTARGETED + return; + if (self.items) + return; + + spawn_field(cmins, cmaxs); +} + + +/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE +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 useful for touch or takedamage doors). + +GOLD_KEY causes the door to open only if the activator holds a gold key. + +SILVER_KEY causes the door to open only if the activator holds a silver key. + +"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet +"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) +"lip" lip remaining at end of move (8 default) +"dmg" damage to inflict when blocked (2 default) +"sounds" +0) no sound +1) stone +2) base +3) stone chain +4) screechy metal +FIXME: only one sound set available at the time being + +*/ + +float door_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteString(MSG_ENTITY, self.classname); + WriteByte(MSG_ENTITY, self.spawnflags); + WriteShort(MSG_ENTITY, ((self.owner == self || !self.owner) ? -1 : num_for_edict(self.owner))); + WriteShort(MSG_ENTITY, ((self.enemy == self || !self.enemy) ? -1 : num_for_edict(self.enemy))); + WriteShort(MSG_ENTITY, num_for_edict(self)); + + WriteByte(MSG_ENTITY, self.scale); + + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteString(MSG_ENTITY, self.model); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + + WriteCoord(MSG_ENTITY, self.movedir_x); + WriteCoord(MSG_ENTITY, self.movedir_y); + WriteCoord(MSG_ENTITY, self.movedir_z); + + WriteAngle(MSG_ENTITY, self.angles_x); + WriteAngle(MSG_ENTITY, self.angles_y); + WriteAngle(MSG_ENTITY, self.angles_z); + + WriteCoord(MSG_ENTITY, self.pos1_x); + WriteCoord(MSG_ENTITY, self.pos1_y); + WriteCoord(MSG_ENTITY, self.pos1_z); + WriteCoord(MSG_ENTITY, self.pos2_x); + WriteCoord(MSG_ENTITY, self.pos2_y); + WriteCoord(MSG_ENTITY, self.pos2_z); + + WriteCoord(MSG_ENTITY, self.size_x); + WriteCoord(MSG_ENTITY, self.size_y); + WriteCoord(MSG_ENTITY, self.size_z); + + WriteShort(MSG_ENTITY, self.wait); + WriteShort(MSG_ENTITY, self.speed); + WriteByte(MSG_ENTITY, self.lip); + WriteByte(MSG_ENTITY, self.state); + WriteShort(MSG_ENTITY, self.ltime); + } + + if(sf & SF_TRIGGER_RESET) + { + // client makes use of this, we do not + } + + if(sf & SF_TRIGGER_UPDATE) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.pos1_x); + WriteCoord(MSG_ENTITY, self.pos1_y); + WriteCoord(MSG_ENTITY, self.pos1_z); + WriteCoord(MSG_ENTITY, self.pos2_x); + WriteCoord(MSG_ENTITY, self.pos2_y); + WriteCoord(MSG_ENTITY, self.pos2_z); + } + - return TRUE; ++ return true; +} + +void door_link() +{ + // set size now, as everything is loaded + FixSize(self); - Net_LinkEntity(self, FALSE, 0, door_send); ++ Net_LinkEntity(self, false, 0, door_send); +} + +void door_init_startopen() +{ + setorigin (self, self.pos2); + self.pos2 = self.pos1; + self.pos1 = self.origin; + + self.SendFlags |= SF_TRIGGER_UPDATE; +} + +#endif + +void door_reset() +{ + setorigin(self, self.pos1); + self.velocity = '0 0 0'; + self.state = STATE_BOTTOM; + self.think = func_null; + self.nextthink = 0; + +#ifdef SVQC + self.SendFlags |= SF_TRIGGER_RESET; +#endif +} + +#ifdef SVQC + +// spawnflags require key (for now only func_door) +void spawnfunc_func_door() +{ + // Quake 1 keys compatibility + if (self.spawnflags & SPAWNFLAGS_GOLD_KEY) + self.itemkeys |= ITEM_KEY_BIT(0); + if (self.spawnflags & SPAWNFLAGS_SILVER_KEY) + self.itemkeys |= ITEM_KEY_BIT(1); + + SetMovedir (); + + self.max_health = self.health; + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + self.classname = "door"; + + self.blocked = door_blocked; + self.use = door_use; + + if(self.dmg && (self.message == "")) + self.message = "was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + + if (self.sounds > 0) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise2 = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } + + if (!self.speed) + self.speed = 100; + if (!self.wait) + self.wait = 3; + if (!self.lip) + self.lip = 8; + + self.pos1 = self.origin; + self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); + +// DOOR_START_OPEN is to allow an entity to be lighted in the closed position +// but spawn in the open position + if (self.spawnflags & DOOR_START_OPEN) + InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION); + + self.state = STATE_BOTTOM; + + if (self.health) + { + self.takedamage = DAMAGE_YES; + self.event_damage = door_damage; + } + + if (self.items) + self.wait = -1; + + self.touch = door_touch; + +// LinkDoors can't be done until all of the doors have been spawned, so +// the sizes can be detected properly. + InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS); + + self.reset = door_reset; +} + +#elif defined(CSQC) + +void ent_door() +{ + float sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + self.classname = strzone(ReadString()); + self.spawnflags = ReadByte(); + float myowner = ReadShort(); + float myenemy = ReadShort(); + self.sv_entnum = ReadShort(); + + self.scale = ReadByte(); + + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.mdl = strzone(ReadString()); + setmodel(self, self.mdl); + + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + + self.movedir_x = ReadCoord(); + self.movedir_y = ReadCoord(); + self.movedir_z = ReadCoord(); + + self.angles_x = ReadAngle(); + self.angles_y = ReadAngle(); + self.angles_z = ReadAngle(); + + self.pos1_x = ReadCoord(); + self.pos1_y = ReadCoord(); + self.pos1_z = ReadCoord(); + self.pos2_x = ReadCoord(); + self.pos2_y = ReadCoord(); + self.pos2_z = ReadCoord(); + + self.size_x = ReadCoord(); + self.size_y = ReadCoord(); + self.size_z = ReadCoord(); + + self.wait = ReadShort(); + self.speed = ReadShort(); + self.lip = ReadByte(); + self.state = ReadByte(); + self.ltime = ReadShort(); + + self.movetype = MOVETYPE_PUSH; + self.solid = SOLID_BSP; + self.trigger_touch = door_touch; + self.draw = trigger_draw_generic; + self.drawmask = MASK_NORMAL; + self.move_time = time; + self.use = door_use; + self.blocked = door_blocked; + + print(ftos(self.entnum), " ", ftos(self.sv_entnum), "\n"); + + self.owner = ((myowner == -1) ? self : findfloat(world, sv_entnum, myowner)); + self.enemy = ((myenemy == -1) ? self : findfloat(world, sv_entnum, myenemy)); + } + + if(sf & SF_TRIGGER_RESET) + { + door_reset(); + } + + if(sf & SF_TRIGGER_UPDATE) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.pos1_x = ReadCoord(); + self.pos1_y = ReadCoord(); + self.pos1_z = ReadCoord(); + self.pos2_x = ReadCoord(); + self.pos2_y = ReadCoord(); + self.pos2_z = ReadCoord(); + } +} + +#endif diff --cc qcsrc/common/triggers/func/door_secret.qc index 09ded99b9,000000000..307fb77f5 mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/door_secret.qc +++ b/qcsrc/common/triggers/func/door_secret.qc @@@ -1,236 -1,0 +1,236 @@@ +#ifdef SVQC +void() fd_secret_move1; +void() fd_secret_move2; +void() fd_secret_move3; +void() fd_secret_move4; +void() fd_secret_move5; +void() fd_secret_move6; +void() fd_secret_done; + +const float SECRET_OPEN_ONCE = 1; // stays open +const float SECRET_1ST_LEFT = 2; // 1st move is left of arrow +const float SECRET_1ST_DOWN = 4; // 1st move is down from arrow +const float SECRET_NO_SHOOT = 8; // only opened by trigger +const float SECRET_YES_SHOOT = 16; // shootable even if targeted + +void fd_secret_use() +{ + float temp; + string message_save; + + self.health = 10000; - self.bot_attack = TRUE; ++ self.bot_attack = true; + + // exit if still moving around... + if (self.origin != self.oldorigin) + return; + + message_save = self.message; + self.message = ""; // no more message + SUB_UseTargets(); // fire all targets / killtargets + self.message = message_save; + + self.velocity = '0 0 0'; + + // Make a sound, wait a little... + + if (self.noise1 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.nextthink = self.ltime + 0.1; + + temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1 + makevectors(self.mangle); + + if (!self.t_width) + { + if (self.spawnflags & SECRET_1ST_DOWN) + self.t_width = fabs(v_up * self.size); + else + self.t_width = fabs(v_right * self.size); + } + + if (!self.t_length) + self.t_length = fabs(v_forward * self.size); + + if (self.spawnflags & SECRET_1ST_DOWN) + self.dest1 = self.origin - v_up * self.t_width; + else + self.dest1 = self.origin + v_right * (self.t_width * temp); + + self.dest2 = self.dest1 + v_forward * self.t_length; + SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1); + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); +} + +void fd_secret_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + fd_secret_use(); +} + +// Wait after first movement... +void fd_secret_move1() +{ + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move2; + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); +} + +// Start moving sideways w/sound... +void fd_secret_move2() +{ + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3); +} + +// Wait here until time to go back... +void fd_secret_move3() +{ + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); + if (!(self.spawnflags & SECRET_OPEN_ONCE)) + { + self.nextthink = self.ltime + self.wait; + self.think = fd_secret_move4; + } +} + +// Move backward... +void fd_secret_move4() +{ + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5); +} + +// Wait 1 second... +void fd_secret_move5() +{ + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move6; + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); +} + +void fd_secret_move6() +{ + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done); +} + +void fd_secret_done() +{ + if (self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + //self.th_pain = fd_secret_use; + } + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); +} + +void secret_blocked() +{ + if (time < self.attack_finished_single) + return; + self.attack_finished_single = time + 0.5; + //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic); +} + +/* +============== +secret_touch + +Prints messages +================ +*/ +void secret_touch() +{ + if (!other.iscreature) + return; + if (self.attack_finished_single > time) + return; + + self.attack_finished_single = time + 2; + + if (self.message) + { + if (IS_CLIENT(other)) + centerprint(other, self.message); + play2(other, "misc/talk.wav"); + } +} + +void secret_reset() +{ + if (self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + } + setorigin(self, self.oldorigin); + self.think = func_null; + self.nextthink = 0; +} + +/*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot +Basic secret door. Slides back, then to the side. Angle determines direction. +wait = # of seconds before coming back +1st_left = 1st move is left of arrow +1st_down = 1st move is down from arrow +always_shoot = even if targeted, keep shootable +t_width = override WIDTH to move back (or height if going down) +t_length = override LENGTH to move sideways +"dmg" damage to inflict when blocked (2 default) + +If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage. +"sounds" +1) medieval +2) metal +3) base +*/ + +void spawnfunc_func_door_secret() +{ + /*if (!self.deathtype) // map makers can override this + self.deathtype = " got in the way";*/ + + if (!self.dmg) + self.dmg = 2; + + // Magic formula... + self.mangle = self.angles; + self.angles = '0 0 0'; + self.classname = "door"; + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + + self.touch = secret_touch; + self.blocked = secret_blocked; + self.speed = 50; + self.use = fd_secret_use; + IFTARGETED + { + } + else + self.spawnflags |= SECRET_YES_SHOOT; + + if(self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + self.event_damage = fd_secret_damage; + } + self.oldorigin = self.origin; + if (!self.wait) + self.wait = 5; // 5 seconds before closing + + self.reset = secret_reset; + secret_reset(); +} +#endif diff --cc qcsrc/common/triggers/func/include.qc index dc0369353,000000000..290c2e984 mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/include.qc +++ b/qcsrc/common/triggers/func/include.qc @@@ -1,17 -1,0 +1,19 @@@ ++#include "include.qh" ++ +#include "bobbing.qc" +#include "breakable.qc" +#include "button.qc" +#include "conveyor.qc" +#include "door.qc" +#include "door_rotating.qc" +#include "door_secret.qc" +#include "fourier.qc" +#include "ladder.qc" +#include "pendulum.qc" +#include "plat.qc" +#include "pointparticles.qc" +#include "rainsnow.qc" +#include "rotating.qc" +#include "stardust.qc" +#include "train.qc" +#include "vectormamamam.qc" diff --cc qcsrc/common/triggers/func/include.qh index 70740c21f,000000000..cca6f91df mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/include.qh +++ b/qcsrc/common/triggers/func/include.qh @@@ -1,4 -1,0 +1,9 @@@ ++#ifndef TRIGGERS_FUNC_INCLUDE_H ++#define TRIGGERS_FUNC_INCLUDE_H ++ +#include "conveyor.qh" +#include "door.qh" +#include "ladder.qh" +#include "plat.qh" ++ ++#endif diff --cc qcsrc/common/triggers/func/ladder.qc index 76cda7536,000000000..483fef80c mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/ladder.qc +++ b/qcsrc/common/triggers/func/ladder.qc @@@ -1,107 -1,0 +1,107 @@@ +void func_ladder_touch() +{ +#ifdef SVQC + if (!other.iscreature) + return; + if (other.vehicle_flags & VHF_ISVEHICLE) + return; +#endif +#ifdef CSQC + if(other.classname != "csqcmodel") + return; +#endif + + EXACTTRIGGER_TOUCH; + + other.ladder_time = time + 0.1; + other.ladder_entity = self; +} + +#ifdef SVQC +float func_ladder_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER); + + WriteString(MSG_ENTITY, self.classname); + WriteByte(MSG_ENTITY, self.warpzone_isboxy); + WriteByte(MSG_ENTITY, self.skin); + WriteByte(MSG_ENTITY, self.speed); + WriteByte(MSG_ENTITY, self.scale); + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + + WriteCoord(MSG_ENTITY, self.movedir_x); + WriteCoord(MSG_ENTITY, self.movedir_y); + WriteCoord(MSG_ENTITY, self.movedir_z); + + WriteCoord(MSG_ENTITY, self.angles_x); + WriteCoord(MSG_ENTITY, self.angles_y); + WriteCoord(MSG_ENTITY, self.angles_z); + - return TRUE; ++ return true; +} + +void func_ladder_link() +{ - Net_LinkEntity(self, FALSE, 0, func_ladder_send); ++ Net_LinkEntity(self, false, 0, func_ladder_send); +} + +void spawnfunc_func_ladder() +{ + EXACTTRIGGER_INIT; + self.touch = func_ladder_touch; + + func_ladder_link(); +} + +void spawnfunc_func_water() +{ + EXACTTRIGGER_INIT; + self.touch = func_ladder_touch; + + func_ladder_link(); +} + +#elif defined(CSQC) +.float speed; + +void ent_func_ladder() +{ + self.classname = strzone(ReadString()); + self.warpzone_isboxy = ReadByte(); + self.skin = ReadByte(); + self.speed = ReadByte(); + self.scale = ReadByte(); + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + self.movedir_x = ReadCoord(); + self.movedir_y = ReadCoord(); + self.movedir_z = ReadCoord(); + self.angles_x = ReadCoord(); + self.angles_y = ReadCoord(); + self.angles_z = ReadCoord(); + + self.solid = SOLID_TRIGGER; + self.draw = trigger_draw_generic; + self.trigger_touch = func_ladder_touch; + self.drawmask = MASK_NORMAL; + self.move_time = time; +} +#endif diff --cc qcsrc/common/triggers/func/plat.qc index 7184c5bec,000000000..5ac8b2a8d mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/plat.qc +++ b/qcsrc/common/triggers/func/plat.qc @@@ -1,226 -1,0 +1,226 @@@ +#ifdef SVQC +void plat_delayedinit() +{ + plat_spawn_inside_trigger (); // the "start moving" trigger +} + +float plat_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_PLAT); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteShort(MSG_ENTITY, num_for_edict(self)); + WriteString(MSG_ENTITY, self.target); + WriteString(MSG_ENTITY, self.target2); + WriteString(MSG_ENTITY, self.target3); + WriteString(MSG_ENTITY, self.target4); + WriteString(MSG_ENTITY, self.targetname); + + WriteByte(MSG_ENTITY, self.platmovetype_start); + WriteByte(MSG_ENTITY, self.platmovetype_turn); + WriteByte(MSG_ENTITY, self.platmovetype_end); + + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteString(MSG_ENTITY, self.model); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + + WriteCoord(MSG_ENTITY, self.pos1_x); + WriteCoord(MSG_ENTITY, self.pos1_y); + WriteCoord(MSG_ENTITY, self.pos1_z); + WriteCoord(MSG_ENTITY, self.pos2_x); + WriteCoord(MSG_ENTITY, self.pos2_y); + WriteCoord(MSG_ENTITY, self.pos2_z); + + WriteCoord(MSG_ENTITY, self.size_x); + WriteCoord(MSG_ENTITY, self.size_y); + WriteCoord(MSG_ENTITY, self.size_z); + + WriteAngle(MSG_ENTITY, self.angles_x); + WriteAngle(MSG_ENTITY, self.angles_y); + WriteAngle(MSG_ENTITY, self.angles_z); + + WriteAngle(MSG_ENTITY, self.mangle_x); + WriteAngle(MSG_ENTITY, self.mangle_y); + WriteAngle(MSG_ENTITY, self.mangle_z); + + WriteShort(MSG_ENTITY, self.speed); + WriteShort(MSG_ENTITY, self.height); + WriteByte(MSG_ENTITY, self.lip); + WriteByte(MSG_ENTITY, self.state); + + WriteShort(MSG_ENTITY, self.dmg); + } + + if(sf & SF_TRIGGER_RESET) + { + // used on client + } + - return TRUE; ++ return true; +} + +void plat_link() +{ - Net_LinkEntity(self, 0, FALSE, plat_send); ++ Net_LinkEntity(self, 0, false, plat_send); +} + +void spawnfunc_func_plat() +{ + if (self.sounds == 0) + self.sounds = 2; + + if(self.spawnflags & 4) + self.dmg = 10000; + + if(self.dmg && (self.message == "")) + self.message = "was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + + if (self.sounds == 1) + { + precache_sound ("plats/plat1.wav"); + precache_sound ("plats/plat2.wav"); + self.noise = "plats/plat1.wav"; + self.noise1 = "plats/plat2.wav"; + } + + if (self.sounds == 2) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } + + if (self.sound1) + { + precache_sound (self.sound1); + self.noise = self.sound1; + } + if (self.sound2) + { + precache_sound (self.sound2); + self.noise1 = self.sound2; + } + + self.mangle = self.angles; + self.angles = '0 0 0'; + + self.classname = "plat"; + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + setsize (self, self.mins , self.maxs); + + self.blocked = plat_crush; + + if (!self.speed) + self.speed = 150; + if (!self.lip) + self.lip = 16; + if (!self.height) + self.height = self.size_z - self.lip; + + self.pos1 = self.origin; + self.pos2 = self.origin; + self.pos2_z = self.origin_z - self.height; + + self.reset = plat_reset; + plat_reset(); + + plat_link(); + + InitializeEntity(self, plat_delayedinit, INITPRIO_FINDTARGET); +} +#elif defined(CSQC) +void plat_draw() +{ + +} + +void ent_plat() +{ + float sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + self.sv_entnum = ReadShort(); + self.target = strzone(ReadString()); + self.target2 = strzone(ReadString()); + self.target3 = strzone(ReadString()); + self.target4 = strzone(ReadString()); + self.targetname = strzone(ReadString()); + + self.platmovetype_start = ReadByte(); + self.platmovetype_turn = ReadByte(); + self.platmovetype_end = ReadByte(); + + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.model = strzone(ReadString()); + setmodel(self, self.model); + + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + + self.pos1_x = ReadCoord(); + self.pos1_y = ReadCoord(); + self.pos1_z = ReadCoord(); + self.pos2_x = ReadCoord(); + self.pos2_y = ReadCoord(); + self.pos2_z = ReadCoord(); + + self.size_x = ReadCoord(); + self.size_y = ReadCoord(); + self.size_z = ReadCoord(); + + self.angles_x = ReadAngle(); + self.angles_y = ReadAngle(); + self.angles_z = ReadAngle(); + + self.mangle_x = ReadAngle(); + self.mangle_y = ReadAngle(); + self.mangle_z = ReadAngle(); + + self.speed = ReadShort(); + self.height = ReadShort(); + self.lip = ReadByte(); + self.state = ReadByte(); + + self.dmg = ReadShort(); + + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + self.drawmask = MASK_NORMAL; + self.draw = plat_draw; + self.use = plat_use; + + plat_reset(); // also called here + } + + if(sf & SF_TRIGGER_RESET) + { + plat_reset(); + } +} +#endif diff --cc qcsrc/common/triggers/func/rainsnow.qc index 61922f434,000000000..e86b15682 mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ b/qcsrc/common/triggers/func/rainsnow.qc @@@ -1,92 -1,0 +1,92 @@@ +#ifdef SVQC +float rainsnow_SendEntity(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW); + WriteByte(MSG_ENTITY, self.state); + WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x); + WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y); + WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x); + WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y); + WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z); + WriteShort(MSG_ENTITY, compressShortVector(self.dest)); + WriteShort(MSG_ENTITY, self.count); + WriteByte(MSG_ENTITY, self.cnt); + return 1; +} + +/*QUAKED spawnfunc_func_rain (0 .5 .8) ? +This is an invisible area like a trigger, which rain falls inside of. + +Keys: +"velocity" + falling direction (should be something like '0 0 -700', use the X and Y velocity for wind) +"cnt" + sets color of rain (default 12 - white) +"count" + adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000 +*/ +void spawnfunc_func_rain() +{ + self.dest = self.velocity; + self.velocity = '0 0 0'; + if (!self.dest) + self.dest = '0 0 -700'; + self.angles = '0 0 0'; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + SetBrushEntityModel(); + if (!self.cnt) + self.cnt = 12; + if (!self.count) + self.count = 2000; + self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024); + if (self.count < 1) + self.count = 1; + if(self.count > 65535) + self.count = 65535; + + self.state = 1; // 1 is rain, 0 is snow + self.Version = 1; + - Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity); ++ Net_LinkEntity(self, false, 0, rainsnow_SendEntity); +} + + +/*QUAKED spawnfunc_func_snow (0 .5 .8) ? +This is an invisible area like a trigger, which snow falls inside of. + +Keys: +"velocity" + falling direction (should be something like '0 0 -300', use the X and Y velocity for wind) +"cnt" + sets color of rain (default 12 - white) +"count" + adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000 +*/ +void spawnfunc_func_snow() +{ + self.dest = self.velocity; + self.velocity = '0 0 0'; + if (!self.dest) + self.dest = '0 0 -300'; + self.angles = '0 0 0'; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + SetBrushEntityModel(); + if (!self.cnt) + self.cnt = 12; + if (!self.count) + self.count = 2000; + self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024); + if (self.count < 1) + self.count = 1; + if(self.count > 65535) + self.count = 65535; + + self.state = 0; // 1 is rain, 0 is snow + self.Version = 1; + - Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity); ++ Net_LinkEntity(self, false, 0, rainsnow_SendEntity); +} +#endif diff --cc qcsrc/common/triggers/func/train.qc index e442eb580,000000000..14da4595f mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/train.qc +++ b/qcsrc/common/triggers/func/train.qc @@@ -1,165 -1,0 +1,165 @@@ +#ifdef SVQC +.float train_wait_turning; +void() train_next; +void train_wait() +{ + entity oldself; + oldself = self; + self = self.enemy; + SUB_UseTargets(); + self = oldself; + self.enemy = world; + + // if turning is enabled, the train will turn toward the next point while waiting + if(self.platmovetype_turn && !self.train_wait_turning) + { + entity targ, cp; + vector ang; + targ = find(world, targetname, self.target); + if((self.spawnflags & 1) && targ.curvetarget) + cp = find(world, targetname, targ.curvetarget); + else + cp = world; + + if(cp) // bezier curves movement + ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner + else // linear movement + ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner + ang = vectoangles(ang); + ang_x = -ang_x; // flip up / down orientation + + if(self.wait > 0) // slow turning + SUB_CalcAngleMove(ang, TSPEED_TIME, self.ltime - time + self.wait, train_wait); + else // instant turning + SUB_CalcAngleMove(ang, TSPEED_TIME, 0.0000001, train_wait); - self.train_wait_turning = TRUE; ++ self.train_wait_turning = true; + return; + } + + if(self.noise != "") + stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway + + if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning + { - self.train_wait_turning = FALSE; ++ self.train_wait_turning = false; + train_next(); + } + else + { + self.think = train_next; + self.nextthink = self.ltime + self.wait; + } +} + +void train_next() +{ + entity targ, cp = world; + vector cp_org = '0 0 0'; + + targ = find(world, targetname, self.target); + self.target = targ.target; + if (self.spawnflags & 1) + { + if(targ.curvetarget) + { + cp = find(world, targetname, targ.curvetarget); // get its second target (the control point) + cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination + } + } + if (self.target == "") + objerror("train_next: no next target"); + self.wait = targ.wait; + if (!self.wait) + self.wait = 0.1; + + if(targ.platmovetype) + { + // this path_corner contains a movetype overrider, apply it + self.platmovetype_start = targ.platmovetype_start; + self.platmovetype_end = targ.platmovetype_end; + } + else + { + // this path_corner doesn't contain a movetype overrider, use the train's defaults + self.platmovetype_start = self.platmovetype_start_default; + self.platmovetype_end = self.platmovetype_end_default; + } + + if (targ.speed) + { + if (cp) + SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + else + SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + } + else + { + if (cp) + SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait); + else + SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait); + } + + if(self.noise != "") + sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE); +} + +void func_train_find() +{ + entity targ; + targ = find(world, targetname, self.target); + self.target = targ.target; + if (self.target == "") + objerror("func_train_find: no next target"); + setorigin(self, targ.origin - self.view_ofs); + self.nextthink = self.ltime + 1; + self.think = train_next; +} + +/*QUAKED spawnfunc_func_train (0 .5 .8) ? +Ridable platform, targets spawnfunc_path_corner path to follow. +speed : speed the train moves (can be overridden by each spawnfunc_path_corner) +target : targetname of first spawnfunc_path_corner (starts here) +*/ +void spawnfunc_func_train() +{ + if (self.noise != "") + precache_sound(self.noise); + + if (self.target == "") + objerror("func_train without a target"); + if (!self.speed) + self.speed = 100; + + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + + if (self.spawnflags & 2) + { - self.platmovetype_turn = TRUE; ++ self.platmovetype_turn = true; + self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now + } + else + self.view_ofs = self.mins; + + // wait for targets to spawn + InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION); + + self.blocked = generic_plat_blocked; + if(self.dmg && (self.message == "")) + self.message = " was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + if(self.dmg && (!self.dmgtime)) + self.dmgtime = 0.25; + self.dmgtime2 = time; + + if(!set_platmovetype(self, self.platmovetype)) + return; + self.platmovetype_start_default = self.platmovetype_start; + self.platmovetype_end_default = self.platmovetype_end; + + // TODO make a reset function for this one +} +#endif diff --cc qcsrc/common/triggers/include.qc index b90a75c27,000000000..8189d3b72 mode 100644,000000..100644 --- a/qcsrc/common/triggers/include.qc +++ b/qcsrc/common/triggers/include.qc @@@ -1,17 -1,0 +1,19 @@@ ++#include "include.qh" ++ +// some required common stuff +#include "subs.qc" +#include "triggers.qc" +#include "platforms.qc" + +// func +#include "func/include.qc" + +// misc +#include "misc/include.qc" + +// target +#include "target/include.qc" + +// trigger +#include "trigger/include.qc" + diff --cc qcsrc/common/triggers/include.qh index 705dec7f1,000000000..1cd37db5b mode 100644,000000..100644 --- a/qcsrc/common/triggers/include.qh +++ b/qcsrc/common/triggers/include.qh @@@ -1,19 -1,0 +1,24 @@@ ++#ifndef TRIGGERS_INCLUDE_H ++#define TRIGGERS_INCLUDE_H ++ +// some required common stuff +#ifdef CSQC + #include "../../server/item_key.qh" +#endif +#include "triggers.qh" +#include "subs.qh" +#include "platforms.qh" + +// func +#include "func/include.qh" + +// misc +#include "misc/include.qh" + +// target +#include "target/include.qh" + +// trigger +#include "trigger/include.qh" ++ ++#endif diff --cc qcsrc/common/triggers/misc/include.qc index 16212a4e9,000000000..8c5678f96 mode 100644,000000..100644 --- a/qcsrc/common/triggers/misc/include.qc +++ b/qcsrc/common/triggers/misc/include.qc @@@ -1,3 -1,0 +1,5 @@@ ++#include "include.qh" ++ +#include "corner.qc" +#include "follow.qc" +#include "laser.qc" diff --cc qcsrc/common/triggers/misc/include.qh index 8f9537e92,000000000..6f43e2a89 mode 100644,000000..100644 --- a/qcsrc/common/triggers/misc/include.qh +++ b/qcsrc/common/triggers/misc/include.qh @@@ -1,1 -1,0 +1,6 @@@ ++#ifndef TRIGGERS_MISC_INCLUDE_H ++#define TRIGGERS_MISC_INCLUDE_H ++ +// nothing yet ++ ++#endif diff --cc qcsrc/common/triggers/misc/laser.qc index 987777a65,000000000..2aaf399f5 mode 100644,000000..100644 --- a/qcsrc/common/triggers/misc/laser.qc +++ b/qcsrc/common/triggers/misc/laser.qc @@@ -1,257 -1,0 +1,257 @@@ +#ifdef SVQC +.float modelscale; +void misc_laser_aim() +{ + vector a; + if(self.enemy) + { + if(self.spawnflags & 2) + { + if(self.enemy.origin != self.mangle) + { + self.mangle = self.enemy.origin; + self.SendFlags |= 2; + } + } + else + { + a = vectoangles(self.enemy.origin - self.origin); + a_x = -a_x; + if(a != self.mangle) + { + self.mangle = a; + self.SendFlags |= 2; + } + } + } + else + { + if(self.angles != self.mangle) + { + self.mangle = self.angles; + self.SendFlags |= 2; + } + } + if(self.origin != self.oldorigin) + { + self.SendFlags |= 1; + self.oldorigin = self.origin; + } +} + +void misc_laser_init() +{ + if(self.target != "") + self.enemy = find(world, targetname, self.target); +} + +.entity pusher; +void misc_laser_think() +{ + vector o; + entity oldself; + entity hitent; + vector hitloc; + + self.nextthink = time; + + if(!self.state) + return; + + misc_laser_aim(); + + if(self.enemy) + { + o = self.enemy.origin; + if (!(self.spawnflags & 2)) + o = self.origin + normalize(o - self.origin) * 32768; + } + else + { + makevectors(self.mangle); + o = self.origin + v_forward * 32768; + } + + if(self.dmg || self.enemy.target != "") + { + traceline(self.origin, o, MOVE_NORMAL, self); + } + hitent = trace_ent; + hitloc = trace_endpos; + + if(self.enemy.target != "") // DETECTOR laser + { + if(trace_ent.iscreature) + { + self.pusher = hitent; + if(!self.count) + { + self.count = 1; + + oldself = self; + self = self.enemy; + activator = self.pusher; + SUB_UseTargets(); + self = oldself; + } + } + else + { + if(self.count) + { + self.count = 0; + + oldself = self; + self = self.enemy; + activator = self.pusher; + SUB_UseTargets(); + self = oldself; + } + } + } + + if(self.dmg) + { + if(self.team) + if(((self.spawnflags & 8) == 0) == (self.team != hitent.team)) + return; + if(hitent.takedamage) + Damage(hitent, self, self, ((self.dmg < 0) ? 100000 : (self.dmg * frametime)), DEATH_HURTTRIGGER, hitloc, '0 0 0'); + } +} + +float laser_SendEntity(entity to, float fl) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_LASER); + fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser + if(self.spawnflags & 2) + fl |= 0x80; + if(self.alpha) + fl |= 0x40; + if(self.scale != 1 || self.modelscale != 1) + fl |= 0x20; + if(self.spawnflags & 4) + fl |= 0x10; + WriteByte(MSG_ENTITY, fl); + if(fl & 1) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + } + if(fl & 8) + { + WriteByte(MSG_ENTITY, self.colormod_x * 255.0); + WriteByte(MSG_ENTITY, self.colormod_y * 255.0); + WriteByte(MSG_ENTITY, self.colormod_z * 255.0); + if(fl & 0x40) + WriteByte(MSG_ENTITY, self.alpha * 255.0); + if(fl & 0x20) + { + WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255)); + WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255)); + } + if((fl & 0x80) || !(fl & 0x10)) // effect doesn't need sending if the laser is infinite and has collision testing turned off + WriteShort(MSG_ENTITY, self.cnt + 1); + } + if(fl & 2) + { + if(fl & 0x80) + { + WriteCoord(MSG_ENTITY, self.enemy.origin_x); + WriteCoord(MSG_ENTITY, self.enemy.origin_y); + WriteCoord(MSG_ENTITY, self.enemy.origin_z); + } + else + { + WriteAngle(MSG_ENTITY, self.mangle_x); + WriteAngle(MSG_ENTITY, self.mangle_y); + } + } + if(fl & 4) + WriteByte(MSG_ENTITY, self.state); + return 1; +} + +/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED +Any object touching the beam will be hurt +Keys: +"target" + spawnfunc_target_position where the laser ends +"mdl" + name of beam end effect to use +"colormod" + color of the beam (default: red) +"dmg" + damage per second (-1 for a laser that kills immediately) +*/ +void laser_use() +{ + self.state = !self.state; + self.SendFlags |= 4; + misc_laser_aim(); +} + +void laser_reset() +{ + if(self.spawnflags & 1) + self.state = 1; + else + self.state = 0; +} + +void spawnfunc_misc_laser() +{ + if(self.mdl) + { + if(self.mdl == "none") + self.cnt = -1; + else + { + self.cnt = particleeffectnum(self.mdl); + if(self.cnt < 0) + if(self.dmg) + self.cnt = particleeffectnum("laser_deadly"); + } + } + else if(!self.cnt) + { + if(self.dmg) + self.cnt = particleeffectnum("laser_deadly"); + else + self.cnt = -1; + } + if(self.cnt < 0) + self.cnt = -1; + + if(self.colormod == '0 0 0') + if(!self.alpha) + self.colormod = '1 0 0'; + if(self.message == "") + self.message = "saw the light"; + if (self.message2 == "") + self.message2 = "was pushed into a laser by"; + if(!self.scale) + self.scale = 1; + if(!self.modelscale) + self.modelscale = 1; + else if(self.modelscale < 0) + self.modelscale = 0; + self.think = misc_laser_think; + self.nextthink = time; + InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET); + + self.mangle = self.angles; + - Net_LinkEntity(self, FALSE, 0, laser_SendEntity); ++ Net_LinkEntity(self, false, 0, laser_SendEntity); + + IFTARGETED + { + self.reset = laser_reset; + laser_reset(); + self.use = laser_use; + } + else + self.state = 1; +} +#endif diff --cc qcsrc/common/triggers/platforms.qc index 03c2f0fe5,000000000..fd4825e2f mode 100644,000000..100644 --- a/qcsrc/common/triggers/platforms.qc +++ b/qcsrc/common/triggers/platforms.qc @@@ -1,273 -1,0 +1,273 @@@ +void generic_plat_blocked() +{ +#ifdef SVQC + if(self.dmg && other.takedamage != DAMAGE_NO) + { + if(self.dmgtime2 < time) + { + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + self.dmgtime2 = time + self.dmgtime; + } + + // Gib dead/dying stuff + if(other.deadflag != DEAD_NO) + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } +#endif +} + +#ifdef SVQC +float plat_trigger_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_PLAT_TRIGGER); + WriteShort(MSG_ENTITY, num_for_edict(self.enemy)); + + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); - return TRUE; ++ return true; +} + +void plat_spawn_inside_trigger() +{ + entity trigger; + vector tmin, tmax; + + trigger = spawn(); + trigger.touch = plat_center_touch; + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.enemy = self; + + tmin = self.absmin + '25 25 0'; + tmax = self.absmax - '25 25 -8'; + tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8); + if (self.spawnflags & PLAT_LOW_TRIGGER) + tmax_z = tmin_z + 8; + + if (self.size_x <= 50) + { + tmin_x = (self.mins_x + self.maxs_x) / 2; + tmax_x = tmin_x + 1; + } + if (self.size_y <= 50) + { + tmin_y = (self.mins_y + self.maxs_y) / 2; + tmax_y = tmin_y + 1; + } + + if(tmin_x < tmax_x) + if(tmin_y < tmax_y) + if(tmin_z < tmax_z) + { + setsize (trigger, tmin, tmax); - Net_LinkEntity(trigger, FALSE, 0, plat_trigger_send); ++ Net_LinkEntity(trigger, false, 0, plat_trigger_send); + return; + } + + // otherwise, something is fishy... + remove(trigger); + objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); +} +#elif defined(CSQC) +void ent_plat_trigger() +{ + float myenemy = ReadShort(); + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + + self.enemy = findfloat(world, sv_entnum, myenemy); + if(!self.enemy) { print("^1BAD BAD BAD!!!\n"); } + self.drawmask = MASK_NORMAL; + self.draw = trigger_draw_generic; + self.trigger_touch = plat_center_touch; + self.solid = SOLID_TRIGGER; +} +#endif + +void plat_hit_top() +{ + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = 1; + self.think = plat_go_down; + self.nextthink = self.ltime + 3; +} + +void plat_hit_bottom() +{ + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = 2; +} + +void plat_go_down() +{ + sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM); + self.state = 3; + SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom); +} + +void plat_go_up() +{ + sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM); + self.state = 4; + SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top); +} + +void plat_center_touch() +{ +#ifdef SVQC + if (!other.iscreature) + return; + + if (other.health <= 0) + return; +#elif defined(CSQC) + if (!IS_PLAYER(other)) + return; +#endif + +#ifdef CSQC + print("Got this far\n"); +#endif + + self = self.enemy; + if (self.state == 2) + plat_go_up (); + else if (self.state == 1) + self.nextthink = self.ltime + 1; // delay going down +} + +void plat_outside_touch() +{ +#ifdef SVQC + if (!other.iscreature) + return; + + if (other.health <= 0) + return; +#elif defined(CSQC) + if (!IS_PLAYER(other)) + return; +#endif + + self = self.enemy; + if (self.state == 1) + plat_go_down (); +} + +void plat_trigger_use() +{ + if (self.think) + return; // already activated + plat_go_down(); +} + + +void plat_crush() +{ + if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) + { // KIll Kill Kill!! +#ifdef SVQC + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); +#endif + } + else + { +#ifdef SVQC + if((self.dmg) && (other.takedamage != DAMAGE_NO)) + { // Shall we bite? + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + // Gib dead/dying stuff + if(other.deadflag != DEAD_NO) + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } +#endif + + if (self.state == 4) + plat_go_down (); + else if (self.state == 3) + plat_go_up (); + // when in other states, then the plat_crush event came delayed after + // plat state already had changed + // this isn't a bug per se! + } +} + +void plat_use() +{ + self.use = func_null; + if (self.state != 4) + objerror ("plat_use: not in up state"); + plat_go_down(); +} + +.string sound1, sound2; + +void plat_reset() +{ + IFTARGETED + { + setorigin (self, self.pos1); + self.state = 4; + self.use = plat_use; + } + else + { + setorigin (self, self.pos2); + self.state = 2; + self.use = plat_trigger_use; + } + +#ifdef SVQC + self.SendFlags |= SF_TRIGGER_RESET; +#endif +} + +#ifdef SVQC +.float platmovetype_start_default, platmovetype_end_default; +float set_platmovetype(entity e, string s) +{ + // sets platmovetype_start and platmovetype_end based on a string consisting of two values + + float n; + n = tokenize_console(s); + if(n > 0) + e.platmovetype_start = stof(argv(0)); + else + e.platmovetype_start = 0; + + if(n > 1) + e.platmovetype_end = stof(argv(1)); + else + e.platmovetype_end = e.platmovetype_start; + + if(n > 2) + if(argv(2) == "force") - return TRUE; // no checking, return immediately ++ return true; // no checking, return immediately + + if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end)) + { + objerror("Invalid platform move type; platform would go in reverse, which is not allowed."); - return FALSE; ++ return false; + } + - return TRUE; ++ return true; +} +#endif diff --cc qcsrc/common/triggers/platforms.qh index 84d9f7a91,000000000..69ad2fae5 mode 100644,000000..100644 --- a/qcsrc/common/triggers/platforms.qh +++ b/qcsrc/common/triggers/platforms.qh @@@ -1,15 -1,0 +1,20 @@@ ++#ifndef PLATFORMS_H ++#define PLATFORMS_H ++ +.float dmgtime2; + +void() plat_center_touch; +void() plat_outside_touch; +void() plat_trigger_use; +void() plat_go_up; +void() plat_go_down; +void() plat_crush; +const float PLAT_LOW_TRIGGER = 1; + +.float dmg; + +#ifdef CSQC +void ent_plat_trigger(); +#endif ++ ++#endif diff --cc qcsrc/common/triggers/subs.qh index 2c2ba7d00,000000000..bf0994897 mode 100644,000000..100644 --- a/qcsrc/common/triggers/subs.qh +++ b/qcsrc/common/triggers/subs.qh @@@ -1,67 -1,0 +1,72 @@@ ++#ifndef SUBS_H ++#define SUBS_H ++ +void SUB_Remove(); +void SUB_SetFade (entity ent, float when, float fading_time); +void SUB_VanishOrRemove (entity ent); + +.vector finaldest, finalangle; //plat.qc stuff +.void() think1; +.float state; +.float t_length, t_width; + +.vector destvec; +.vector destvec2; + +// player animation state +.float animstate_startframe; +.float animstate_numframes; +.float animstate_framerate; +.float animstate_starttime; +.float animstate_endtime; +.float animstate_override; +.float animstate_looping; + +.float delay; +.float wait; +.float lip; +.float speed; +.float sounds; +.string platmovetype; +.float platmovetype_start, platmovetype_end; + +entity activator; + +.string killtarget; + +.vector pos1, pos2; +.vector mangle; + +.string target2; +.string target3; +.string target4; +.string curvetarget; +.float target_random; +.float trigger_reverse; + +// Keys player is holding +.float itemkeys; +// message delay for func_door locked by keys and key locks +// this field is used on player entities +.float key_door_messagetime; + +.vector dest1, dest2; + +#ifdef CSQC +// this stuff is defined in the server side engine VM, so we must define it separately here +.float takedamage; +const float DAMAGE_NO = 0; +const float DAMAGE_YES = 1; +const float DAMAGE_AIM = 2; + +float STATE_TOP = 0; +float STATE_BOTTOM = 1; +float STATE_UP = 2; +float STATE_DOWN = 3; + +.string noise, noise1, noise2, noise3; // contains names of wavs to play + +.float max_health; // players maximum health is stored here +#endif ++ ++#endif diff --cc qcsrc/common/triggers/target/include.qc index a8c876b9c,000000000..3b8e02442 mode 100644,000000..100644 --- a/qcsrc/common/triggers/target/include.qc +++ b/qcsrc/common/triggers/target/include.qc @@@ -1,5 -1,0 +1,7 @@@ ++#include "include.qh" ++ +#include "changelevel.qc" +#include "music.qc" +#include "spawn.qc" +#include "speaker.qc" +#include "voicescript.qc" diff --cc qcsrc/common/triggers/target/include.qh index 8f9537e92,000000000..cadbf3746 mode 100644,000000..100644 --- a/qcsrc/common/triggers/target/include.qh +++ b/qcsrc/common/triggers/target/include.qh @@@ -1,1 -1,0 +1,6 @@@ ++#ifndef TRIGGERS_TARGET_INCLUDE_H ++#define TRIGGERS_TARGET_INCLUDE_H ++ +// nothing yet ++ ++#endif diff --cc qcsrc/common/triggers/target/music.qc index 5aa095d12,000000000..fb38dad0b mode 100644,000000..100644 --- a/qcsrc/common/triggers/target/music.qc +++ b/qcsrc/common/triggers/target/music.qc @@@ -1,136 -1,0 +1,146 @@@ ++#if defined(CSQC) ++#elif defined(MENUQC) ++#elif defined(SVQC) ++ #include "../../../dpdefs/progsdefs.qh" ++ #include "../../../dpdefs/dpextensions.qh" ++ #include "../../constants.qh" ++ #include "../../../server/constants.qh" ++ #include "../../../server/defs.qh" ++#endif ++ +#ifdef SVQC - .float lifetime; ++ +// values: +// volume +// noise +// targetname +// lifetime +// fade_time +// fade_rate +// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0) +// when targetname is not set, THIS ONE is default +void target_music_sendto(float to, float is) +{ + WriteByte(to, SVC_TEMPENTITY); + WriteByte(to, TE_CSQC_TARGET_MUSIC); + WriteShort(to, num_for_edict(self)); + WriteByte(to, self.volume * 255.0 * is); + WriteByte(to, self.fade_time * 16.0); + WriteByte(to, self.fade_rate * 16.0); + WriteByte(to, self.lifetime); + WriteString(to, self.noise); +} +void target_music_reset() +{ + if(self.targetname == "") + target_music_sendto(MSG_ALL, 1); +} +void target_music_use() +{ + if(!activator) + return; + if(IS_REAL_CLIENT(activator)) + { + msg_entity = activator; + target_music_sendto(MSG_ONE, 1); + } + entity head; + FOR_EACH_SPEC(head) if(head.enemy == activator) { msg_entity = head; target_music_sendto(MSG_ONE, 1); } +} +void spawnfunc_target_music() +{ + self.use = target_music_use; + self.reset = target_music_reset; + if(!self.volume) + self.volume = 1; + if(self.targetname == "") + target_music_sendto(MSG_INIT, 1); + else + target_music_sendto(MSG_INIT, 0); +} +void TargetMusic_RestoreGame() +{ + for(self = world; (self = find(self, classname, "target_music")); ) + { + if(self.targetname == "") + target_music_sendto(MSG_INIT, 1); + else + target_music_sendto(MSG_INIT, 0); + } +} +// values: +// volume +// noise +// targetname +// fade_time +// spawnflags: +// 1 = START_OFF +// when triggered, it is disabled/enabled for everyone +float trigger_music_SendEntity(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); + sf &= ~0x80; + if(self.cnt) + sf |= 0x80; + WriteByte(MSG_ENTITY, sf); + if(sf & 4) + { - WriteCoord(MSG_ENTITY, self.origin_x); - WriteCoord(MSG_ENTITY, self.origin_y); - WriteCoord(MSG_ENTITY, self.origin_z); ++ WriteCoord(MSG_ENTITY, self.origin.x); ++ WriteCoord(MSG_ENTITY, self.origin.y); ++ WriteCoord(MSG_ENTITY, self.origin.z); + } + if(sf & 1) + { + if(self.model != "null") + { + WriteShort(MSG_ENTITY, self.modelindex); - WriteCoord(MSG_ENTITY, self.mins_x); - WriteCoord(MSG_ENTITY, self.mins_y); - WriteCoord(MSG_ENTITY, self.mins_z); - WriteCoord(MSG_ENTITY, self.maxs_x); - WriteCoord(MSG_ENTITY, self.maxs_y); - WriteCoord(MSG_ENTITY, self.maxs_z); ++ WriteCoord(MSG_ENTITY, self.mins.x); ++ WriteCoord(MSG_ENTITY, self.mins.y); ++ WriteCoord(MSG_ENTITY, self.mins.z); ++ WriteCoord(MSG_ENTITY, self.maxs.x); ++ WriteCoord(MSG_ENTITY, self.maxs.y); ++ WriteCoord(MSG_ENTITY, self.maxs.z); + } + else + { + WriteShort(MSG_ENTITY, 0); - WriteCoord(MSG_ENTITY, self.maxs_x); - WriteCoord(MSG_ENTITY, self.maxs_y); - WriteCoord(MSG_ENTITY, self.maxs_z); ++ WriteCoord(MSG_ENTITY, self.maxs.x); ++ WriteCoord(MSG_ENTITY, self.maxs.y); ++ WriteCoord(MSG_ENTITY, self.maxs.z); + } + WriteByte(MSG_ENTITY, self.volume * 255.0); + WriteByte(MSG_ENTITY, self.fade_time * 16.0); + WriteByte(MSG_ENTITY, self.fade_rate * 16.0); + WriteString(MSG_ENTITY, self.noise); + } + return 1; +} +void trigger_music_reset() +{ + self.cnt = !(self.spawnflags & 1); + self.SendFlags |= 0x80; +} +void trigger_music_use() +{ + self.cnt = !self.cnt; + self.SendFlags |= 0x80; +} +void spawnfunc_trigger_music() +{ + if(self.model != "") + setmodel(self, self.model); + if(!self.volume) + self.volume = 1; + if(!self.modelindex) + { + setorigin(self, self.origin + self.mins); + setsize(self, '0 0 0', self.maxs - self.mins); + } + trigger_music_reset(); + + self.use = trigger_music_use; + self.reset = trigger_music_reset; + - Net_LinkEntity(self, FALSE, 0, trigger_music_SendEntity); ++ Net_LinkEntity(self, false, 0, trigger_music_SendEntity); +} +#endif diff --cc qcsrc/common/triggers/target/music.qh index 000000000,000000000..400e4b8e5 new file mode 100644 --- /dev/null +++ b/qcsrc/common/triggers/target/music.qh @@@ -1,0 -1,0 +1,6 @@@ ++#ifndef TARGET_MUSIC_H ++#define TARGET_MUSIC_H ++ ++.float lifetime; ++ ++#endif diff --cc qcsrc/common/triggers/target/spawn.qc index d661b98eb,000000000..98a3209cc mode 100644,000000..100644 --- a/qcsrc/common/triggers/target/spawn.qc +++ b/qcsrc/common/triggers/target/spawn.qc @@@ -1,337 -1,0 +1,347 @@@ ++#if defined(CSQC) ++#elif defined(MENUQC) ++#elif defined(SVQC) ++ #include "../../../dpdefs/progsdefs.qh" ++ #include "../../../dpdefs/dpextensions.qh" ++ #include "../../util.qh" ++ #include "../../../server/defs.qh" ++#endif ++ +#ifdef SVQC ++ +// spawner entity +// "classname" "target_spawn" +// "message" "fieldname value fieldname value ..." +// "spawnflags" +// 1 = call the spawn function +// 2 = trigger on map load + +float target_spawn_initialized; +.void() target_spawn_spawnfunc; +float target_spawn_spawnfunc_field; +.entity target_spawn_activator; +.float target_spawn_id; +float target_spawn_count; + +void target_spawn_helper_setmodel() +{ + setmodel(self, self.model); +} + +void target_spawn_helper_setsize() +{ + setsize(self, self.mins, self.maxs); +} + +void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act) +{ + float i, n, valuefieldpos; + string key, value, valuefield, valueoffset, valueoffsetrandom; + entity valueent; + vector data, data2; + entity oldself; + entity oldactivator; + + n = tokenize_console(msg); + + for(i = 0; i < n-1; i += 2) + { + key = argv(i); + value = argv(i+1); + if(key == "$") + { - data_x = -1; - data_y = FIELD_STRING; ++ data.x = -1; ++ data.y = FIELD_STRING; + } + else + { + data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key))); - if(data_y == 0) // undefined field, i.e., invalid type ++ if(data.y == 0) // undefined field, i.e., invalid type + { + print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n"); + continue; + } + } + if(substring(value, 0, 1) == "$") + { + value = substring(value, 1, strlen(value) - 1); + if(substring(value, 0, 1) == "$") + { + // deferred replacement + // do nothing + // useful for creating target_spawns with this! + } + else + { + // replace me! + valuefieldpos = strstrofs(value, "+", 0); + valueoffset = ""; + if(valuefieldpos != -1) + { + valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); + value = substring(value, 0, valuefieldpos); + } + + valuefieldpos = strstrofs(valueoffset, "+", 0); + valueoffsetrandom = ""; + if(valuefieldpos != -1) + { + valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1); + valueoffset = substring(valueoffset, 0, valuefieldpos); + } + + valuefieldpos = strstrofs(value, ".", 0); + valuefield = ""; + if(valuefieldpos != -1) + { + valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); + value = substring(value, 0, valuefieldpos); + } + + if(value == "self") + { + valueent = self; + value = ""; + } + else if(value == "activator") + { + valueent = act; + value = ""; + } + else if(value == "other") + { + valueent = other; + value = ""; + } + else if(value == "pusher") + { + if(time < act.pushltime) + valueent = act.pusher; + else + valueent = world; + value = ""; + } + else if(value == "target") + { + valueent = e; + value = ""; + } + else if(value == "killtarget") + { + valueent = kt; + value = ""; + } + else if(value == "target2") + { + valueent = t2; + value = ""; + } + else if(value == "target3") + { + valueent = t3; + value = ""; + } + else if(value == "target4") + { + valueent = t4; + value = ""; + } + else if(value == "time") + { + valueent = world; + value = ftos(time); + } + else + { + print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n"); + continue; + } + + if(valuefield == "") + { + if(value == "") + value = ftos(num_for_edict(valueent)); + } + else + { + if(value != "") + { + print("target_spawn: try to get a field of a non-entity, ignored!\n"); + continue; + } + data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield))); + if(data2_y == 0) // undefined field, i.e., invalid type + { + print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n"); + continue; + } + value = getentityfieldstring(data2_x, valueent); + } + + if(valueoffset != "") + { - switch(data_y) ++ switch(data.y) + { + case FIELD_STRING: + value = strcat(value, valueoffset); + break; + case FIELD_FLOAT: + value = ftos(stof(value) + stof(valueoffset)); + break; + case FIELD_VECTOR: + value = vtos(stov(value) + stov(valueoffset)); + break; + default: + print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n"); + break; + } + } + + if(valueoffsetrandom != "") + { - switch(data_y) ++ switch(data.y) + { + case FIELD_FLOAT: + value = ftos(stof(value) + random() * stof(valueoffsetrandom)); + break; + case FIELD_VECTOR: + data2 = stov(valueoffsetrandom); + value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1'); + break; + default: + print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n"); + break; + } + } + } + } + if(key == "$") + { + if(substring(value, 0, 1) == "_") + value = strcat("target_spawn_helper", value); + putentityfieldstring(target_spawn_spawnfunc_field, e, value); + + oldself = self; + oldactivator = activator; + + self = e; + activator = act; + + self.target_spawn_spawnfunc(); + + self = oldself; + activator = oldactivator; + } + else + { - if(data_y == FIELD_VECTOR) ++ if(data.y == FIELD_VECTOR) + value = strreplace("'", "", value); // why?!? - putentityfieldstring(data_x, e, value); ++ putentityfieldstring(data.x, e, value); + } + } +} + +void target_spawn_useon(entity e) +{ + self.target_spawn_activator = activator; + target_spawn_edit_entity( + e, + self.message, + find(world, targetname, self.killtarget), + find(world, targetname, self.target2), + find(world, targetname, self.target3), + find(world, targetname, self.target4), + activator + ); +} + +float target_spawn_cancreate() +{ + float c; + entity e; + + c = self.count; + if(c == 0) // no limit? + return 1; + + ++c; // increase count to not include MYSELF + for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c) + ; + + // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more + if(c == 0) + return 0; + return 1; +} + +void target_spawn_use() +{ + entity e; + + if(self.target == "") + { + // spawn new entity + if(!target_spawn_cancreate()) + return; + e = spawn(); + target_spawn_useon(e); + e.target_spawn_id = self.target_spawn_id; + } + else if(self.target == "*activator") + { + // edit entity + if(activator) + target_spawn_useon(activator); + } + else + { + // edit entity + for(e = world; (e = find(e, targetname, self.target)); ) + target_spawn_useon(e); + } +} + +void target_spawn_spawnfirst() +{ + activator = self.target_spawn_activator; + if(self.spawnflags & 2) + target_spawn_use(); +} + +void initialize_field_db() +{ + if(!target_spawn_initialized) + { + float n, i; + string fn; + vector prev, new; + float ft; + + n = numentityfields(); + for(i = 0; i < n; ++i) + { + fn = entityfieldname(i); + ft = entityfieldtype(i); + new = i * '1 0 0' + ft * '0 1 0' + '0 0 1'; + prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn))); - if(prev_y == 0) ++ if(prev.y == 0) + { + db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(new)); + if(fn == "target_spawn_spawnfunc") + target_spawn_spawnfunc_field = i; + } + } + + target_spawn_initialized = 1; + } +} + +void spawnfunc_target_spawn() +{ + initialize_field_db(); + self.use = target_spawn_use; + self.message = strzone(strreplace("'", "\"", self.message)); + self.target_spawn_id = ++target_spawn_count; + InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST); +} +#endif diff --cc qcsrc/common/triggers/trigger/gravity.qc index a656afc65,000000000..a709a1f2a mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/gravity.qc +++ b/qcsrc/common/triggers/trigger/gravity.qc @@@ -1,106 -1,0 +1,106 @@@ +#ifdef SVQC +.entity trigger_gravity_check; +void trigger_gravity_remove(entity own) +{ + if(own.trigger_gravity_check.owner == own) + { + UpdateCSQCProjectile(own); + own.gravity = own.trigger_gravity_check.gravity; + remove(own.trigger_gravity_check); + } + else + backtrace("Removing a trigger_gravity_check with no valid owner"); + own.trigger_gravity_check = world; +} +void trigger_gravity_check_think() +{ + // This spawns when a player enters the gravity zone and checks if he left. + // Each frame, self.count is set to 2 by trigger_gravity_touch() and decreased by 1 here. + // It the player has left the gravity trigger, this will be allowed to reach 0 and indicate that. + if(self.count <= 0) + { + if(self.owner.trigger_gravity_check == self) + trigger_gravity_remove(self.owner); + else + remove(self); + return; + } + else + { + self.count -= 1; + self.nextthink = time; + } +} + +void trigger_gravity_use() +{ + self.state = !self.state; +} + +void trigger_gravity_touch() +{ + float g; + - if(self.state != TRUE) ++ if(self.state != true) + return; + + EXACTTRIGGER_TOUCH; + + g = self.gravity; + + if (!(self.spawnflags & 1)) + { + if(other.trigger_gravity_check) + { + if(self == other.trigger_gravity_check.enemy) + { + // same? + other.trigger_gravity_check.count = 2; // gravity one more frame... + return; + } + + // compare prio + if(self.cnt > other.trigger_gravity_check.enemy.cnt) + trigger_gravity_remove(other); + else + return; + } + other.trigger_gravity_check = spawn(); + other.trigger_gravity_check.enemy = self; + other.trigger_gravity_check.owner = other; + other.trigger_gravity_check.gravity = other.gravity; + other.trigger_gravity_check.think = trigger_gravity_check_think; + other.trigger_gravity_check.nextthink = time; + other.trigger_gravity_check.count = 2; + if(other.gravity) + g *= other.gravity; + } + + if (other.gravity != g) + { + other.gravity = g; + if(self.noise != "") + sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM); + UpdateCSQCProjectile(self.owner); + } +} + +void spawnfunc_trigger_gravity() +{ + if(self.gravity == 1) + return; + + EXACTTRIGGER_INIT; + self.touch = trigger_gravity_touch; + if(self.noise != "") + precache_sound(self.noise); + - self.state = TRUE; ++ self.state = true; + IFTARGETED + { + self.use = trigger_gravity_use; + if(self.spawnflags & 2) - self.state = FALSE; ++ self.state = false; + } +} +#endif diff --cc qcsrc/common/triggers/trigger/hurt.qc index fbf11a522,000000000..3a15a77ff mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/hurt.qc +++ b/qcsrc/common/triggers/trigger/hurt.qc @@@ -1,92 -1,0 +1,92 @@@ +#ifdef SVQC +void trigger_hurt_use() +{ + if(IS_PLAYER(activator)) + self.enemy = activator; + else + self.enemy = world; // let's just destroy it, if taking over is too much work +} + +.float triggerhurttime; +void trigger_hurt_touch() +{ + if (self.active != ACTIVE_ACTIVE) + return; + + if(self.team) + if(((self.spawnflags & 4) == 0) == (self.team != other.team)) + return; + + // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) + if (other.iscreature) + { + if (other.takedamage) + if (other.triggerhurttime < time) + { + EXACTTRIGGER_TOUCH; + other.triggerhurttime = time + 1; + + entity own; + own = self.enemy; + if (!IS_PLAYER(own)) + { + own = self; + self.enemy = world; // I still hate you all + } + + Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } + } + else if(other.damagedbytriggers) + { + if(other.takedamage) + { + EXACTTRIGGER_TOUCH; + Damage(other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } + } + + return; +} + +/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ? +Any object touching this will be hurt +set dmg to damage amount +defalt dmg = 5 +*/ +.entity trigger_hurt_next; +entity trigger_hurt_last; +entity trigger_hurt_first; +void spawnfunc_trigger_hurt() +{ + EXACTTRIGGER_INIT; + self.active = ACTIVE_ACTIVE; + self.touch = trigger_hurt_touch; + self.use = trigger_hurt_use; + self.enemy = world; // I hate you all + if (!self.dmg) + self.dmg = 1000; + if (self.message == "") + self.message = "was in the wrong place"; + if (self.message2 == "") + self.message2 = "was thrown into a world of hurt by"; + // self.message = "someone like %s always gets wrongplaced"; + + if(!trigger_hurt_first) + trigger_hurt_first = self; + if(trigger_hurt_last) + trigger_hurt_last.trigger_hurt_next = self; + trigger_hurt_last = self; +} + +float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end) +{ + entity th; + + for(th = trigger_hurt_first; th; th = th.trigger_hurt_next) + if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax)) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} +#endif diff --cc qcsrc/common/triggers/trigger/impulse.qc index dbdfaa0fa,000000000..d4368812f mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ b/qcsrc/common/triggers/trigger/impulse.qc @@@ -1,152 -1,0 +1,146 @@@ +#ifdef SVQC - // tZorks trigger impulse / gravity - .float radius; - .float falloff; - .float strength; - .float lastpushtime; - +// targeted (directional) mode +void trigger_impulse_touch1() +{ + entity targ; + float pushdeltatime; + float str; + + if (self.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(other)) + return; + + EXACTTRIGGER_TOUCH; + + targ = find(world, targetname, self.target); + if(!targ) + { + objerror("trigger_force without a (valid) .target!\n"); + remove(self); + return; + } + + str = min(self.radius, vlen(self.origin - other.origin)); + + if(self.falloff == 1) + str = (str / self.radius) * self.strength; + else if(self.falloff == 2) + str = (1 - (str / self.radius)) * self.strength; + else + str = self.strength; + + pushdeltatime = time - other.lastpushtime; + if (pushdeltatime > 0.15) pushdeltatime = 0; + other.lastpushtime = time; + if(!pushdeltatime) return; + + other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime; + other.flags &= ~FL_ONGROUND; + UpdateCSQCProjectile(other); +} + +// Directionless (accelerator/decelerator) mode +void trigger_impulse_touch2() +{ + float pushdeltatime; + + if (self.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(other)) + return; + + EXACTTRIGGER_TOUCH; + + pushdeltatime = time - other.lastpushtime; + if (pushdeltatime > 0.15) pushdeltatime = 0; + other.lastpushtime = time; + if(!pushdeltatime) return; + + // div0: ticrate independent, 1 = identity (not 20) + other.velocity = other.velocity * pow(self.strength, pushdeltatime); + UpdateCSQCProjectile(other); +} + +// Spherical (gravity/repulsor) mode +void trigger_impulse_touch3() +{ + float pushdeltatime; + float str; + + if (self.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(other)) + return; + + EXACTTRIGGER_TOUCH; + + pushdeltatime = time - other.lastpushtime; + if (pushdeltatime > 0.15) pushdeltatime = 0; + other.lastpushtime = time; + if(!pushdeltatime) return; + + setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius); + + str = min(self.radius, vlen(self.origin - other.origin)); + + if(self.falloff == 1) + str = (1 - str / self.radius) * self.strength; // 1 in the inside + else if(self.falloff == 2) + str = (str / self.radius) * self.strength; // 0 in the inside + else + str = self.strength; + + other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime; + UpdateCSQCProjectile(other); +} + +/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ? +-------- KEYS -------- +target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed. + If not, this trigger acts like a damper/accelerator field. + +strength : This is how mutch force to add in the direction of .target each second + when .target is set. If not, this is hoe mutch to slow down/accelerate + someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble) + +radius : If set, act as a spherical device rather then a liniar one. + +falloff : 0 = none, 1 = liniar, 2 = inverted liniar + +-------- NOTES -------- +Use a brush textured with common/origin in the trigger entity to determine the origin of the force +in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect). +*/ + +void spawnfunc_trigger_impulse() +{ + self.active = ACTIVE_ACTIVE; + + EXACTTRIGGER_INIT; + if(self.radius) + { + if(!self.strength) self.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier; + setorigin(self, self.origin); + setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius); + self.touch = trigger_impulse_touch3; + } + else + { + if(self.target) + { + if(!self.strength) self.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier; + self.touch = trigger_impulse_touch1; + } + else + { + if(!self.strength) self.strength = 0.9; + self.strength = pow(self.strength, autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier; + self.touch = trigger_impulse_touch2; + } + } +} +#endif diff --cc qcsrc/common/triggers/trigger/impulse.qh index 000000000,000000000..67d6361fb new file mode 100644 --- /dev/null +++ b/qcsrc/common/triggers/trigger/impulse.qh @@@ -1,0 -1,0 +1,10 @@@ ++#ifndef TRIGGER_IMPULSE_H ++#define TRIGGER_IMPULSE_H ++ ++// tZorks trigger impulse / gravity ++.float radius; ++.float falloff; ++.float strength; ++.float lastpushtime; ++ ++#endif diff --cc qcsrc/common/triggers/trigger/include.qc index 5a0e83f5a,000000000..a71de8990 mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/include.qc +++ b/qcsrc/common/triggers/trigger/include.qc @@@ -1,20 -1,0 +1,22 @@@ ++#include "include.qh" ++ +#include "counter.qc" +#include "delay.qc" +#include "disablerelay.qc" +#include "flipflop.qc" +#include "gamestart.qc" +#include "gravity.qc" +#include "heal.qc" +#include "hurt.qc" +#include "impulse.qc" +#include "jumppads.qc" +#include "magicear.qc" +#include "monoflop.qc" +#include "multi.qc" +#include "multivibrator.qc" +#include "relay.qc" +#include "relay_activators.qc" +#include "relay_if.qc" +#include "relay_teamcheck.qc" +#include "secret.qc" +#include "swamp.qc" diff --cc qcsrc/common/triggers/trigger/include.qh index 5ec31751c,000000000..6f4825b13 mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/include.qh +++ b/qcsrc/common/triggers/trigger/include.qh @@@ -1,4 -1,0 +1,9 @@@ ++#ifndef TRIGGERS_TRIGGER_INCLUDE_H ++#define TRIGGERS_TRIGGER_INCLUDE_H ++ +#include "multi.qh" +#include "jumppads.qh" +#include "secret.qh" +#include "swamp.qh" ++ ++#endif diff --cc qcsrc/common/triggers/trigger/jumppads.qc index 67fd6a91d,000000000..d60e4ff9a mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ b/qcsrc/common/triggers/trigger/jumppads.qc @@@ -1,496 -1,0 +1,491 @@@ +// TODO: split target_push and put it in the target folder - +#ifdef SVQC ++#include "jumppads.qh" + +void trigger_push_use() +{ + if(teamplay) + { + self.team = activator.team; + self.SendFlags |= 2; + } +} +#endif + - float trigger_push_calculatevelocity_flighttime; - +/* + trigger_push_calculatevelocity + + Arguments: + org - origin of the object which is to be pushed + tgt - target entity (can be either a point or a model entity; if it is + the latter, its midpoint is used) + ht - jump height, measured from the higher one of org and tgt's midpoint + + Returns: velocity for the jump + the global trigger_push_calculatevelocity_flighttime is set to the total + jump time + */ + +vector trigger_push_calculatevelocity(vector org, entity tgt, float ht) +{ + float grav, sdist, zdist, vs, vz, jumpheight; + vector sdir, torg; + + torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5; + + grav = PHYS_GRAVITY; + if(PHYS_ENTGRAVITY(other)) + grav *= PHYS_ENTGRAVITY(other); + - zdist = torg_z - org_z; ++ zdist = torg.z - org.z; + sdist = vlen(torg - org - zdist * '0 0 1'); + sdir = normalize(torg - org - zdist * '0 0 1'); + + // how high do we need to push the player? + jumpheight = fabs(ht); + if(zdist > 0) + jumpheight = jumpheight + zdist; + + /* + STOP. + + You will not understand the following equations anyway... + But here is what I did to get them. + + I used the functions + + s(t) = t * vs + z(t) = t * vz - 1/2 grav t^2 + + and solved for: + + s(ti) = sdist + z(ti) = zdist + max(z, ti) = jumpheight + + From these three equations, you will find the three parameters vs, vz + and ti. + */ + + // push him so high... + vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)! + + // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump! + if(ht < 0) + if(zdist < 0) + vz = -vz; + + vector solution; + solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist" + // ALWAYS solvable because jumpheight >= zdist - if(!solution_z) - solution_y = solution_x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0) ++ if(!solution.z) ++ solution.y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0) + if(zdist == 0) - solution_x = solution_y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually) ++ solution.x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually) + + if(zdist < 0) + { + // down-jump + if(ht < 0) + { + // almost straight line type + // jump apex is before the jump + // we must take the larger one - trigger_push_calculatevelocity_flighttime = solution_y; ++ trigger_push_calculatevelocity_flighttime = solution.y; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one too - trigger_push_calculatevelocity_flighttime = solution_y; ++ trigger_push_calculatevelocity_flighttime = solution.y; + } + } + else + { + // up-jump + if(ht < 0) + { + // almost straight line type + // jump apex is after the jump + // we must take the smaller one - trigger_push_calculatevelocity_flighttime = solution_x; ++ trigger_push_calculatevelocity_flighttime = solution.x; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one - trigger_push_calculatevelocity_flighttime = solution_y; ++ trigger_push_calculatevelocity_flighttime = solution.y; + } + } + vs = sdist / trigger_push_calculatevelocity_flighttime; + + // finally calculate the velocity + return sdir * vs + '0 0 1' * vz; +} + +void trigger_push_touch() +{ + if (self.active == ACTIVE_NOT) + return; + +#ifdef SVQC + if (!isPushable(other)) + return; +#endif + + if(self.team) + if(((self.spawnflags & 4) == 0) == (DIFF_TEAM(self, other))) + return; + + EXACTTRIGGER_TOUCH; + + if(self.enemy) + { + other.velocity = trigger_push_calculatevelocity(other.origin, self.enemy, self.height); + } + else if(self.target) + { + entity e; + RandomSelection_Init(); + for(e = world; (e = find(e, targetname, self.target)); ) + { + if(e.cnt) + RandomSelection_Add(e, 0, string_null, e.cnt, 1); + else + RandomSelection_Add(e, 0, string_null, 1, 1); + } + other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, self.height); + } + else + { + other.velocity = self.movedir; + } + + UNSET_ONGROUND(other); + +#ifdef SVQC + if (IS_PLAYER(other)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + other.oldvelocity = other.velocity; + + if(self.pushltime < time) // prevent "snorring" sound when a player hits the jumppad more than once + { + // flash when activated + pointparticles(particleeffectnum("jumppad_activate"), other.origin, other.velocity, 1); + sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM); + self.pushltime = time + 0.2; + } - + if(IS_REAL_CLIENT(other) || IS_BOT_CLIENT(other)) + { - float i; - float found; - found = FALSE; - for(i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i) ++ bool found = false; ++ for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i) + if(other.(jumppadsused[i]) == self) - found = TRUE; ++ found = true; + if(!found) + { + other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = self; + other.jumppadcount = other.jumppadcount + 1; + } + + if(IS_REAL_CLIENT(other)) + { + if(self.message) + centerprint(other, self.message); + } + else + other.lastteleporttime = time; + + if (other.deadflag == DEAD_NO) - animdecide_setaction(other, ANIMACTION_JUMP, TRUE); ++ animdecide_setaction(other, ANIMACTION_JUMP, true); + } + else - other.jumppadcount = TRUE; ++ other.jumppadcount = true; + + // reset tracking of who pushed you into a hazard (for kill credit) + other.pushltime = 0; + other.istypefrag = 0; + } + + if(self.enemy.target) + { + entity oldself; + oldself = self; + activator = other; + self = self.enemy; + SUB_UseTargets(); + self = oldself; + } + + if (other.flags & FL_PROJECTILE) + { + other.angles = vectoangles (other.velocity); + switch(other.movetype) + { + case MOVETYPE_FLY: + other.movetype = MOVETYPE_TOSS; + other.gravity = 1; + break; + case MOVETYPE_BOUNCEMISSILE: + other.movetype = MOVETYPE_BOUNCE; + other.gravity = 1; + break; + } + UpdateCSQCProjectile(other); + } + + if (self.spawnflags & PUSH_ONCE) + { + self.touch = func_null; + self.think = SUB_Remove; + self.nextthink = time; + } +#endif +} + +#ifdef SVQC +void trigger_push_link(); +void trigger_push_updatelink(); +#endif +void trigger_push_findtarget() +{ + entity t; + vector org; + + // first calculate a typical start point for the jump + org = (self.absmin + self.absmax) * 0.5; - org_z = self.absmax_z - PL_MIN_z; ++ org.z = self.absmax.z - PL_MIN_z; + + if (self.target) + { + float n = 0; + for(t = world; (t = find(t, targetname, self.target)); ) + { + ++n; +#ifdef SVQC + entity e = spawn(); + setorigin(e, org); + setsize(e, PL_MIN, PL_MAX); + e.velocity = trigger_push_calculatevelocity(org, t, self.height); + tracetoss(e, e); + if(e.movetype == MOVETYPE_NONE) + waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity)); + remove(e); +#endif + } + + if(!n) + { + // no dest! +#ifdef SVQC + objerror ("Jumppad with nonexistant target"); +#endif + return; + } + else if(n == 1) + { + // exactly one dest - bots love that + self.enemy = find(world, targetname, self.target); + } + else + { + // have to use random selection every single time + self.enemy = world; + } + } +#ifdef SVQC + else + { + entity e = spawn(); + setorigin(e, org); + setsize(e, PL_MIN, PL_MAX); + e.velocity = self.movedir; + tracetoss(e, e); + waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity)); + remove(e); + } + + trigger_push_link(); + defer(0.1, trigger_push_updatelink); +#endif +} + +#ifdef SVQC +float trigger_push_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); + WriteByte(MSG_ENTITY, sf); + + if(sf & 1) + { + WriteString(MSG_ENTITY, self.target); + WriteByte(MSG_ENTITY, self.team); + WriteInt24_t(MSG_ENTITY, self.spawnflags); + WriteByte(MSG_ENTITY, self.active); + WriteByte(MSG_ENTITY, self.warpzone_isboxy); + WriteByte(MSG_ENTITY, self.height); + WriteByte(MSG_ENTITY, self.scale); + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + + WriteCoord(MSG_ENTITY, self.movedir_x); + WriteCoord(MSG_ENTITY, self.movedir_y); + WriteCoord(MSG_ENTITY, self.movedir_z); + + WriteCoord(MSG_ENTITY, self.angles_x); + WriteCoord(MSG_ENTITY, self.angles_y); + WriteCoord(MSG_ENTITY, self.angles_z); + } + + if(sf & 2) + { + WriteByte(MSG_ENTITY, self.team); + WriteByte(MSG_ENTITY, self.active); + } + - return TRUE; ++ return true; +} + +void trigger_push_updatelink() +{ + self.SendFlags |= 1; +} + +void trigger_push_link() +{ - Net_LinkEntity(self, FALSE, 0, trigger_push_send); ++ Net_LinkEntity(self, false, 0, trigger_push_send); +} +#endif +#ifdef SVQC +/* + * ENTITY PARAMETERS: + * + * target: target of jump + * height: the absolute value is the height of the highest point of the jump + * trajectory above the higher one of the player and the target. + * the sign indicates whether the highest point is INSIDE (positive) + * or OUTSIDE (negative) of the jump trajectory. General rule: use + * positive values for targets mounted on the floor, and use negative + * values to target a point on the ceiling. + * movedir: if target is not set, this * speed * 10 is the velocity to be reached. + */ +void spawnfunc_trigger_push() +{ + SetMovedir (); + + EXACTTRIGGER_INIT; + + self.active = ACTIVE_ACTIVE; + self.use = trigger_push_use; + self.touch = trigger_push_touch; + + // normal push setup + if (!self.speed) + self.speed = 1000; + self.movedir = self.movedir * self.speed * 10; + + if (!self.noise) + self.noise = "misc/jumppad.wav"; + precache_sound (self.noise); + + // this must be called to spawn the teleport waypoints for bots + InitializeEntity(self, trigger_push_findtarget, INITPRIO_FINDTARGET); +} + + +float target_push_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); + + WriteByte(MSG_ENTITY, self.cnt); + WriteString(MSG_ENTITY, self.targetname); + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + - return TRUE; ++ return true; +} + +void target_push_link() +{ - Net_LinkEntity(self, FALSE, 0, target_push_send); ++ Net_LinkEntity(self, false, 0, target_push_send); + self.SendFlags |= 1; // update +} + +void spawnfunc_target_push() { target_push_link(); } +void spawnfunc_info_notnull() { target_push_link(); } +void spawnfunc_target_position() { target_push_link(); } + +#endif + +#ifdef CSQC +void ent_trigger_push() +{ + float sf = ReadByte(); + + if(sf & 1) + { + self.classname = "jumppad"; + self.target = strzone(ReadString()); + float mytm = ReadByte(); if(mytm) { self.team = mytm - 1; } + self.spawnflags = ReadInt24_t(); + self.active = ReadByte(); + self.warpzone_isboxy = ReadByte(); + self.height = ReadByte(); + self.scale = ReadByte(); + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + self.movedir_x = ReadCoord(); + self.movedir_y = ReadCoord(); + self.movedir_z = ReadCoord(); + self.angles_x = ReadCoord(); + self.angles_y = ReadCoord(); + self.angles_z = ReadCoord(); + + self.solid = SOLID_TRIGGER; + self.draw = trigger_draw_generic; + self.trigger_touch = trigger_push_touch; + self.drawmask = MASK_ENGINE; + self.move_time = time; + trigger_push_findtarget(); + } + + if(sf & 2) + { + self.team = ReadByte(); + self.active = ReadByte(); + } +} + +void ent_target_push() +{ + self.classname = "push_target"; + self.cnt = ReadByte(); + self.targetname = strzone(ReadString()); + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.drawmask = MASK_ENGINE; +} +#endif diff --cc qcsrc/common/triggers/trigger/jumppads.qh index 6330dc3c4,000000000..bb0f4750e mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/jumppads.qh +++ b/qcsrc/common/triggers/trigger/jumppads.qh @@@ -1,12 -1,0 +1,68 @@@ ++#ifndef T_JUMPPADS_H ++#define T_JUMPPADS_H ++ ++const float PUSH_ONCE = 1; ++const float PUSH_SILENT = 2; ++ ++.float pushltime; ++.float istypefrag; ++.float height; ++ ++const int NUM_JUMPPADSUSED = 3; ++.float jumppadcount; ++.entity jumppadsused[NUM_JUMPPADSUSED]; ++ ++float trigger_push_calculatevelocity_flighttime; ++ ++#ifdef SVQC ++void() SUB_UseTargets; ++void trigger_push_use(); ++#endif ++ +#ifdef CSQC +void ent_trigger_push(); ++ +void ent_target_push(); +#endif + - const float PUSH_ONCE = 1; - const float PUSH_SILENT = 2; ++/* ++ trigger_push_calculatevelocity + - .float pushltime; - .float istypefrag; ++ Arguments: ++ org - origin of the object which is to be pushed ++ tgt - target entity (can be either a point or a model entity; if it is ++ the latter, its midpoint is used) ++ ht - jump height, measured from the higher one of org and tgt's midpoint + - void() SUB_UseTargets; ++ Returns: velocity for the jump ++ the global trigger_push_calculatevelocity_flighttime is set to the total ++ jump time ++ */ ++ ++vector trigger_push_calculatevelocity(vector org, entity tgt, float ht); ++ ++void trigger_push_touch(); ++ ++.vector dest; ++void trigger_push_findtarget(); ++ ++/* ++ * ENTITY PARAMETERS: ++ * ++ * target: target of jump ++ * height: the absolute value is the height of the highest point of the jump ++ * trajectory above the higher one of the player and the target. ++ * the sign indicates whether the highest point is INSIDE (positive) ++ * or OUTSIDE (negative) of the jump trajectory. General rule: use ++ * positive values for targets mounted on the floor, and use negative ++ * values to target a point on the ceiling. ++ * movedir: if target is not set, this * speed * 10 is the velocity to be reached. ++ */ ++#ifdef SVQC ++void spawnfunc_trigger_push(); ++ ++void spawnfunc_target_push(); ++void spawnfunc_info_notnull(); ++void spawnfunc_target_position(); ++#endif ++#endif diff --cc qcsrc/common/triggers/trigger/magicear.qc index 1034d5d68,000000000..f14b75c31 mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/magicear.qc +++ b/qcsrc/common/triggers/trigger/magicear.qc @@@ -1,204 -1,0 +1,204 @@@ +#ifdef SVQC +float magicear_matched; +float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo); +string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin) +{ + float domatch, dotrigger, matchstart, l; + string s, msg; + entity oldself; + string savemessage; + - magicear_matched = FALSE; ++ magicear_matched = false; + + dotrigger = ((IS_PLAYER(source)) && (source.deadflag == DEAD_NO) && ((ear.radius == 0) || (vlen(source.origin - ear.origin) <= ear.radius))); + domatch = ((ear.spawnflags & 32) || dotrigger); + + if (!domatch) + return msgin; + + if (!msgin) + { + // we are in TUBA mode! + if (!(ear.spawnflags & 256)) + return msgin; + + if(!W_Tuba_HasPlayed(source, ear.message, ear.movedir_x, !(ear.spawnflags & 512), ear.movedir_y, ear.movedir_z)) + return msgin; + - magicear_matched = TRUE; ++ magicear_matched = true; + + if(dotrigger) + { + oldself = self; + activator = source; + self = ear; + savemessage = self.message; + self.message = string_null; + SUB_UseTargets(); + self.message = savemessage; + self = oldself; + } + + if(ear.netname != "") + return ear.netname; + + return msgin; + } + + if(ear.spawnflags & 256) // ENOTUBA + return msgin; + + if(privatesay) + { + if(ear.spawnflags & 4) + return msgin; + } + else + { + if(!teamsay) + if(ear.spawnflags & 1) + return msgin; + if(teamsay > 0) + if(ear.spawnflags & 2) + return msgin; + if(teamsay < 0) + if(ear.spawnflags & 8) + return msgin; + } + + matchstart = -1; + l = strlen(ear.message); + + if(ear.spawnflags & 128) + msg = msgin; + else + msg = strdecolorize(msgin); + + if(substring(ear.message, 0, 1) == "*") + { + if(substring(ear.message, -1, 1) == "*") + { + // two wildcards + // as we need multi-replacement here... + s = substring(ear.message, 1, -2); + l -= 2; + if(strstrofs(msg, s, 0) >= 0) + matchstart = -2; // we use strreplace on s + } + else + { + // match at start + s = substring(ear.message, 1, -1); + l -= 1; + if(substring(msg, -l, l) == s) + matchstart = strlen(msg) - l; + } + } + else + { + if(substring(ear.message, -1, 1) == "*") + { + // match at end + s = substring(ear.message, 0, -2); + l -= 1; + if(substring(msg, 0, l) == s) + matchstart = 0; + } + else + { + // full match + s = ear.message; + if(msg == ear.message) + matchstart = 0; + } + } + + if(matchstart == -1) // no match + return msgin; + - magicear_matched = TRUE; ++ magicear_matched = true; + + if(dotrigger) + { + oldself = self; + activator = source; + self = ear; + savemessage = self.message; + self.message = string_null; + SUB_UseTargets(); + self.message = savemessage; + self = oldself; + } + + if(ear.spawnflags & 16) + { + return ear.netname; + } + else if(ear.netname != "") + { + if(matchstart < 0) + return strreplace(s, ear.netname, msg); + else + return strcat( + substring(msg, 0, matchstart), + ear.netname, + substring(msg, matchstart + l, -1) + ); + } + else + return msgin; +} + +entity magicears; +string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin) +{ + entity ear; + string msgout; + for(ear = magicears; ear; ear = ear.enemy) + { + msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin); + if(!(ear.spawnflags & 64)) + if(magicear_matched) + return msgout; + msgin = msgout; + } + return msgin; +} + +void spawnfunc_trigger_magicear() +{ + self.enemy = magicears; + magicears = self; + + // actually handled in "say" processing + // spawnflags: + // 1 = ignore say + // 2 = ignore teamsay + // 4 = ignore tell + // 8 = ignore tell to unknown player + // 16 = let netname replace the whole message (otherwise, netname is a word replacement if set) + // 32 = perform the replacement even if outside the radius or dead + // 64 = continue replacing/triggering even if this one matched + // 128 = don't decolorize message before matching + // 256 = message is a tuba note sequence (pitch.duration pitch.duration ...) + // 512 = tuba notes must be exact right pitch, no transposing + // message: either + // *pattern* + // or + // *pattern + // or + // pattern* + // or + // pattern + // netname: + // if set, replacement for the matched text + // radius: + // "hearing distance" + // target: + // what to trigger + // movedir: + // for spawnflags 256, defines 'instrument+1 mintempo maxtempo' (zero component doesn't matter) + + self.movedir_x -= 1; // map to tuba instrument numbers +} +#endif diff --cc qcsrc/common/triggers/trigger/secret.qc index 48dc360af,000000000..267c69a8c mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/secret.qc +++ b/qcsrc/common/triggers/trigger/secret.qc @@@ -1,75 -1,0 +1,85 @@@ ++#if defined(CSQC) ++#elif defined(MENUQC) ++#elif defined(SVQC) ++ #include "../../..//dpdefs/progsdefs.qh" ++ #include "../../util.qh" ++ #include "../../../server/defs.qh" ++ #include "secret.qh" ++#endif ++ +#ifdef SVQC ++ +void secrets_setstatus() { + self.stat_secrets_total = secrets_total; + self.stat_secrets_found = secrets_found; +} + +/** + * A secret has been found (maybe :P) + */ +void trigger_secret_touch() { + // only a player can trigger this + if (!IS_PLAYER(other)) + return; + + // update secrets found counter + secrets_found += 1; + //print("Secret found: ", ftos(secret_counter.cnt), "/"); + //print(ftos(secret_counter.count), "\n"); + + // centerprint message (multi_touch() doesn't always call centerprint()) + centerprint(other, self.message); + self.message = ""; + + // handle normal trigger features + multi_touch(); + remove(self); +} + +/*QUAKED trigger_secret (.5 .5 .5) ? +Variable sized secret trigger. Can be targeted at one or more entities. +Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found. +-------- KEYS -------- +sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1) +noise: path to sound file, if you want to play something else +target: trigger all entities with this targetname when triggered +message: print this message to the player who activated the trigger instead of the standard 'You found a secret!' +killtarget: remove all entities with this targetname when triggered +-------- NOTES -------- +You should create a common/trigger textured brush covering the entrance to a secret room/area. +Trigger secret can only be trigger by a player's touch and can not be a target itself. +*/ +void spawnfunc_trigger_secret() { + // FIXME: should it be disabled in most modes? + + // update secrets count + secrets_total += 1; + + // add default message + if (self.message == "") + self.message = "You found a secret!"; + + // set default sound + if (self.noise == "") + if (!self.sounds) + self.sounds = 1; // misc/secret.wav + + // this entity can't be a target itself!!!! + self.targetname = ""; + + // you can't just shoot a room to find it, can you? + self.health = 0; + + // a secret can not be delayed + self.delay = 0; + + // convert this trigger to trigger_once + self.classname = "trigger_once"; + spawnfunc_trigger_once(); + + // take over the touch() function, so we can mark secret as found + self.touch = trigger_secret_touch; + // ignore triggering; + self.use = func_null; +} +#endif diff --cc qcsrc/common/triggers/trigger/secret.qh index e289f57ec,000000000..c09da6b96 mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/secret.qh +++ b/qcsrc/common/triggers/trigger/secret.qh @@@ -1,20 -1,0 +1,24 @@@ ++#ifndef SECRET_H ++#define SECRET_H +#ifdef SVQC ++ +/** + * Total number of secrets on the map. + */ +float secrets_total; + +/** + * Total numbe of secrets found on the map. + */ +float secrets_found; + + +.float stat_secrets_total; +.float stat_secrets_found; + +/** + * update secrets status. + */ +void secrets_setstatus(); +#endif ++#endif diff --cc qcsrc/common/triggers/trigger/swamp.qc index a13aa6742,000000000..474eb2467 mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/swamp.qc +++ b/qcsrc/common/triggers/trigger/swamp.qc @@@ -1,170 -1,0 +1,192 @@@ ++#if defined(CSQC) ++#elif defined(MENUQC) ++#elif defined(SVQC) ++ #include "../../../dpdefs/progsdefs.qh" ++ #include "../../../warpzonelib/util_server.qh" ++ #include "../../weapons/weapons.qh" ++ #include "../../../server/defs.qh" ++ #include "../../deathtypes.qh" ++#endif ++ ++/* ++* t_swamp.c ++* Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+ ++* Author tZork (Jakob MG) ++* jakob@games43.se ++* 2005 11 29 ++*/ ++ ++.float swamp_interval; //Hurt players in swamp with this interval ++.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) ++.entity swampslug; ++ +#ifdef SVQC +void spawnfunc_trigger_swamp(void); +#endif +void swamp_touch(void); +void swampslug_think(); + + +/* +* Uses a entity calld swampslug to handle players in the swamp +* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp +* attaches a new "swampslug" to the player. As long as the plyer is inside +* the swamp the swamp gives the slug new health. But the slug slowly kills itself +* so when the player goes outside the swamp, it dies and releases the player from the +* swamps curses (dmg/slowdown) +* +* I do it this way becuz there is no "untouch" event. +*/ +void swampslug_think(void) +{ + //Slowly kill the slug + self.health = self.health - 1; + + //Slug dead? then remove curses. + if(self.health <= 0) + { + self.owner.in_swamp = 0; + remove(self); + //centerprint(self.owner,"Killing slug...\n"); + return; + } + + // Slug still alive, so we are still in the swamp + // Or we have exited it very recently. + // Do the damage and renew the timer. +#ifdef SVQC + Damage (self.owner, self, self, self.dmg, DEATH_SWAMP, other.origin, '0 0 0'); +#endif + + self.nextthink = time + self.swamp_interval; +} + +void swamp_touch(void) +{ + // If whatever thats touching the swamp is not a player + // or if its a dead player, just dont care abt it. + if(!IS_PLAYER(other) || PHYS_DEAD(other)) + return; + + EXACTTRIGGER_TOUCH; + + // Chech if player alredy got a swampslug. + if(other.in_swamp != 1) + { + // If not attach one. + //centerprint(other,"Entering swamp!\n"); + other.swampslug = spawn(); + other.swampslug.health = 2; + other.swampslug.think = swampslug_think; + other.swampslug.nextthink = time; + other.swampslug.owner = other; + other.swampslug.dmg = self.dmg; + other.swampslug.swamp_interval = self.swamp_interval; + other.swamp_slowdown = self.swamp_slowdown; + other.in_swamp = 1; + return; + } + + //other.in_swamp = 1; + + //Revitalize players swampslug + other.swampslug.health = 2; +} + +#ifdef SVQC +float swamp_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER); + + WriteByte(MSG_ENTITY, self.warpzone_isboxy); + WriteByte(MSG_ENTITY, self.scale); + WriteByte(MSG_ENTITY, self.dmg); // can probably get away with using a single byte here + WriteByte(MSG_ENTITY, self.swamp_slowdown); + WriteByte(MSG_ENTITY, self.swamp_interval); + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + + WriteCoord(MSG_ENTITY, self.movedir_x); + WriteCoord(MSG_ENTITY, self.movedir_y); + WriteCoord(MSG_ENTITY, self.movedir_z); + + WriteAngle(MSG_ENTITY, self.angles_x); + WriteAngle(MSG_ENTITY, self.angles_y); + WriteAngle(MSG_ENTITY, self.angles_z); + - return TRUE; ++ return true; +} + +void swamp_link() +{ - Net_LinkEntity(self, FALSE, 0, func_ladder_send); ++ Net_LinkEntity(self, false, 0, func_ladder_send); +} + +/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ? +Players gettin into the swamp will +get slowd down and damaged +*/ +void spawnfunc_trigger_swamp(void) +{ + // Init stuff + EXACTTRIGGER_INIT; + self.touch = swamp_touch; + + // Setup default keys, if missing + if(self.dmg <= 0) + self.dmg = 5; + if(self.swamp_interval <= 0) + self.swamp_interval = 1; + if(self.swamp_slowdown <= 0) + self.swamp_slowdown = 0.5; + + swamp_link(); +} + +#elif defined(CSQC) + +void ent_swamp() +{ + self.warpzone_isboxy = ReadByte(); + self.scale = ReadByte(); + self.dmg = ReadByte(); + self.swamp_slowdown = ReadByte(); + self.swamp_interval = ReadByte(); + + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + + self.movedir_x = ReadCoord(); + self.movedir_y = ReadCoord(); + self.movedir_z = ReadCoord(); + + self.angles_x = ReadAngle(); + self.angles_y = ReadAngle(); + self.angles_z = ReadAngle(); + + self.classname = "trigger_swamp"; + self.solid = SOLID_TRIGGER; + self.draw = trigger_draw_generic; + self.trigger_touch = swamp_touch; + self.drawmask = MASK_NORMAL; + self.move_time = time; +} +#endif diff --cc qcsrc/common/triggers/trigger/swamp.qh index c58ac5f62,000000000..86b14315b mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/swamp.qh +++ b/qcsrc/common/triggers/trigger/swamp.qh @@@ -1,10 -1,0 +1,15 @@@ ++#ifndef TRIGGER_SWAMP_H ++#define TRIGGER_SWAMP_H ++ +.float swamp_interval; //Hurt players in swamp with this interval +.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) +.entity swampslug; + +.float in_swamp; // bool +.entity swampslug; // Uses this to release from swamp ("untouch" fix) + +#ifdef CSQC +void ent_swamp(); +#endif ++ ++#endif diff --cc qcsrc/common/triggers/triggers.qh index 68eaf1a47,000000000..d1a5ea3bb mode 100644,000000..100644 --- a/qcsrc/common/triggers/triggers.qh +++ b/qcsrc/common/triggers/triggers.qh @@@ -1,43 -1,0 +1,54 @@@ ++#ifndef TRIGGERS_H ++#define TRIGGERS_H ++ +const float SF_TRIGGER_INIT = 1; +const float SF_TRIGGER_UPDATE = 2; +const float SF_TRIGGER_RESET = 4; + +const float SPAWNFLAG_NOMESSAGE = 1; +const float SPAWNFLAG_NOTOUCH = 1; + +.void() trigger_touch; + +.float height; + +.float nottargeted; +#define IFTARGETED if(!self.nottargeted && self.targetname != "") + +.string bgmscript; +.float bgmscriptattack; +.float bgmscriptdecay; +.float bgmscriptsustain; +.float bgmscriptrelease; + ++.float lip; ++ +// used elsewhere (will fix) +#ifdef SVQC +void spawnfunc_trigger_once(); +string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin); + +void target_voicescript_next(entity pl); +void target_voicescript_clear(entity pl); +#endif + +.float volume, atten; + +.vector dest; + +#ifdef CSQC ++float WarpZoneLib_ExactTrigger_Touch(); ++#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return ++ +.float active; +.string target; +.string targetname; - #define ACTIVE_NOT 0 - #define ACTIVE_ACTIVE 1 - #define ACTIVE_IDLE 2 - #define ACTIVE_BUSY 2 - #define ACTIVE_TOGGLE 3 ++ ++const int ACTIVE_NOT = 0; ++const int ACTIVE_ACTIVE = 1; ++const int ACTIVE_IDLE = 2; ++const int ACTIVE_BUSY = 2; ++const int ACTIVE_TOGGLE = 3; ++#endif ++ +#endif diff --cc qcsrc/common/weapons/w_porto.qc index e1fb82f8f,0010449a0..569a15d97 --- a/qcsrc/common/weapons/w_porto.qc +++ b/qcsrc/common/weapons/w_porto.qc @@@ -38,6 -38,6 +38,8 @@@ PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PR #endif #else #ifdef SVQC ++#include "../triggers/trigger/jumppads.qh" ++ void spawnfunc_weapon_porto(void) { weapon_defaultspawnfunc(WEP_PORTO); } void W_Porto_Success(void) diff --cc qcsrc/server/bot/havocbot/havocbot.qc index e58e67097,0ecafaf1e..7ba2e1612 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@@ -2,6 -2,6 +2,7 @@@ #include "role_onslaught.qc" #include "role_keyhunt.qc" #include "roles.qc" ++#include "../../../common/triggers/trigger/jumppads.qh" void havocbot_ai() { diff --cc qcsrc/server/cheats.qc index c1a637514,657ec4d7f..3119182a6 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@@ -1,3 -1,27 +1,28 @@@ + #include "cheats.qh" + #include "g_damage.qh" + #include "race.qh" + #include "t_teleporters.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/util_server.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/monsters/monsters.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/tracing.qh" + #include "autocvars.qh" + #include "defs.qh" + #include "../common/deathtypes.qh" ++ #include "../common/triggers/subs.qh" + #include "mutators/mutators_include.qh" + #include "../csqcmodellib/sv_model.qh" + #endif + void CopyBody(float keepvelocity); #ifdef NOCHEATS diff --cc qcsrc/server/cl_client.qc index 37a630af8,53ec96503..5453f5199 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@@ -1,3 -1,31 +1,34 @@@ + #include "waypointsprites.qh" + + #include "cl_impulse.qh" + #include "cl_player.qh" + #include "ent_cs.qh" + #include "g_subs.qh" + #include "ipban.qh" + #include "miscfunctions.qh" + #include "portals.qh" + #include "teamplay.qh" + #include "playerdemo.qh" -#include "secret.qh" + + #include "bot/bot.qh" + #include "bot/navigation.qh" + + #include "weapons/hitplot.qh" + #include "weapons/weaponsystem.qh" + + #include "../common/net_notice.qh" + #include "../common/physics.qh" + ++#include "../common/triggers/subs.qh" ++#include "../common/triggers/triggers.qh" ++#include "../common/triggers/trigger/secret.qh" ++ + #include "../common/monsters/sv_monsters.qh" + + #include "../warpzonelib/server.qh" + + float c1, c2, c3, c4; + void send_CSQC_teamnagger() { WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); diff --cc qcsrc/server/cl_player.qc index d899b3db3,b2e680e83..49ae14a6a --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@@ -1,9 -1,12 +1,11 @@@ - .entity pusher; - .float pushltime; - .float istypefrag; + #include "cl_player.qh" -#include "g_triggers.qh" + #include "g_violence.qh" + #include "miscfunctions.qh" + + #include "weapons/weaponstats.qh" + + #include "../common/animdecide.qh" - .float CopyBody_nextthink; - .void(void) CopyBody_think; void CopyBody_Think(void) { if(self.CopyBody_nextthink && time > self.CopyBody_nextthink) diff --cc qcsrc/server/constants.qh index e9d944c44,1d4cc5523..c1def6b82 --- a/qcsrc/server/constants.qh +++ b/qcsrc/server/constants.qh @@@ -14,8 -17,11 +17,10 @@@ const int RESPAWN_SILENT = 2 #define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT) - const float MSG_ENTITY = 5; // csqc + const int MSG_ENTITY = 5; // csqc + + const int NUM_PLAYERSKINS_TEAMPLAY = 3; - const float NUM_PLAYERSKINS_TEAMPLAY = 3; + const int ASSAULT_VALUE_INACTIVE = 1000; - const float ASSAULT_VALUE_INACTIVE = 1000; -const int DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag + #endif diff --cc qcsrc/server/defs.qh index c4b5574d3,fc85ed9d5..f27f50340 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@@ -233,10 -284,10 +238,6 @@@ float default_weapon_alpha .float version_nagtime; - #define NUM_JUMPPADSUSED 3 -const int NUM_JUMPPADSUSED = 3; --.float jumppadcount; --.entity jumppadsused[NUM_JUMPPADSUSED]; -- string gamemode_name; float startitem_failed; diff --cc qcsrc/server/g_hook.qc index eab482618,310833e43..05032c2d0 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@@ -1,3 -1,22 +1,23 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/common.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "vehicles/vehicles_def.qh" + #include "command/common.qh" + #include "g_hook.qh" + #include "round_handler.qh" ++ #include "weapons/common.qh" + #endif + /*============================================ Wazat's Xonotic Grappling Hook diff --cc qcsrc/server/g_models.qc index 8a2fe9ef3,6e1c06d05..95b83f3d7 --- a/qcsrc/server/g_models.qc +++ b/qcsrc/server/g_models.qc @@@ -1,3 -1,15 +1,16 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../common/constants.qh" ++ #include "../common/triggers/subs.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../csqcmodellib/sv_model.qh" + #endif + .float modelscale; void g_model_setcolormaptoactivator (void) diff --cc qcsrc/server/g_subs.qc index 871a20bbd,74a8cf7f9..d42cc6750 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@@ -1,3 -1,7 +1,5 @@@ + #include "g_subs.qh" + -void SUB_NullThink(void) { } - void spawnfunc_info_null (void) { remove(self); @@@ -48,7 -52,380 +50,6 @@@ void updateanim(entity e //print(ftos(time), " -> ", ftos(e.frame), "\n"); } -/* -================== -SUB_Remove - -Remove self -================== -*/ -void SUB_Remove (void) -{ - remove (self); -} - -/* -================== -SUB_Friction - -Applies some friction to self -================== -*/ -void SUB_Friction (void) -{ - self.nextthink = time; - if(self.flags & FL_ONGROUND) - self.velocity = self.velocity * (1 - frametime * self.friction); -} - -/* -================== -SUB_VanishOrRemove - -Makes client invisible or removes non-client -================== -*/ -void SUB_VanishOrRemove (entity ent) -{ - if (IS_CLIENT(ent)) - { - // vanish - ent.alpha = -1; - ent.effects = 0; - ent.glow_size = 0; - ent.pflags = 0; - } - else - { - // remove - remove (ent); - } -} - -void SUB_SetFade_Think (void) -{ - if(self.alpha == 0) - self.alpha = 1; - self.think = SUB_SetFade_Think; - self.nextthink = time; - self.alpha -= frametime * self.fade_rate; - if (self.alpha < 0.01) - SUB_VanishOrRemove(self); - else - self.nextthink = time; -} - -/* -================== -SUB_SetFade - -Fade 'ent' out when time >= 'when' -================== -*/ -void SUB_SetFade (entity ent, float when, float fadetime) -{ - ent.fade_rate = 1/fadetime; - ent.think = SUB_SetFade_Think; - ent.nextthink = when; -} - -/* -============= -SUB_CalcMove - -calculate self.velocity and self.nextthink to reach dest from -self.origin traveling at speed -=============== -*/ -void SUB_CalcMoveDone (void) -{ - // After moving, set origin to exact final destination - - setorigin (self, self.finaldest); - self.velocity = '0 0 0'; - self.nextthink = -1; - if (self.think1) - self.think1 (); -} - -void SUB_CalcMove_controller_think (void) -{ - entity oldself; - float traveltime; - float phasepos; - float nexttick; - vector delta; - vector delta2; - vector veloc; - vector angloc; - vector nextpos; - delta = self.destvec; - delta2 = self.destvec2; - if(time < self.animstate_endtime) { - nexttick = time + sys_frametime; - - traveltime = self.animstate_endtime - self.animstate_starttime; - phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1] - phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos); - nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos); - // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning) - - if(self.owner.platmovetype_turn) - { - vector destangle; - destangle = delta + 2 * delta2 * phasepos; - destangle = vectoangles(destangle); - destangle.x = -destangle.x; // flip up / down orientation - - // take the shortest distance for the angles - self.owner.angles_x -= 360 * floor((self.owner.angles.x - destangle.x) / 360 + 0.5); - self.owner.angles_y -= 360 * floor((self.owner.angles.y - destangle.y) / 360 + 0.5); - self.owner.angles_z -= 360 * floor((self.owner.angles.z - destangle.z) / 360 + 0.5); - angloc = destangle - self.owner.angles; - angloc = angloc * (1 / sys_frametime); // so it arrives for the next frame - self.owner.avelocity = angloc; - } - if(nexttick < self.animstate_endtime) - veloc = nextpos - self.owner.origin; - else - veloc = self.finaldest - self.owner.origin; - veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame - - self.owner.velocity = veloc; - self.nextthink = nexttick; - } else { - // derivative: delta + 2 * delta2 (e.g. for angle positioning) - oldself = self; - self.owner.think = self.think1; - self = self.owner; - remove(oldself); - self.think(); - } -} - -void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest) -{ - // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t - // 2 * control * t - 2 * control * t * t + dest * t * t - // 2 * control * t + (dest - 2 * control) * t * t - - controller.origin = org; // starting point - control -= org; - dest -= org; - - controller.destvec = 2 * control; // control point - controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point - // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (dest - control) -} - -void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest) -{ - // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t - // 2 * control * t - 2 * control * t * t + dest * t * t - // 2 * control * t + (dest - 2 * control) * t * t - - controller.origin = org; // starting point - dest -= org; - - controller.destvec = dest; // end point - controller.destvec2 = '0 0 0'; -} - -void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func) -{ - float traveltime; - entity controller; - - if (!tspeed) - objerror ("No speed is defined!"); - - self.think1 = func; - self.finaldest = tdest; - self.think = SUB_CalcMoveDone; - - switch(tspeedtype) - { - default: - case TSPEED_START: - traveltime = 2 * vlen(tcontrol - self.origin) / tspeed; - break; - case TSPEED_END: - traveltime = 2 * vlen(tcontrol - tdest) / tspeed; - break; - case TSPEED_LINEAR: - traveltime = vlen(tdest - self.origin) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - if (traveltime < 0.1) // useless anim - { - self.velocity = '0 0 0'; - self.nextthink = self.ltime + 0.1; - return; - } - - controller = spawn(); - controller.classname = "SUB_CalcMove_controller"; - controller.owner = self; - controller.platmovetype = self.platmovetype; - controller.platmovetype_start = self.platmovetype_start; - controller.platmovetype_end = self.platmovetype_end; - SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest); - controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit. - controller.animstate_starttime = time; - controller.animstate_endtime = time + traveltime; - controller.think = SUB_CalcMove_controller_think; - controller.think1 = self.think; - - // the thinking is now done by the controller - self.think = SUB_NullThink; // for PushMove - self.nextthink = self.ltime + traveltime; - - // invoke controller - self = controller; - self.think(); - self = self.owner; -} - -void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func) -{ - vector delta; - float traveltime; - - if (!tspeed) - objerror ("No speed is defined!"); - - self.think1 = func; - self.finaldest = tdest; - self.think = SUB_CalcMoveDone; - - if (tdest == self.origin) - { - self.velocity = '0 0 0'; - self.nextthink = self.ltime + 0.1; - return; - } - - delta = tdest - self.origin; - - switch(tspeedtype) - { - default: - case TSPEED_START: - case TSPEED_END: - case TSPEED_LINEAR: - traveltime = vlen (delta) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - // Very short animations don't really show off the effect - // of controlled animation, so let's just use linear movement. - // Alternatively entities can choose to specify non-controlled movement. - // The only currently implemented alternative movement is linear (value 1) - if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct? - { - self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division - self.nextthink = self.ltime + traveltime; - return; - } - - // now just run like a bezier curve... - SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func); -} - -void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func) -{ - entity oldself; - - oldself = self; - self = ent; - - SUB_CalcMove (tdest, tspeedtype, tspeed, func); - - self = oldself; -} - -/* -============= -SUB_CalcAngleMove - -calculate self.avelocity and self.nextthink to reach destangle from -self.angles rotating - -The calling function should make sure self.think is valid -=============== -*/ -void SUB_CalcAngleMoveDone (void) -{ - // After rotating, set angle to exact final angle - self.angles = self.finalangle; - self.avelocity = '0 0 0'; - self.nextthink = -1; - if (self.think1) - self.think1 (); -} - -// FIXME: I fixed this function only for rotation around the main axes -void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func) -{ - vector delta; - float traveltime; - - if (!tspeed) - objerror ("No speed is defined!"); - - // take the shortest distance for the angles - self.angles_x -= 360 * floor((self.angles.x - destangle.x) / 360 + 0.5); - self.angles_y -= 360 * floor((self.angles.y - destangle.y) / 360 + 0.5); - self.angles_z -= 360 * floor((self.angles.z - destangle.z) / 360 + 0.5); - delta = destangle - self.angles; - - switch(tspeedtype) - { - default: - case TSPEED_START: - case TSPEED_END: - case TSPEED_LINEAR: - traveltime = vlen (delta) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - self.think1 = func; - self.finalangle = destangle; - self.think = SUB_CalcAngleMoveDone; - - if (traveltime < 0.1) - { - self.avelocity = '0 0 0'; - self.nextthink = self.ltime + 0.1; - return; - } - - self.avelocity = delta * (1 / traveltime); - self.nextthink = self.ltime + traveltime; -} - -void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func) -{ - entity oldself; - - oldself = self; - self = ent; - - SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func); - - self = oldself; -} -- /* ================== main diff --cc qcsrc/server/g_subs.qh index 000000000,04477e629..d62bbb612 mode 000000,100644..100644 --- a/qcsrc/server/g_subs.qh +++ b/qcsrc/server/g_subs.qh @@@ -1,0 -1,197 +1,191 @@@ + #ifndef G_SUBS_H + #define G_SUBS_H + + void SUB_NullThink(void); + + void() SUB_CalcMoveDone; + void() SUB_CalcAngleMoveDone; + //void() SUB_UseTargets; + void() SUB_Remove; + + void spawnfunc_info_null (void); + + void setanim(entity e, vector anim, float looping, float override, float restart); + + void updateanim(entity e); + + /* + ================== + SUB_Remove + + Remove self + ================== + */ + void SUB_Remove (void); + + /* + ================== + SUB_Friction + + Applies some friction to self + ================== + */ + .float friction; + void SUB_Friction (void); + + /* + ================== + SUB_VanishOrRemove + + Makes client invisible or removes non-client + ================== + */ + void SUB_VanishOrRemove (entity ent); + + void SUB_SetFade_Think (void); + + /* + ================== + SUB_SetFade + + Fade 'ent' out when time >= 'when' + ================== + */ + void SUB_SetFade (entity ent, float when, float fadetime); + + /* + ============= + SUB_CalcMove + + calculate self.velocity and self.nextthink to reach dest from + self.origin traveling at speed + =============== + */ + void SUB_CalcMoveDone (void); + + .float platmovetype_turn; + void SUB_CalcMove_controller_think (void); + + void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest); + + void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest); + -float TSPEED_TIME = -1; -float TSPEED_LINEAR = 0; -float TSPEED_START = 1; -float TSPEED_END = 2; -// TODO average too? - + void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func); + + void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func); + + void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func); + + /* + ============= + SUB_CalcAngleMove + + calculate self.avelocity and self.nextthink to reach destangle from + self.angles rotating + + The calling function should make sure self.think is valid + =============== + */ + void SUB_CalcAngleMoveDone (void); + + // FIXME: I fixed this function only for rotation around the main axes + void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func); + + void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func); + + /* + ================== + main + + unused but required by the engine + ================== + */ + void main (void); + + // Misc + + /* + ================== + traceline_antilag + + A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack + Additionally it moves players back into the past before the trace and restores them afterward. + ================== + */ + void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz); + void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag); + void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag); + + float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity); // returns the number of traces done, for benchmarking + + void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity); + + /* + ================== + findbetterlocation + + Returns a point at least 12 units away from walls + (useful for explosion animations, although the blast is performed where it really happened) + Ripped from DPMod + ================== + */ + vector findbetterlocation (vector org, float mindist); + + /* + ================== + crandom + + Returns a random number between -1.0 and 1.0 + ================== + */ + float crandom (void); + + /* + ================== + Angc used for animations + ================== + */ + + + float angc (float a1, float a2); + + .string lodtarget1; + .string lodtarget2; + .string lodmodel1; + .string lodmodel2; + .float lodmodelindex0; + .float lodmodelindex1; + .float lodmodelindex2; + .float loddistance1; + .float loddistance2; + + float LOD_customize(); + + void LOD_uncustomize(); + + void LODmodel_attach(); + + void ApplyMinMaxScaleAngles(entity e); + + void SetBrushEntityModel(); + + void SetBrushEntityModelNoLOD(); + + /* + ================ + InitTrigger + ================ + */ + + void SetMovedir(); + + void InitTrigger(); + + void InitSolidBSPTrigger(); + + float InitMovingBrushTrigger(); + #endif diff --cc qcsrc/server/g_world.qc index adfb59a08,88e70233f..d1808d988 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@@ -1,4 -1,41 +1,40 @@@ - #define LATENCY_THINKRATE 10 + #include "g_world.qh" + + #include "../common/buffs.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../common/constants.qh" + #include "../common/stats.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/monsters/sv_monsters.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/weaponstats.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "mutators/mutators_include.qh" + #include "campaign.qh" + #include "../common/mapinfo.qh" + #include "command/common.qh" + #include "command/vote.qh" + #include "command/getreplies.qh" + #include "command/sv_cmd.qh" + #include "anticheat.qh" + #include "cheats.qh" + #include "../common/playerstats.qh" + #include "g_hook.qh" + #include "scores.qh" + #include "mapvoting.qh" + #include "ipban.qh" + #include "race.qh" + #include "antilag.qh" - #include "secret.qh" + #endif + + const float LATENCY_THINKRATE = 10; .float latency_sum; .float latency_cnt; .float latency_time; diff --cc qcsrc/server/item_key.qc index 79003853a,f4893094e..85159e49c --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@@ -1,3 -1,16 +1,17 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/util_server.qh" + #include "../common/util.qh" + #include "../common/monsters/monsters.qh" ++ #include "../common/triggers/subs.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "item_key.qh" + #endif + /* TODO: - add an unlock sound (here to trigger_keylock and to func_door) @@@ -424,6 -431,6 +432,4 @@@ void spawnfunc_trigger_keylock(void) EXACTTRIGGER_INIT; self.touch = trigger_keylock_touch; --}; -- -- ++} diff --cc qcsrc/server/item_key.qh index b077c0070,6ab560503..4a2acd85b --- a/qcsrc/server/item_key.qh +++ b/qcsrc/server/item_key.qh @@@ -22,3 -24,3 +25,5 @@@ float item_keys_usekey(entity l, entit */ string item_keys_keylist(float keylist); #endif ++ ++#endif diff --cc qcsrc/server/miscfunctions.qc index 7b4e76381,bcbae77ea..8ef3018fd --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@@ -1,9 -1,35 +1,36 @@@ - var void remove(entity e); - void objerror(string s); - void droptofloor(); - .vector dropped_origin; + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "miscfunctions.qh" + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../common/playerstats.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/urllib.qh" + #include "../common/command/generic.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" ++ #include "../common/triggers/subs.qh" + #include "mutators/mutators_include.qh" + #include "tturrets/include/turrets_early.qh" + #include "../common/mapinfo.qh" + #include "command/common.qh" + #include "../csqcmodellib/sv_model.qh" + #include "ipban.qh" + #endif - void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); void crosshair_trace(entity pl) { traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); @@@ -552,499 -517,9 +518,7 @@@ vector randompos(vector m1, vector m2 return v; } - //#NO AUTOCVARS START - - float g_pickup_shells; - float g_pickup_shells_max; - float g_pickup_nails; - float g_pickup_nails_max; - float g_pickup_rockets; - float g_pickup_rockets_max; - float g_pickup_cells; - float g_pickup_cells_max; - float g_pickup_plasma; - float g_pickup_plasma_max; - float g_pickup_fuel; - float g_pickup_fuel_jetpack; - float g_pickup_fuel_max; - float g_pickup_armorsmall; - float g_pickup_armorsmall_max; - float g_pickup_armorsmall_anyway; - float g_pickup_armormedium; - float g_pickup_armormedium_max; - float g_pickup_armormedium_anyway; - float g_pickup_armorbig; - float g_pickup_armorbig_max; - float g_pickup_armorbig_anyway; - float g_pickup_armorlarge; - float g_pickup_armorlarge_max; - float g_pickup_armorlarge_anyway; - float g_pickup_healthsmall; - float g_pickup_healthsmall_max; - float g_pickup_healthsmall_anyway; - float g_pickup_healthmedium; - float g_pickup_healthmedium_max; - float g_pickup_healthmedium_anyway; - float g_pickup_healthlarge; - float g_pickup_healthlarge_max; - float g_pickup_healthlarge_anyway; - float g_pickup_healthmega; - float g_pickup_healthmega_max; - float g_pickup_healthmega_anyway; - float g_pickup_ammo_anyway; - float g_pickup_weapons_anyway; - float g_weaponarena; - WepSet g_weaponarena_weapons; - float g_weaponarena_random; - float g_weaponarena_random_with_blaster; - string g_weaponarena_list; - float g_weaponspeedfactor; - float g_weaponratefactor; - float g_weapondamagefactor; - float g_weaponforcefactor; - float g_weaponspreadfactor; - - WepSet start_weapons; - WepSet start_weapons_default; - WepSet start_weapons_defaultmask; - float start_items; - float start_ammo_shells; - float start_ammo_nails; - float start_ammo_rockets; - float start_ammo_cells; - float start_ammo_plasma; - float start_ammo_fuel; - float start_health; - float start_armorvalue; - WepSet warmup_start_weapons; - WepSet warmup_start_weapons_default; - WepSet warmup_start_weapons_defaultmask; - #define WARMUP_START_WEAPONS ((g_warmup_allguns == 1) ? (warmup_start_weapons & (weaponsInMap | start_weapons)) : warmup_start_weapons) - float warmup_start_ammo_shells; - float warmup_start_ammo_nails; - float warmup_start_ammo_rockets; - float warmup_start_ammo_cells; - float warmup_start_ammo_plasma; - float warmup_start_ammo_fuel; - float warmup_start_health; - float warmup_start_armorvalue; - float g_weapon_stay; - - float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done? - { - var float i = weaponinfo.weapon; - var float d = 0; - - if (!i) - return 0; - - if (g_lms || g_ca || allguns) - { - if(weaponinfo.spawnflags & WEP_FLAG_NORMAL) - d = TRUE; - else - d = FALSE; - } - else if (g_cts) - d = (i == WEP_SHOTGUN); - else if (g_nexball) - d = 0; // weapon is set a few lines later - else - d = !(!weaponinfo.weaponstart); - - if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook - d |= (i == WEP_HOOK); - if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns - d = 0; - - var float t = weaponinfo.weaponstartoverride; - - //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n")); - - // bit order in t: - // 1: want or not - // 2: is default? - // 4: is set by default? - if(t < 0) - t = 4 | (3 * d); - else - t |= (2 * d); - - return t; - } - - void readplayerstartcvars() - { - entity e; - float i, j, t; - string s; - - // initialize starting values for players - start_weapons = '0 0 0'; - start_weapons_default = '0 0 0'; - start_weapons_defaultmask = '0 0 0'; - start_items = 0; - start_ammo_shells = 0; - start_ammo_nails = 0; - start_ammo_rockets = 0; - start_ammo_cells = 0; - start_ammo_plasma = 0; - start_health = cvar("g_balance_health_start"); - start_armorvalue = cvar("g_balance_armor_start"); - - g_weaponarena = 0; - g_weaponarena_weapons = '0 0 0'; - - s = cvar_string("g_weaponarena"); - if (s == "0" || s == "") - { - if(g_ca) - s = "most"; - } - - if (s == "0" || s == "") - { - // no arena - } - else if (s == "off") - { - // forcibly turn off weaponarena - } - else if (s == "all" || s == "1") - { - g_weaponarena = 1; - g_weaponarena_list = "All Weapons"; - for (j = WEP_FIRST; j <= WEP_LAST; ++j) - { - e = get_weaponinfo(j); - if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)) - g_weaponarena_weapons |= WepSet_FromWeapon(j); - } - } - else if (s == "most") - { - g_weaponarena = 1; - g_weaponarena_list = "Most Weapons"; - for (j = WEP_FIRST; j <= WEP_LAST; ++j) - { - e = get_weaponinfo(j); - if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)) - if (e.spawnflags & WEP_FLAG_NORMAL) - g_weaponarena_weapons |= WepSet_FromWeapon(j); - } - } - else if (s == "none") - { - g_weaponarena = 1; - g_weaponarena_list = "No Weapons"; - } - else - { - g_weaponarena = 1; - t = tokenize_console(s); - g_weaponarena_list = ""; - for (i = 0; i < t; ++i) - { - s = argv(i); - for (j = WEP_FIRST; j <= WEP_LAST; ++j) - { - e = get_weaponinfo(j); - if (e.netname == s) - { - g_weaponarena_weapons |= WepSet_FromWeapon(j); - g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & "); - break; - } - } - if (j > WEP_LAST) - { - print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n"); - } - } - g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3)); - } - - if(g_weaponarena) - g_weaponarena_random = cvar("g_weaponarena_random"); - else - g_weaponarena_random = 0; - g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster"); - - if (g_weaponarena) - { - g_weapon_stay = 0; // incompatible - start_weapons = g_weaponarena_weapons; - start_items |= IT_UNLIMITED_AMMO; - } - else - { - for (i = WEP_FIRST; i <= WEP_LAST; ++i) - { - e = get_weaponinfo(i); - float w = want_weapon(e, FALSE); - if(w & 1) - start_weapons |= WepSet_FromWeapon(i); - if(w & 2) - start_weapons_default |= WepSet_FromWeapon(i); - if(w & 4) - start_weapons_defaultmask |= WepSet_FromWeapon(i); - } - } - - if(!cvar("g_use_ammunition")) - start_items |= IT_UNLIMITED_AMMO; - - if(start_items & IT_UNLIMITED_WEAPON_AMMO) - { - start_ammo_shells = 999; - start_ammo_nails = 999; - start_ammo_rockets = 999; - start_ammo_cells = 999; - start_ammo_plasma = 999; - start_ammo_fuel = 999; - } - else - { - start_ammo_shells = cvar("g_start_ammo_shells"); - start_ammo_nails = cvar("g_start_ammo_nails"); - start_ammo_rockets = cvar("g_start_ammo_rockets"); - start_ammo_cells = cvar("g_start_ammo_cells"); - start_ammo_plasma = cvar("g_start_ammo_plasma"); - start_ammo_fuel = cvar("g_start_ammo_fuel"); - } - - if (warmup_stage) - { - warmup_start_ammo_shells = start_ammo_shells; - warmup_start_ammo_nails = start_ammo_nails; - warmup_start_ammo_rockets = start_ammo_rockets; - warmup_start_ammo_cells = start_ammo_cells; - warmup_start_ammo_plasma = start_ammo_plasma; - warmup_start_ammo_fuel = start_ammo_fuel; - warmup_start_health = start_health; - warmup_start_armorvalue = start_armorvalue; - warmup_start_weapons = start_weapons; - warmup_start_weapons_default = start_weapons_default; - warmup_start_weapons_defaultmask = start_weapons_defaultmask; - - if (!g_weaponarena && !g_ca) - { - warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells"); - warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails"); - warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets"); - warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells"); - warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma"); - warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel"); - warmup_start_health = cvar("g_warmup_start_health"); - warmup_start_armorvalue = cvar("g_warmup_start_armor"); - warmup_start_weapons = '0 0 0'; - warmup_start_weapons_default = '0 0 0'; - warmup_start_weapons_defaultmask = '0 0 0'; - for (i = WEP_FIRST; i <= WEP_LAST; ++i) - { - e = get_weaponinfo(i); - float w = want_weapon(e, g_warmup_allguns); - if(w & 1) - warmup_start_weapons |= WepSet_FromWeapon(i); - if(w & 2) - warmup_start_weapons_default |= WepSet_FromWeapon(i); - if(w & 4) - warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i); - } - } - } - - if (g_jetpack) - start_items |= IT_JETPACK; - - MUTATOR_CALLHOOK(SetStartItems); - - if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK))) - { - start_items |= IT_FUEL_REGEN; - start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable")); - warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable")); - } - - WepSet precache_weapons = start_weapons; - if (g_warmup_allguns != 1) - precache_weapons |= warmup_start_weapons; - for (i = WEP_FIRST; i <= WEP_LAST; ++i) - { - e = get_weaponinfo(i); - if(precache_weapons & WepSet_FromWeapon(i)) - WEP_ACTION(i, WR_INIT); - } - - start_ammo_shells = max(0, start_ammo_shells); - start_ammo_nails = max(0, start_ammo_nails); - start_ammo_rockets = max(0, start_ammo_rockets); - start_ammo_cells = max(0, start_ammo_cells); - start_ammo_plasma = max(0, start_ammo_plasma); - start_ammo_fuel = max(0, start_ammo_fuel); - - warmup_start_ammo_shells = max(0, warmup_start_ammo_shells); - warmup_start_ammo_nails = max(0, warmup_start_ammo_nails); - warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets); - warmup_start_ammo_cells = max(0, warmup_start_ammo_cells); - warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma); - warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel); - } - - float g_bugrigs; - float g_bugrigs_planar_movement; - float g_bugrigs_planar_movement_car_jumping; - float g_bugrigs_reverse_spinning; - float g_bugrigs_reverse_speeding; - float g_bugrigs_reverse_stopping; - float g_bugrigs_air_steering; - float g_bugrigs_angle_smoothing; - float g_bugrigs_friction_floor; - float g_bugrigs_friction_brake; - float g_bugrigs_friction_air; - float g_bugrigs_accel; - float g_bugrigs_speed_ref; - float g_bugrigs_speed_pow; - float g_bugrigs_steer; - - float sv_autotaunt; - float sv_taunt; - - string GetGametype(); // g_world.qc - void mutators_add(); // mutators.qc - void readlevelcvars(void) - { - // load mutators - mutators_add(); - - if(cvar("sv_allow_fullbright")) - serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT; - - g_bugrigs = cvar("g_bugrigs"); - g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement"); - g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping"); - g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning"); - g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding"); - g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping"); - g_bugrigs_air_steering = cvar("g_bugrigs_air_steering"); - g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing"); - g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor"); - g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake"); - g_bugrigs_friction_air = cvar("g_bugrigs_friction_air"); - g_bugrigs_accel = cvar("g_bugrigs_accel"); - g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref"); - g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow"); - g_bugrigs_steer = cvar("g_bugrigs_steer"); - - g_instagib = cvar("g_instagib"); - - sv_clones = cvar("sv_clones"); - sv_foginterval = cvar("sv_foginterval"); - g_cloaked = cvar("g_cloaked"); - g_footsteps = cvar("g_footsteps"); - g_grappling_hook = cvar("g_grappling_hook"); - g_jetpack = cvar("g_jetpack"); - sv_maxidle = cvar("sv_maxidle"); - sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle"); - sv_autotaunt = cvar("sv_autotaunt"); - sv_taunt = cvar("sv_taunt"); - - warmup_stage = cvar("g_warmup"); - g_warmup_limit = cvar("g_warmup_limit"); - g_warmup_allguns = cvar("g_warmup_allguns"); - g_warmup_allow_timeout = cvar("g_warmup_allow_timeout"); - - if ((g_race && g_race_qualifying == 2) || g_assault || cvar("g_campaign")) - warmup_stage = 0; // these modes cannot work together, sorry - - g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon"); - g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon"); - g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo"); - g_pickup_respawntime_short = cvar("g_pickup_respawntime_short"); - g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium"); - g_pickup_respawntime_long = cvar("g_pickup_respawntime_long"); - g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup"); - g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon"); - g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon"); - g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo"); - g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short"); - g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium"); - g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long"); - g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup"); - - g_weaponspeedfactor = cvar("g_weaponspeedfactor"); - g_weaponratefactor = cvar("g_weaponratefactor"); - g_weapondamagefactor = cvar("g_weapondamagefactor"); - g_weaponforcefactor = cvar("g_weaponforcefactor"); - g_weaponspreadfactor = cvar("g_weaponspreadfactor"); - - g_pickup_shells = cvar("g_pickup_shells"); - g_pickup_shells_max = cvar("g_pickup_shells_max"); - g_pickup_nails = cvar("g_pickup_nails"); - g_pickup_nails_max = cvar("g_pickup_nails_max"); - g_pickup_rockets = cvar("g_pickup_rockets"); - g_pickup_rockets_max = cvar("g_pickup_rockets_max"); - g_pickup_cells = cvar("g_pickup_cells"); - g_pickup_cells_max = cvar("g_pickup_cells_max"); - g_pickup_plasma = cvar("g_pickup_plasma"); - g_pickup_plasma_max = cvar("g_pickup_plasma_max"); - g_pickup_fuel = cvar("g_pickup_fuel"); - g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack"); - g_pickup_fuel_max = cvar("g_pickup_fuel_max"); - g_pickup_armorsmall = cvar("g_pickup_armorsmall"); - g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max"); - g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway"); - g_pickup_armormedium = cvar("g_pickup_armormedium"); - g_pickup_armormedium_max = cvar("g_pickup_armormedium_max"); - g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway"); - g_pickup_armorbig = cvar("g_pickup_armorbig"); - g_pickup_armorbig_max = cvar("g_pickup_armorbig_max"); - g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway"); - g_pickup_armorlarge = cvar("g_pickup_armorlarge"); - g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max"); - g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway"); - g_pickup_healthsmall = cvar("g_pickup_healthsmall"); - g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max"); - g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway"); - g_pickup_healthmedium = cvar("g_pickup_healthmedium"); - g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max"); - g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway"); - g_pickup_healthlarge = cvar("g_pickup_healthlarge"); - g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max"); - g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway"); - g_pickup_healthmega = cvar("g_pickup_healthmega"); - g_pickup_healthmega_max = cvar("g_pickup_healthmega_max"); - g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway"); - - g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway"); - g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway"); - - g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay")); - if(!g_weapon_stay) - g_weapon_stay = cvar("g_weapon_stay"); - - if (!warmup_stage) - game_starttime = time + cvar("g_start_delay"); - - float i; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - WEP_ACTION(i, WR_INIT); - - readplayerstartcvars(); - } - - //#NO AUTOCVARS END - - // Sound functions - string precache_sound (string s) = #19; - float precache_sound_index (string s) = #19; -- - #define SND_VOLUME 1 - #define SND_ATTENUATION 2 - #define SND_LARGEENTITY 8 - #define SND_LARGESOUND 16 -- -float sound_allowed(float _dest, entity e) +float sound_allowed(float destin, entity e) { // sounds from world may always pass for (;;) @@@ -1059,9 -534,9 +533,9 @@@ break; } // sounds to self may always pass - if (_dest == MSG_ONE) + if (destin == MSG_ONE) if (e == msg_entity) - return TRUE; + return true; // sounds by players can be removed if (autocvar_bot_sound_monopoly) if (IS_REAL_CLIENT(e)) @@@ -1075,10 -550,10 +549,10 @@@ void sound(entity e, float chan, strin { if (!sound_allowed(MSG_BROADCAST, e)) return; - sound7(e, chan, samp, vol, _atten, 0, 0); + sound7(e, chan, samp, vol, attenu, 0, 0); } - void soundtoat(float destin, entity e, vector o, float chan, string samp, float vol, float attenu) -void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten) ++void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu) { float entno, idx; @@@ -1103,16 -578,16 +577,16 @@@ if (idx >= 256) sflags |= SND_LARGESOUND; - WriteByte(destin, SVC_SOUND); - WriteByte(destin, sflags); + WriteByte(_dest, SVC_SOUND); + WriteByte(_dest, sflags); if (sflags & SND_VOLUME) - WriteByte(destin, vol); + WriteByte(_dest, vol); if (sflags & SND_ATTENUATION) - WriteByte(destin, attenu); - WriteByte(_dest, _atten); ++ WriteByte(_dest, attenu); if (sflags & SND_LARGEENTITY) { - WriteShort(destin, entno); - WriteByte(destin, chan); + WriteShort(_dest, entno); + WriteByte(_dest, chan); } else { diff --cc qcsrc/server/miscfunctions.qh index 000000000,7cebe9c5d..016285fb1 mode 000000,100644..100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@@ -1,0 -1,687 +1,685 @@@ + #ifndef MISCFUNCTIONS_H + #define MISCFUNCTIONS_H + + #include "t_items.qh" + + #include "mutators/base.qh" + #include "mutators/gamemode_race.qh" + + #include "../common/constants.qh" + #include "../common/mapinfo.qh" + + #ifdef RELEASE + #define cvar_string_normal builtin_cvar_string + #define cvar_normal builtin_cvar + #else + string cvar_string_normal(string n) + { + if (!(cvar_type(n) & 1)) + backtrace(strcat("Attempt to access undefined cvar: ", n)); + return builtin_cvar_string(n); + } + + float cvar_normal(string n) + { + return stof(cvar_string_normal(n)); + } + #endif + #define cvar_set_normal builtin_cvar_set + + .vector dropped_origin; + .void(void) uncustomizeentityforclient; + .float uncustomizeentityforclient_set; + .float nottargeted; + + + float DistributeEvenly_amount; + float DistributeEvenly_totalweight; + var void remove(entity e); + void objerror(string s); + void droptofloor(); + void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints + void() spawnpoint_use; + void() SUB_Remove; + + void attach_sameorigin(entity e, entity to, string tag); + + void crosshair_trace(entity pl); + + void crosshair_trace_plusvisibletriggers(entity pl); + + void detach_sameorigin(entity e); + + void follow_sameorigin(entity e, entity to); + + string formatmessage(string msg); + + void GameLogEcho(string s); + + void GameLogInit(); + + void GameLogClose(); + + void GetCvars(float f); + + string GetMapname(); + + float isPushable(entity e); + + float LostMovetypeFollow(entity ent); + + float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance); + + string NearestLocation(vector p); + + void play2(entity e, string filename); + + string playername(entity p); + + void precache(); + + void remove_safely(entity e); + + void remove_unsafely(entity e); + + void SetMovetypeFollow(entity ent, entity e); + + vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn); + + void soundto(float dest, entity e, float chan, string samp, float vol, float atten); + + void stopsound(entity e, float chan); + + float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma); + + void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + + void WarpZone_crosshair_trace(entity pl); + + void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); + + -#define IFTARGETED if(!self.nottargeted && self.targetname != "") - + #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP)) + + #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return + + const string STR_PLAYER = "player"; + const string STR_SPECTATOR = "spectator"; + const string STR_OBSERVER = "observer"; + + #define IS_PLAYER(v) (v.classname == STR_PLAYER) + #define IS_SPEC(v) (v.classname == STR_SPECTATOR) + #define IS_OBSERVER(v) (v.classname == STR_OBSERVER) + #define IS_CLIENT(v) (v.flags & FL_CLIENT) + #define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT) + #define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL) + #define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT) + + #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); ) + #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v)) + #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v)) + + #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(IS_PLAYER(v)) + #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (!IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too + #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v)) + + #define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; ) + + #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5))) + + // copies a string to a tempstring (so one can strunzone it) + string strcat1(string s) = #115; // FRIK_FILE + + float logfile_open; + float logfile; + + #define strstr strstrofs + /* + // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN. + // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP + // STRINGS AND TAKE QUITE LONG. haystack and needle MUST + // BE CONSTANT OR strzoneD! + float strstr(string haystack, string needle, float offset) + { + float len, endpos; + string found; + len = strlen(needle); + endpos = strlen(haystack) - len; + while(offset <= endpos) + { + found = substring(haystack, offset, len); + if(found == needle) + return offset; + offset = offset + 1; + } + return -1; + } + */ + + const float NUM_NEAREST_ENTITIES = 4; + entity nearest_entity[NUM_NEAREST_ENTITIES]; + float nearest_length[NUM_NEAREST_ENTITIES]; + + + //#NO AUTOCVARS START + + float g_pickup_shells; + float g_pickup_shells_max; + float g_pickup_nails; + float g_pickup_nails_max; + float g_pickup_rockets; + float g_pickup_rockets_max; + float g_pickup_cells; + float g_pickup_cells_max; + float g_pickup_plasma; + float g_pickup_plasma_max; + float g_pickup_fuel; + float g_pickup_fuel_jetpack; + float g_pickup_fuel_max; + float g_pickup_armorsmall; + float g_pickup_armorsmall_max; + float g_pickup_armorsmall_anyway; + float g_pickup_armormedium; + float g_pickup_armormedium_max; + float g_pickup_armormedium_anyway; + float g_pickup_armorbig; + float g_pickup_armorbig_max; + float g_pickup_armorbig_anyway; + float g_pickup_armorlarge; + float g_pickup_armorlarge_max; + float g_pickup_armorlarge_anyway; + float g_pickup_healthsmall; + float g_pickup_healthsmall_max; + float g_pickup_healthsmall_anyway; + float g_pickup_healthmedium; + float g_pickup_healthmedium_max; + float g_pickup_healthmedium_anyway; + float g_pickup_healthlarge; + float g_pickup_healthlarge_max; + float g_pickup_healthlarge_anyway; + float g_pickup_healthmega; + float g_pickup_healthmega_max; + float g_pickup_healthmega_anyway; + float g_pickup_ammo_anyway; + float g_pickup_weapons_anyway; + float g_weaponarena; + WepSet g_weaponarena_weapons; + float g_weaponarena_random; + float g_weaponarena_random_with_blaster; + string g_weaponarena_list; + float g_weaponspeedfactor; + float g_weaponratefactor; + float g_weapondamagefactor; + float g_weaponforcefactor; + float g_weaponspreadfactor; + + WepSet start_weapons; + WepSet start_weapons_default; + WepSet start_weapons_defaultmask; + int start_items; + float start_ammo_shells; + float start_ammo_nails; + float start_ammo_rockets; + float start_ammo_cells; + float start_ammo_plasma; + float start_ammo_fuel; + float start_health; + float start_armorvalue; + WepSet warmup_start_weapons; + WepSet warmup_start_weapons_default; + WepSet warmup_start_weapons_defaultmask; + #define WARMUP_START_WEAPONS ((g_warmup_allguns == 1) ? (warmup_start_weapons & (weaponsInMap | start_weapons)) : warmup_start_weapons) + float warmup_start_ammo_shells; + float warmup_start_ammo_nails; + float warmup_start_ammo_rockets; + float warmup_start_ammo_cells; + float warmup_start_ammo_plasma; + float warmup_start_ammo_fuel; + float warmup_start_health; + float warmup_start_armorvalue; + float g_weapon_stay; + + float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done? + { + int i = weaponinfo.weapon; + int d = 0; + + if (!i) + return 0; + + if (g_lms || g_ca || allguns) + { + if(weaponinfo.spawnflags & WEP_FLAG_NORMAL) + d = true; + else + d = false; + } + else if (g_cts) + d = (i == WEP_SHOTGUN); + else if (g_nexball) + d = 0; // weapon is set a few lines later + else + d = !(!weaponinfo.weaponstart); + + if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook + d |= (i == WEP_HOOK); + if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns + d = 0; + + float t = weaponinfo.weaponstartoverride; + + //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n")); + + // bit order in t: + // 1: want or not + // 2: is default? + // 4: is set by default? + if(t < 0) + t = 4 | (3 * d); + else + t |= (2 * d); + + return t; + } + + void readplayerstartcvars() + { + entity e; + float i, j, t; + string s; + + // initialize starting values for players + start_weapons = '0 0 0'; + start_weapons_default = '0 0 0'; + start_weapons_defaultmask = '0 0 0'; + start_items = 0; + start_ammo_shells = 0; + start_ammo_nails = 0; + start_ammo_rockets = 0; + start_ammo_cells = 0; + start_ammo_plasma = 0; + start_health = cvar("g_balance_health_start"); + start_armorvalue = cvar("g_balance_armor_start"); + + g_weaponarena = 0; + g_weaponarena_weapons = '0 0 0'; + + s = cvar_string("g_weaponarena"); + if (s == "0" || s == "") + { + if(g_ca) + s = "most"; + } + + if (s == "0" || s == "") + { + // no arena + } + else if (s == "off") + { + // forcibly turn off weaponarena + } + else if (s == "all" || s == "1") + { + g_weaponarena = 1; + g_weaponarena_list = "All Weapons"; + for (j = WEP_FIRST; j <= WEP_LAST; ++j) + { + e = get_weaponinfo(j); + if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)) + g_weaponarena_weapons |= WepSet_FromWeapon(j); + } + } + else if (s == "most") + { + g_weaponarena = 1; + g_weaponarena_list = "Most Weapons"; + for (j = WEP_FIRST; j <= WEP_LAST; ++j) + { + e = get_weaponinfo(j); + if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)) + if (e.spawnflags & WEP_FLAG_NORMAL) + g_weaponarena_weapons |= WepSet_FromWeapon(j); + } + } + else if (s == "none") + { + g_weaponarena = 1; + g_weaponarena_list = "No Weapons"; + } + else + { + g_weaponarena = 1; + t = tokenize_console(s); + g_weaponarena_list = ""; + for (i = 0; i < t; ++i) + { + s = argv(i); + for (j = WEP_FIRST; j <= WEP_LAST; ++j) + { + e = get_weaponinfo(j); + if (e.netname == s) + { + g_weaponarena_weapons |= WepSet_FromWeapon(j); + g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & "); + break; + } + } + if (j > WEP_LAST) + { + print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n"); + } + } + g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3)); + } + + if(g_weaponarena) + g_weaponarena_random = cvar("g_weaponarena_random"); + else + g_weaponarena_random = 0; + g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster"); + + if (g_weaponarena) + { + g_weapon_stay = 0; // incompatible + start_weapons = g_weaponarena_weapons; + start_items |= IT_UNLIMITED_AMMO; + } + else + { + for (i = WEP_FIRST; i <= WEP_LAST; ++i) + { + e = get_weaponinfo(i); + int w = want_weapon(e, false); + if(w & 1) + start_weapons |= WepSet_FromWeapon(i); + if(w & 2) + start_weapons_default |= WepSet_FromWeapon(i); + if(w & 4) + start_weapons_defaultmask |= WepSet_FromWeapon(i); + } + } + + if(!cvar("g_use_ammunition")) + start_items |= IT_UNLIMITED_AMMO; + + if(start_items & IT_UNLIMITED_WEAPON_AMMO) + { + start_ammo_shells = 999; + start_ammo_nails = 999; + start_ammo_rockets = 999; + start_ammo_cells = 999; + start_ammo_plasma = 999; + start_ammo_fuel = 999; + } + else + { + start_ammo_shells = cvar("g_start_ammo_shells"); + start_ammo_nails = cvar("g_start_ammo_nails"); + start_ammo_rockets = cvar("g_start_ammo_rockets"); + start_ammo_cells = cvar("g_start_ammo_cells"); + start_ammo_plasma = cvar("g_start_ammo_plasma"); + start_ammo_fuel = cvar("g_start_ammo_fuel"); + } + + if (warmup_stage) + { + warmup_start_ammo_shells = start_ammo_shells; + warmup_start_ammo_nails = start_ammo_nails; + warmup_start_ammo_rockets = start_ammo_rockets; + warmup_start_ammo_cells = start_ammo_cells; + warmup_start_ammo_plasma = start_ammo_plasma; + warmup_start_ammo_fuel = start_ammo_fuel; + warmup_start_health = start_health; + warmup_start_armorvalue = start_armorvalue; + warmup_start_weapons = start_weapons; + warmup_start_weapons_default = start_weapons_default; + warmup_start_weapons_defaultmask = start_weapons_defaultmask; + + if (!g_weaponarena && !g_ca) + { + warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells"); + warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails"); + warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets"); + warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells"); + warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma"); + warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel"); + warmup_start_health = cvar("g_warmup_start_health"); + warmup_start_armorvalue = cvar("g_warmup_start_armor"); + warmup_start_weapons = '0 0 0'; + warmup_start_weapons_default = '0 0 0'; + warmup_start_weapons_defaultmask = '0 0 0'; + for (i = WEP_FIRST; i <= WEP_LAST; ++i) + { + e = get_weaponinfo(i); + int w = want_weapon(e, g_warmup_allguns); + if(w & 1) + warmup_start_weapons |= WepSet_FromWeapon(i); + if(w & 2) + warmup_start_weapons_default |= WepSet_FromWeapon(i); + if(w & 4) + warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i); + } + } + } + + if (g_jetpack) + start_items |= IT_JETPACK; + + MUTATOR_CALLHOOK(SetStartItems); + + if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK))) + { + start_items |= IT_FUEL_REGEN; + start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable")); + warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable")); + } + + WepSet precache_weapons = start_weapons; + if (g_warmup_allguns != 1) + precache_weapons |= warmup_start_weapons; + for (i = WEP_FIRST; i <= WEP_LAST; ++i) + { + e = get_weaponinfo(i); + if(precache_weapons & WepSet_FromWeapon(i)) + WEP_ACTION(i, WR_INIT); + } + + start_ammo_shells = max(0, start_ammo_shells); + start_ammo_nails = max(0, start_ammo_nails); + start_ammo_rockets = max(0, start_ammo_rockets); + start_ammo_cells = max(0, start_ammo_cells); + start_ammo_plasma = max(0, start_ammo_plasma); + start_ammo_fuel = max(0, start_ammo_fuel); + + warmup_start_ammo_shells = max(0, warmup_start_ammo_shells); + warmup_start_ammo_nails = max(0, warmup_start_ammo_nails); + warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets); + warmup_start_ammo_cells = max(0, warmup_start_ammo_cells); + warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma); + warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel); + } + + float g_bugrigs; + float g_bugrigs_planar_movement; + float g_bugrigs_planar_movement_car_jumping; + float g_bugrigs_reverse_spinning; + float g_bugrigs_reverse_speeding; + float g_bugrigs_reverse_stopping; + float g_bugrigs_air_steering; + float g_bugrigs_angle_smoothing; + float g_bugrigs_friction_floor; + float g_bugrigs_friction_brake; + float g_bugrigs_friction_air; + float g_bugrigs_accel; + float g_bugrigs_speed_ref; + float g_bugrigs_speed_pow; + float g_bugrigs_steer; + + float sv_autotaunt; + float sv_taunt; + + string GetGametype(); // g_world.qc + void mutators_add(); // mutators.qc + void readlevelcvars(void) + { + // load mutators + mutators_add(); + + if(cvar("sv_allow_fullbright")) + serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT; + + g_bugrigs = cvar("g_bugrigs"); + g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement"); + g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping"); + g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning"); + g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding"); + g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping"); + g_bugrigs_air_steering = cvar("g_bugrigs_air_steering"); + g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing"); + g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor"); + g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake"); + g_bugrigs_friction_air = cvar("g_bugrigs_friction_air"); + g_bugrigs_accel = cvar("g_bugrigs_accel"); + g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref"); + g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow"); + g_bugrigs_steer = cvar("g_bugrigs_steer"); + + g_instagib = cvar("g_instagib"); + + sv_clones = cvar("sv_clones"); + sv_foginterval = cvar("sv_foginterval"); + g_cloaked = cvar("g_cloaked"); + g_footsteps = cvar("g_footsteps"); + g_grappling_hook = cvar("g_grappling_hook"); + g_jetpack = cvar("g_jetpack"); + sv_maxidle = cvar("sv_maxidle"); + sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle"); + sv_autotaunt = cvar("sv_autotaunt"); + sv_taunt = cvar("sv_taunt"); + + warmup_stage = cvar("g_warmup"); + g_warmup_limit = cvar("g_warmup_limit"); + g_warmup_allguns = cvar("g_warmup_allguns"); + g_warmup_allow_timeout = cvar("g_warmup_allow_timeout"); + + if ((g_race && g_race_qualifying == 2) || g_assault || cvar("g_campaign")) + warmup_stage = 0; // these modes cannot work together, sorry + + g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon"); + g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon"); + g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo"); + g_pickup_respawntime_short = cvar("g_pickup_respawntime_short"); + g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium"); + g_pickup_respawntime_long = cvar("g_pickup_respawntime_long"); + g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup"); + g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon"); + g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon"); + g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo"); + g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short"); + g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium"); + g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long"); + g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup"); + + g_weaponspeedfactor = cvar("g_weaponspeedfactor"); + g_weaponratefactor = cvar("g_weaponratefactor"); + g_weapondamagefactor = cvar("g_weapondamagefactor"); + g_weaponforcefactor = cvar("g_weaponforcefactor"); + g_weaponspreadfactor = cvar("g_weaponspreadfactor"); + + g_pickup_shells = cvar("g_pickup_shells"); + g_pickup_shells_max = cvar("g_pickup_shells_max"); + g_pickup_nails = cvar("g_pickup_nails"); + g_pickup_nails_max = cvar("g_pickup_nails_max"); + g_pickup_rockets = cvar("g_pickup_rockets"); + g_pickup_rockets_max = cvar("g_pickup_rockets_max"); + g_pickup_cells = cvar("g_pickup_cells"); + g_pickup_cells_max = cvar("g_pickup_cells_max"); + g_pickup_plasma = cvar("g_pickup_plasma"); + g_pickup_plasma_max = cvar("g_pickup_plasma_max"); + g_pickup_fuel = cvar("g_pickup_fuel"); + g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack"); + g_pickup_fuel_max = cvar("g_pickup_fuel_max"); + g_pickup_armorsmall = cvar("g_pickup_armorsmall"); + g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max"); + g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway"); + g_pickup_armormedium = cvar("g_pickup_armormedium"); + g_pickup_armormedium_max = cvar("g_pickup_armormedium_max"); + g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway"); + g_pickup_armorbig = cvar("g_pickup_armorbig"); + g_pickup_armorbig_max = cvar("g_pickup_armorbig_max"); + g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway"); + g_pickup_armorlarge = cvar("g_pickup_armorlarge"); + g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max"); + g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway"); + g_pickup_healthsmall = cvar("g_pickup_healthsmall"); + g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max"); + g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway"); + g_pickup_healthmedium = cvar("g_pickup_healthmedium"); + g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max"); + g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway"); + g_pickup_healthlarge = cvar("g_pickup_healthlarge"); + g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max"); + g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway"); + g_pickup_healthmega = cvar("g_pickup_healthmega"); + g_pickup_healthmega_max = cvar("g_pickup_healthmega_max"); + g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway"); + + g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway"); + g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway"); + + g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay")); + if(!g_weapon_stay) + g_weapon_stay = cvar("g_weapon_stay"); + + if (!warmup_stage) + game_starttime = time + cvar("g_start_delay"); + + for(int i = WEP_FIRST; i <= WEP_LAST; ++i) + WEP_ACTION(i, WR_INIT); + + readplayerstartcvars(); + } + + //#NO AUTOCVARS END + + + // Sound functions + //string precache_sound (string s) = #19; + // hack + float precache_sound_index (string s) = #19; + + const float SND_VOLUME = 1; + const float SND_ATTENUATION = 2; + const float SND_LARGEENTITY = 8; + const float SND_LARGESOUND = 16; + + // WARNING: this kills the trace globals + #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return + #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init() + + const float INITPRIO_FIRST = 0; + const float INITPRIO_GAMETYPE = 0; + const float INITPRIO_GAMETYPE_FALLBACK = 1; + const float INITPRIO_FINDTARGET = 10; + const float INITPRIO_DROPTOFLOOR = 20; + const float INITPRIO_SETLOCATION = 90; + const float INITPRIO_LINKDOORS = 91; + const float INITPRIO_LAST = 99; + + .void(void) initialize_entity; + .float initialize_entity_order; + .entity initialize_entity_next; + entity initialize_entity_first; + + + + + + float sound_allowed(float dest, entity e); + void InitializeEntity(entity e, void(void) func, float order); + void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer); + void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc); + + #endif diff --cc qcsrc/server/mutators/gamemode_assault.qc index 8a8c50b38,49deac0e8..de0932263 --- a/qcsrc/server/mutators/gamemode_assault.qc +++ b/qcsrc/server/mutators/gamemode_assault.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/triggers/subs.qh" ++ // random functions void assault_objective_use() { diff --cc qcsrc/server/mutators/gamemode_domination.qc index 8e4d929be,dd6e8b210..314221a19 --- a/qcsrc/server/mutators/gamemode_domination.qc +++ b/qcsrc/server/mutators/gamemode_domination.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/triggers/subs.qh" ++ void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later { if(autocvar_sv_eventlog) diff --cc qcsrc/server/mutators/gamemode_onslaught.qc index 74cba2897,d1f20f7ee..93ed9f7d8 --- a/qcsrc/server/mutators/gamemode_onslaught.qc +++ b/qcsrc/server/mutators/gamemode_onslaught.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/triggers/subs.qh" ++ float autocvar_g_onslaught_spawn_at_controlpoints; float autocvar_g_onslaught_spawn_at_generator; float autocvar_g_onslaught_cp_proxydecap; diff --cc qcsrc/server/mutators/mutator_buffs.qc index 765619df7,b2f800a27..942c96513 --- a/qcsrc/server/mutators/mutator_buffs.qc +++ b/qcsrc/server/mutators/mutator_buffs.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/triggers/target/music.qh" ++ float buffs_BuffModel_Customize() { entity player, myowner; diff --cc qcsrc/server/mutators/mutator_nades.qc index 91bd53e5a,3932ce407..dc1b6bc3b --- a/qcsrc/server/mutators/mutator_nades.qc +++ b/qcsrc/server/mutators/mutator_nades.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/triggers/target/music.qh" ++ .entity nade_spawnloc; void nade_timer_think() diff --cc qcsrc/server/mutators/mutators_include.qc index 0f52e34f0,a0170e480..c3bdec50c --- a/qcsrc/server/mutators/mutators_include.qc +++ b/qcsrc/server/mutators/mutators_include.qc @@@ -1,3 -1,85 +1,84 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/anglestransform.qh" + #include "../../warpzonelib/mathlib.qh" + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/util_server.qh" + #include "../../warpzonelib/server.qh" + #include "../../common/constants.qh" + #include "../../common/stats.qh" + #include "../../common/teams.qh" + #include "../../common/util.qh" + #include "../../common/nades.qh" + #include "../../common/buffs.qh" + #include "../../common/test.qh" + #include "../../common/counting.qh" + #include "../../common/urllib.qh" + #include "../../common/command/markup.qh" + #include "../../common/command/rpn.qh" + #include "../../common/command/generic.qh" + #include "../../common/command/shared_defs.qh" + #include "../../common/net_notice.qh" + #include "../../common/animdecide.qh" + #include "../../common/monsters/monsters.qh" + #include "../../common/monsters/sv_monsters.qh" + #include "../../common/monsters/spawn.qh" + #include "../../common/weapons/config.qh" + #include "../../common/weapons/weapons.qh" + #include "../weapons/accuracy.qh" + #include "../weapons/common.qh" + #include "../weapons/csqcprojectile.qh" + #include "../weapons/hitplot.qh" + #include "../weapons/selection.qh" + #include "../weapons/spawning.qh" + #include "../weapons/throwing.qh" + #include "../weapons/tracing.qh" + #include "../weapons/weaponstats.qh" + #include "../weapons/weaponsystem.qh" + #include "../t_items.qh" + #include "../autocvars.qh" + #include "../constants.qh" + #include "../defs.qh" + #include "../../common/notifications.qh" + #include "../../common/deathtypes.qh" + #include "mutators_include.qh" + #include "../tturrets/include/turrets_early.qh" + #include "../vehicles/vehicles_def.qh" + #include "../campaign.qh" + #include "../../common/campaign_common.qh" + #include "../../common/mapinfo.qh" + #include "../command/common.qh" + #include "../command/banning.qh" + #include "../command/radarmap.qh" + #include "../command/vote.qh" + #include "../command/getreplies.qh" + #include "../command/cmd.qh" + #include "../command/sv_cmd.qh" + #include "../../common/csqcmodel_settings.qh" + #include "../../csqcmodellib/common.qh" + #include "../../csqcmodellib/sv_model.qh" + #include "../anticheat.qh" + #include "../cheats.qh" + #include "../../common/playerstats.qh" + #include "../portals.qh" + #include "../g_hook.qh" + #include "../scores.qh" + #include "../spawnpoints.qh" + #include "../mapvoting.qh" + #include "../ipban.qh" + #include "../race.qh" + #include "../antilag.qh" + #include "../playerdemo.qh" + #include "../round_handler.qh" + #include "../item_key.qh" - #include "../secret.qh" + #include "../pathlib/pathlib.qh" + #include "../tturrets/include/turrets.qh" + #include "../vehicles/vehicles.qh" + #endif + #include "base.qc" #include "gamemode_assault.qc" #include "gamemode_ca.qc" diff --cc qcsrc/server/progs.src index 8da8a6fc5,6d4c76b1b..c45e3cb6a --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@@ -2,153 -2,75 +2,67 @@@ ../common/util-pre.qh sys-pre.qh - ../dpdefs/progsdefs.qc - ../dpdefs/dpextensions.qc + ../dpdefs/progsdefs.qh + ../dpdefs/dpextensions.qh sys-post.qh - ../warpzonelib/anglestransform.qh - ../warpzonelib/mathlib.qh - ../warpzonelib/common.qh - ../warpzonelib/util_server.qh - ../warpzonelib/server.qh - ../common/constants.qh - ../common/stats.qh - ../common/teams.qh - ../common/util.qh - ../common/nades.qh - ../common/buffs.qh - ../common/test.qh - ../common/counting.qh - ../common/urllib.qh - ../common/command/markup.qh - ../common/command/rpn.qh - ../common/command/generic.qh - ../common/command/shared_defs.qh - ../common/net_notice.qh - ../common/animdecide.qh - ../common/monsters/monsters.qh - ../common/monsters/sv_monsters.qh - ../common/monsters/spawn.qh - - ../common/weapons/config.qh - ../common/weapons/weapons.qh // TODO - weapons/accuracy.qh - weapons/common.qh - weapons/csqcprojectile.qh // TODO - weapons/hitplot.qh - weapons/selection.qh - weapons/spawning.qh - weapons/throwing.qh - weapons/tracing.qh - weapons/weaponstats.qh - weapons/weaponsystem.qh - - t_items.qh - - autocvars.qh - constants.qh - defs.qh // Should rename this, it has fields and globals - - ../common/notifications.qh // must be after autocvars - ../common/deathtypes.qh // must be after notifications - - mutators/mutators_include.qh - - //// tZork Turrets //// - tturrets/include/turrets_early.qh - vehicles/vehicles_def.qh - - campaign.qh - ../common/campaign_common.qh - ../common/mapinfo.qh - - command/common.qh - command/banning.qh - command/radarmap.qh - command/vote.qh - command/getreplies.qh - command/cmd.qh - command/sv_cmd.qh - - - ../common/csqcmodel_settings.qh - ../csqcmodellib/common.qh - ../csqcmodellib/sv_model.qh + anticheat.qc + antilag.qc + // assault.qc + campaign.qc + cheats.qc + cl_client.qc + cl_impulse.qc + cl_player.qc csqceffects.qc - - anticheat.qh - cheats.qh - ../common/playerstats.qh - - ../common/triggers/include.qh - - portals.qh - - g_hook.qh // TODO - - scores.qh - - spawnpoints.qh - - mapvoting.qh - - ipban.qh - - race.qh - - antilag.qh - - playerdemo.qh - - round_handler.qh - - // singleplayer stuff - item_key.qh - - scores_rules.qc - - miscfunctions.qc - - mutators/mutators.qc - - waypointsprites.qc - - bot/bot.qc - + // ctf.qc + // domination.qc + ent_cs.qc -func_breakable.qc + g_casings.qc + g_damage.qc + g_hook.qc + g_models.qc g_subs.qc - g_tetris.qc - - //runematch.qc - -g_triggers.qc g_violence.qc - g_damage.qc - - teamplay.qc - - ../common/physics.qh - ../common/physics.qc - - // tZork's libs - movelib.qc - steerlib.qc - pathlib/pathlib.qh - g_world.qc - g_casings.qc - + ipban.qc + item_key.qc mapvoting.qc - - ../common/triggers/include.qc - + miscfunctions.qc + // mode_onslaught.qc + movelib.qc + // nexball.qc + playerdemo.qc + portals.qc + race.qc + round_handler.qc + // runematch.qc + scores.qc + scores_rules.qc -secret.qc + spawnpoints.qc + steerlib.qc + sv_main.qc -target_music.qc -target_spawn.qc + teamplay.qc + t_halflife.qc + t_items.qc -t_jumppads.qc -t_plats.qc + t_quake3.qc + t_quake.qc -t_swamp.qc t_teleporters.qc + waypointsprites.qc - sv_main.qc + bot/bot.qc - g_models.qc + command/banning.qc + command/cmd.qc + command/common.qc + command/getreplies.qc + command/radarmap.qc + command/sv_cmd.qc + command/vote.qc - // singleplayer stuff - item_key.qc + mutators/mutators_include.qc + mutators/mutators.qc weapons/accuracy.qc weapons/common.qc @@@ -160,92 -82,33 +74,34 @@@ weapons/throwing.q weapons/tracing.qc weapons/weaponstats.qc weapons/weaponsystem.qc - ../common/weapons/config.qc - ../common/weapons/weapons.qc // TODO - t_items.qc - cl_impulse.qc - - ent_cs.qc - - cl_player.qc - cl_client.qc - antilag.qc - - //ctf.qc - //domination.qc - //mode_onslaught.qc - //nexball.qc - g_hook.qc - - campaign.qc + ../common/animdecide.qc + ../common/buffs.qc ../common/campaign_file.qc ../common/campaign_setup.qc - ../common/urllib.qc - + ../common/command/generic.qc ../common/command/markup.qc ../common/command/rpn.qc - ../common/command/generic.qc - ../common/net_notice.qc - - command/common.qc - command/banning.qc - command/radarmap.qc - command/vote.qc - command/getreplies.qc - command/cmd.qc - command/sv_cmd.qc - - //assault.qc - - ipban.qc - ../common/mapinfo.qc - - t_quake3.qc - t_halflife.qc - t_quake.qc - - race.qc - - - //// tZork Turrets //// - tturrets/include/turrets.qh - vehicles/vehicles.qh - - scores.qc - - spawnpoints.qc - - portals.qc - - ../common/nades.qc - ../common/buffs.qc - - ../csqcmodellib/sv_model.qc - - playerdemo.qc - - anticheat.qc - cheats.qc - ../common/playerstats.qc - - round_handler.qc - - ../common/monsters/sv_monsters.qc ../common/monsters/monsters.qc - ../common/monsters/spawn.qc + ../common/monsters/sv_monsters.qc + ../common/nades.qc + ../common/net_notice.qc + ../common/notifications.qc + ../common/physics.qc + ../common/playerstats.qc + ../common/test.qc ++../common/triggers/include.qc + ../common/urllib.qc + ../common/util.qc + ../common/weapons/config.qc + ../common/weapons/weapons.qc // TODO - mutators/mutators_include.qc + ../csqcmodellib/sv_model.qc ../warpzonelib/anglestransform.qc - ../warpzonelib/mathlib.qc ../warpzonelib/common.qc - ../warpzonelib/util_server.qc + ../warpzonelib/mathlib.qc ../warpzonelib/server.qc - - ../common/animdecide.qc - ../common/test.qc - ../common/util.qc - ../common/notifications.qc + ../warpzonelib/util_server.qc diff --cc qcsrc/server/race.qc index 1c12058d0,bb7f0eccc..a42d24034 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@@ -1,3 -1,7 +1,8 @@@ + #include "race.qh" ++#include "../common/triggers/subs.qh" + + void W_Porto_Fail(float failhard); + float race_readTime(string map, float pos) { string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; diff --cc qcsrc/server/spawnpoints.qc index 3f4e72c3b,253c70fbe..aa5b09e1e --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@@ -1,3 -1,20 +1,21 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/util_server.qh" + #include "../common/constants.qh" + #include "../common/teams.qh" ++ #include "../common/triggers/subs.qh" + #include "../common/util.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "mutators/mutators_include.qh" + #include "spawnpoints.qh" + #include "race.qh" + #endif + float SpawnPoint_Send(entity to, float sf) { WriteByte(MSG_ENTITY, ENT_CLIENT_SPAWNPOINT); diff --cc qcsrc/server/t_items.qc index 4455d3fb4,03af802e7..ba0ee4410 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@@ -1,3 -1,33 +1,34 @@@ + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "../common/util.qh" + #include "../common/buffs.qh" + #include "../common/weapons/weapons.qh" + #include "../client/autocvars.qh" + #include "../client/movetypes.qh" + #include "../client/main.qh" + #include "../csqcmodellib/common.qh" + #include "../csqcmodellib/cl_model.qh" + #include "t_items.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/util_server.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/monsters/monsters.qh" ++ #include "../common/triggers/subs.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/weaponsystem.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" + #endif + #ifdef CSQC void ItemDraw() { diff --cc qcsrc/server/t_jumppads.qh index 000000000,36f203b9e..48f4d5cab mode 000000,100644..100644 --- a/qcsrc/server/t_jumppads.qh +++ b/qcsrc/server/t_jumppads.qh @@@ -1,0 -1,72 +1,71 @@@ + #ifndef T_JUMPPADS_H + #define T_JUMPPADS_H + + const float PUSH_ONCE = 1; + const float PUSH_SILENT = 2; + + .float pushltime; + .float istypefrag; + .float height; + + + float trigger_push_calculatevelocity_flighttime; + + #ifdef SVQC + void() SUB_UseTargets; + void trigger_push_use(); + #endif + + #ifdef CSQC + .float active; + .string target; + .string targetname; + + const int ACTIVE_NOT = 0; + const int ACTIVE_ACTIVE = 1; + const int ACTIVE_IDLE = 2; + const int ACTIVE_BUSY = 2; + const int ACTIVE_TOGGLE = 3; -void trigger_push_draw(); + #endif + + /* + trigger_push_calculatevelocity + + Arguments: + org - origin of the object which is to be pushed + tgt - target entity (can be either a point or a model entity; if it is + the latter, its midpoint is used) + ht - jump height, measured from the higher one of org and tgt's midpoint + + Returns: velocity for the jump + the global trigger_push_calculatevelocity_flighttime is set to the total + jump time + */ + + vector trigger_push_calculatevelocity(vector org, entity tgt, float ht); + + void trigger_push_touch(); + + .vector dest; + void trigger_push_findtarget(); + + /* + * ENTITY PARAMETERS: + * + * target: target of jump + * height: the absolute value is the height of the highest point of the jump + * trajectory above the higher one of the player and the target. + * the sign indicates whether the highest point is INSIDE (positive) + * or OUTSIDE (negative) of the jump trajectory. General rule: use + * positive values for targets mounted on the floor, and use negative + * values to target a point on the ceiling. + * movedir: if target is not set, this * speed * 10 is the velocity to be reached. + */ + #ifdef SVQC + void spawnfunc_trigger_push(); + + void spawnfunc_target_push(); + void spawnfunc_info_notnull(); + void spawnfunc_target_position(); + #endif + #endif diff --cc qcsrc/server/t_teleporters.qc index 543c1cf0b,46df0eb78..38d35bb03 --- a/qcsrc/server/t_teleporters.qc +++ b/qcsrc/server/t_teleporters.qc @@@ -1,3 -1,24 +1,25 @@@ + #include "t_teleporters.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../warpzonelib/common.qh" + #include "../warpzonelib/util_server.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" ++ #include "../common/triggers/subs.qh" + #include "../common/util.qh" + #include "weapons/csqcprojectile.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/deathtypes.qh" + #include "tturrets/include/turrets_early.qh" + #include "vehicles/vehicles_def.qh" + #include "../common/mapinfo.qh" + #include "anticheat.qh" + #endif + void trigger_teleport_use() { if(teamplay) diff --cc qcsrc/server/tturrets/system/system_main.qc index d56a81bbf,f2b0c56ce..4c3a52324 --- a/qcsrc/server/tturrets/system/system_main.qc +++ b/qcsrc/server/tturrets/system/system_main.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../../common/triggers/subs.qh" ++ #define cvar_base "g_turrets_unit_" .float clientframe; void turrets_setframe(float _frame, float client_only) diff --cc qcsrc/server/vehicles/racer.qc index fc9cee865,87dfe52a7..06760b09f --- a/qcsrc/server/vehicles/racer.qc +++ b/qcsrc/server/vehicles/racer.qc @@@ -1,7 -1,7 +1,9 @@@ - #define RACER_MIN '-120 -120 -40' - #define RACER_MAX '120 120 40' + const vector RACER_MIN = '-120 -120 -40'; + const vector RACER_MAX = '120 120 40'; #ifdef SVQC ++#include "../../common/triggers/trigger/impulse.qh" ++ void racer_exit(float eject); void racer_enter(); diff --cc qcsrc/server/vehicles/vehicles.qc index 785d39516,96d54e3de..5f639a880 --- a/qcsrc/server/vehicles/vehicles.qc +++ b/qcsrc/server/vehicles/vehicles.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/triggers/subs.qh" ++ float autocvar_g_vehicles_crush_dmg; float autocvar_g_vehicles_crush_force; float autocvar_g_vehicles_delayspawn; diff --cc qcsrc/warpzonelib/common.qc index 16e026d48,6481242dc..a6744f702 --- a/qcsrc/warpzonelib/common.qc +++ b/qcsrc/warpzonelib/common.qc @@@ -564,45 -573,6 +573,45 @@@ vector WarpZoneLib_NearestPointOnBox(ve return nearest; } +float WarpZoneLib_BadClassname(string myclassname) +{ + switch(myclassname) + { + case "weapon_info": + case "monster_info": + case "deathtype": + case "callback": + case "callbackchain": + case "weaponentity": + case "exteriorweaponentity": + case "csqc_score_team": + case "pingplreport": + case "ent_client_scoreinfo": + case "saved_cvar_value": + case "accuracy": + case "entcs_sender_v2": + case "entcs_receiver_v2": + case "clientinit": + case "sprite_waypoint": + case "waypoint": + case "gibsplash": + //case "net_linked": // actually some real entities are linked without classname, fail + case "": - return TRUE; ++ return true; + } + + if(startsWith(myclassname, "msg_")) - return TRUE; ++ return true; + + if(startsWith(myclassname, "target_")) - return TRUE; ++ return true; + + if(startsWith(myclassname, "info_")) - return TRUE; ++ return true; + - return FALSE; ++ return false; +} + .float WarpZone_findradius_hit; .entity WarpZone_findradius_next; void WarpZone_FindRadius_Recurse(vector org, float rad, vector org0, vector transform, vector shift, float needlineofsight) diff --cc qcsrc/warpzonelib/server.qc index d25c769b0,5d529c12e..8e25b983f --- a/qcsrc/warpzonelib/server.qc +++ b/qcsrc/warpzonelib/server.qc @@@ -1,3 -1,17 +1,18 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "common.qh" + #include "server.qh" + #include "../common/constants.qh" ++ #include "../common/triggers/subs.qh" + #include "../common/util.qh" + #include "../server/constants.qh" + #include "../server/defs.qh" + #include "../server/command/common.qh" + #endif + #ifdef WARPZONELIB_KEEPDEBUG #define WARPZONELIB_REMOVEHACK #endif