Initial contrib from blue24
This commit is contained in:
commit
8f453b9e1d
180 changed files with 38188 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.pk3
|
5
src/Makefile
Normal file
5
src/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
CC=fteqcc
|
||||
|
||||
all:
|
||||
cd client && $(MAKE)
|
||||
cd server && $(MAKE)
|
4
src/client/Makefile
Normal file
4
src/client/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
CC=fteqcc
|
||||
|
||||
all:
|
||||
$(CC) progs.src
|
57
src/client/clientinfo.h
Normal file
57
src/client/clientinfo.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
// NEW FILE
|
||||
// Handles things that need to be persistent, or at least accessible, through
|
||||
// the spectator and player - class changes.
|
||||
// In English please?
|
||||
// Available whether the player is in Spectator mode or ingame
|
||||
// (collision, interacting seen by other players, etc.)
|
||||
// WARNING: included before the gamemod's client defs.h file. Beware if anything
|
||||
// important is missing from here (context) because of that.
|
||||
|
||||
|
||||
typedef struct ClientInfo_s{
|
||||
|
||||
// Anyways, this object will store data related to weapon purchases temporarily
|
||||
// (tempConfig).
|
||||
// When a purchase is finallized, tempConfig will replace currentConfig
|
||||
// (copy it over).
|
||||
// But if "cancel" is used from the 1st layer of shop buttons after purchases
|
||||
// (edits to tempConfig), it is all scrapped. tempConfig will be restored to
|
||||
// currentCofig instead.
|
||||
// currentConfig will be used to build the player's inventory at spawn.
|
||||
weaponconfig_data_t weaponconfig_current;
|
||||
weaponconfig_data_t weaponconfig_temp;
|
||||
|
||||
//TAGGG - CRITICAL!
|
||||
// At some point, it may be wise to store these too, remove from the player class
|
||||
// after enabling here.
|
||||
// Also the flMoney var here is just "money" in player, for removal.
|
||||
// And this will probably mean replacing 'clientstat(... STAT_MONEY)' with something
|
||||
// that sends a message to the connected client (SVC_CGAMEPACKET) anytime a
|
||||
// persistent money value (a serverside clientdata?) is changed. This avoids
|
||||
// saving to the player/spectator entity, which may be odd memory to use when both
|
||||
// player and spectator need to refer to how much money the 'client' has, despite
|
||||
// being different entities.
|
||||
// Or maybe it all still works somehow with 0 side-effects.
|
||||
// -----------------------------------------------------------
|
||||
/*
|
||||
// How much money does this player/spectator have? Can't spend more than this.
|
||||
// A var for slots would be needed if this varried between players, number of slots
|
||||
// used is for after having spawned (player-only) so it doesn't need to be here.
|
||||
float flMoney;
|
||||
|
||||
// Provided for quick reference. How many slots does the current loadout take?
|
||||
// There is a record of how much money has been spent, similar to config, but that
|
||||
// isn't too important.
|
||||
// The attached "money" (variable) is much more important (only pay attention to it
|
||||
// serverside, fetch it from clientside: getstati(...) or something.
|
||||
int iTotalSlots;
|
||||
int iTotalPrice;
|
||||
*/
|
||||
// -----------------------------------------------------------
|
||||
|
||||
|
||||
} ClientInfo_t;
|
||||
|
||||
|
||||
void ClientInfo_init(ClientInfo_t* arg_this);
|
13
src/client/clientinfo.qc
Normal file
13
src/client/clientinfo.qc
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
|
||||
// pSeat and pSeatLocal can be trusted as they are, they're set appropriately in init.qc
|
||||
// right before this call for each.
|
||||
void ClientInfo_init(ClientInfo_t* arg_this){
|
||||
|
||||
// weaponconfig_current = spawn(weaponconfig_data_t);
|
||||
// weaponconfig_temp = spawn(weaponconfig_data_t);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
121
src/client/cmds.qc
Normal file
121
src/client/cmds.qc
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
int
|
||||
ClientGame_ConsoleCommand(void)
|
||||
{
|
||||
|
||||
//printfline("ClientGame_ConsoleCommand: %s", argv(0));
|
||||
|
||||
switch(argv(0)) {
|
||||
|
||||
//TAGGG - is that ok?
|
||||
case "+speedcustom":
|
||||
pSeatLocal->iInputSpeed = TRUE;
|
||||
break;
|
||||
case "-speedcustom":
|
||||
pSeatLocal->iInputSpeed = FALSE;
|
||||
break;
|
||||
|
||||
//TAGGG - NEW ONES
|
||||
case "getorigin":
|
||||
sendevent("TS_Debug_getOrigin", "");
|
||||
break;
|
||||
case "getangle":
|
||||
sendevent("TS_Debug_getAngle", "");
|
||||
break;
|
||||
case "firemode":
|
||||
TS_playerChangeFiremode();
|
||||
break;
|
||||
case "useitems":
|
||||
TS_playerUseItems();
|
||||
break;
|
||||
case "usepowerup":
|
||||
TS_playerUsePowerup();
|
||||
break;
|
||||
// I think the plus is good here then?
|
||||
// ...wait, not continuously reacted to in original TS. I have no idea.
|
||||
// Oh well, catch the - anyway too to stop an annoying printout about that command being missing (release)
|
||||
case "+alt1":
|
||||
TS_playerCallAlt1();
|
||||
break;
|
||||
case "-alt1":
|
||||
|
||||
break;
|
||||
//TAGGG - TODO: low priority.
|
||||
// Let holding down the coldcock key (C by default) continuously use it, just like
|
||||
// holding down primary fire to continuously fire. Something about setting a var
|
||||
// to represent being held down or not for frame-by-frame logic to check for being on, then
|
||||
// do the 'sendevent'. I think?
|
||||
case "+alt2":
|
||||
TS_playerCallAlt2();
|
||||
//pSeat->m_iInputAlt2 = TRUE;
|
||||
break;
|
||||
case "-alt2":
|
||||
//pSeat->m_iInputAlt2 = FALSE;
|
||||
break;
|
||||
|
||||
case "dev_testorbituary":
|
||||
//TAGGG - CRITICAL. Orbituary stuff...
|
||||
//HUD_AddOrbituaries(player_localnum, TEAM_T, player_localnum, TEAM_CT, floor(random(1, CS_WEAPON_COUNT)), FALSE);
|
||||
break;
|
||||
|
||||
// CUT.
|
||||
/*
|
||||
case "minimap":
|
||||
pSeat.iMapExpand = 1 - pSeat.iMapExpand;
|
||||
break;
|
||||
case "overview_test":
|
||||
pSeat.iOverview = 1 - pSeat.iOverview;
|
||||
break;
|
||||
*/
|
||||
case "motd":
|
||||
if(getplayerkeyvalue( player_localnum, "*spec" ) != "0"){
|
||||
VGUI_ChangeScreen(VGUI_SCREEN::MOTD);
|
||||
}
|
||||
break;
|
||||
case "buy":
|
||||
//if(getstatf(STAT_BUYZONE) == TRUE) {
|
||||
// VGUI_BuyMenu();
|
||||
//}
|
||||
|
||||
//If we're in spectator mode we can do this
|
||||
// no-screen check, not necessary probably: pSeatLocal->fVGUI_Display == VGUI_SCREEN::NONE &&
|
||||
if(getplayerkeyvalue( player_localnum, "*spec" ) != "0"){
|
||||
//we can show it!
|
||||
VGUI_ChangeScreen(VGUI_SCREEN::BUYSIDEMENU);
|
||||
}
|
||||
|
||||
break;
|
||||
case "chooseteam":
|
||||
//VGUI_ChooseTeam();
|
||||
break;
|
||||
|
||||
//TAGGG - CRITICAL
|
||||
// See CSEv_DropWeapon of src/server/weapons.qc, tie into that better
|
||||
// somehow, may need to rely on Nuclide's own pickup type instead of our own.
|
||||
|
||||
case "drop":
|
||||
//sendevent("WeaponDrop", "");
|
||||
TS_playerDropWeapon(); //I do it fine.
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
101
src/client/defs.h
Normal file
101
src/client/defs.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "obituary.h"
|
||||
#include "particles.h"
|
||||
|
||||
|
||||
//TAGGG - VERY LITTLE USES "hud_color" RIGHT NOW!
|
||||
// See inventory_logic_draw.qc for places that benefit from involving it, compare to behavior
|
||||
// in original TS to see what it affects.
|
||||
vector g_hud_color;
|
||||
vector g_hudmins;
|
||||
vector g_hudres;
|
||||
|
||||
|
||||
/*
|
||||
// Do we need these? Note the similarly named "g_hud_color" already above!
|
||||
// Going to keep the latter two for compatability.
|
||||
// Yes using old VGUI is kinda crappy but good for now.
|
||||
vector vHUDColor; // Defined in HUD_Draw (HUD.c)
|
||||
*/
|
||||
vector vVGUIColor; // Defined in HUD_Draw (VGUI.c)
|
||||
vector vCrossColor; // Defined in HUD_Draw (HUDCrosshair.c)
|
||||
|
||||
//TAGGG INCLUSION - new. Global var to store the current screen size. If it changes, the font should be re-loaded to fit it.
|
||||
var vector Recorded_video_res;
|
||||
|
||||
//TAGGG - INCLUSION. eplaces several cases of FONT_CON to be able to draw larger text (well).
|
||||
var float FONT_ARIAL;
|
||||
var float FONT_ARIAL_TITLE;
|
||||
var float FONT_ARIAL_STD; //stands for "standard" ya hooligans.
|
||||
var float FONT_ARIAL_NUMSLASH;
|
||||
var float FONT_ARIAL_SCOPEMAG;
|
||||
|
||||
// Be sure to keep this up to date with the font FONT_ARIAL_STD as it's loaded in _base/client/entry.c,
|
||||
// notice this is always one less than the actual expected corresponding font.
|
||||
const vector vButtonFontSize = [13, 13, 0];
|
||||
const vector vFontSizeNumSlash = [15, 15, 0];
|
||||
const vector vFontArialScopeMag = [16, 16, 0];
|
||||
|
||||
const vector vButtonSizStandard = [127, 19, 0];
|
||||
|
||||
const vector clrBtnDefault = [0, 0, 0];
|
||||
const vector clrPaleBluePurple = [174.0f/255.0f, 152.0f/255.0f, 255.0f/255.0f];
|
||||
|
||||
//const vector clrFailedSlotbarColor = [240.0f/255.0f, 116.0f/255.0f, 0/255.0f];
|
||||
const vector clrFailedSlotbarColor = [255.0f/255.0f, 90.0f/255.0f, 100.0f/255.0f];
|
||||
|
||||
const vector clrHUDWeaponEmpty = [255.0f/255.0f, 75f/255.0f, 75f/255.0f];
|
||||
|
||||
|
||||
// wait, for the default way the GUI colors work (hud_r, hud_g, hud_b) be 150, 150, 255 then?
|
||||
// TODO - test that later.
|
||||
const vector clrPaleBlue = [180.0f/255.0f, 195.0f/255.0f, 255.0f/255.0f];
|
||||
const vector clrMedRed = [255.0f/255.0f, 56.0f/255.0f, 56.0f/255.0f];
|
||||
|
||||
const vector clrGreen = [0f/255.0f, 255.0f/255.0f, 0f/255.0f];
|
||||
const vector clrRed = [255.0f/255.0f, 0f/255.0f, 0f/255.0f];
|
||||
|
||||
const vector clrLaserHUDDot = [255.0f/255.0f, 35.0f/255.0f, 35.0f/255.0f];
|
||||
|
||||
|
||||
// !!!
|
||||
// g_seatslocal AND pSeatLocal definition moved to seatlocal.h
|
||||
|
||||
void HUD_WeaponPickupNotify(int);
|
||||
|
||||
|
||||
// CRITICAL:
|
||||
// Should these be per pSeat instead? Unsure if that makes sense.
|
||||
var float TS_keyRefTapped = 0;
|
||||
var float TS_keyRefUp = 0;
|
||||
var float TS_keyRefUpASCII = 0;
|
||||
var float TS_keyRefDown = 0;
|
||||
var float TS_mouseClickRef = 0;
|
||||
|
||||
|
||||
|
||||
//TAGGG - NEW.
|
||||
// The "fov" CVar is set to this at startup. This must be changed to be persistent
|
||||
// bewteen games.
|
||||
var float autocvar_fov_default = 80.0f;
|
||||
|
||||
//TAGGG - TODO. Is this even used yet?
|
||||
var int autocvar_cl_autoweaponswitch = TRUE;
|
||||
|
||||
|
||||
|
146
src/client/draw.qc
Normal file
146
src/client/draw.qc
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// For pain arrows, see Nuclide's "Damage_Draw" method. That's called for in HUD_Draw
|
||||
// oddly enough, not these event-driven draw methods.
|
||||
|
||||
// Also, View_PostDraw is called even before HUD_DRAW, although a draw call early on
|
||||
// in that method, above the rest should have the same effect anyway.
|
||||
|
||||
|
||||
extern var string g_damage_spr_t;
|
||||
extern var string g_damage_spr_b;
|
||||
extern var string g_damage_spr_l;
|
||||
extern var string g_damage_spr_r;
|
||||
|
||||
|
||||
void drawPainArrows(void);
|
||||
void drawPainFlash(void);
|
||||
void ClientGame_DamageDraw(void);
|
||||
|
||||
|
||||
|
||||
void
|
||||
ClientGame_PreDraw(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ClientGame_PostDraw(void)
|
||||
{
|
||||
ClientGame_DamageDraw();
|
||||
}
|
||||
|
||||
|
||||
// copied from Nuclide
|
||||
void
|
||||
drawPainArrows(void)
|
||||
{
|
||||
vector center;
|
||||
vector rel_pos;
|
||||
float fw, fw_alpha;
|
||||
float rt, rt_alpha;
|
||||
|
||||
if (pSeat->m_flDamageAlpha <= 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
center = video_mins + (video_res / 2);
|
||||
|
||||
/* the pos relative to the player + view_dir determines which
|
||||
* and how bright each indicator is drawn. so first get the relative
|
||||
* position between us and the attacker, then calculate the strength
|
||||
* of each direction based on a dotproduct tested against our
|
||||
* camera direction.
|
||||
*/
|
||||
rel_pos = normalize(pSeat->m_vecDamagePos - getproperty(VF_ORIGIN));
|
||||
makevectors(getproperty(VF_CL_VIEWANGLES));
|
||||
fw = dotproduct(rel_pos, v_forward);
|
||||
rt = dotproduct(rel_pos, v_right);
|
||||
|
||||
fw_alpha = fabs(fw) * pSeat->m_flDamageAlpha;
|
||||
if (fw > 0.25f) {
|
||||
drawpic(center + [-64,-102], g_damage_spr_t,
|
||||
[128,48], [1,1,1], fw_alpha, DRAWFLAG_ADDITIVE);
|
||||
} else if (fw < -0.25f) {
|
||||
drawpic(center + [-64,70], g_damage_spr_b,
|
||||
[128,48], [1,1,1], fw_alpha, DRAWFLAG_ADDITIVE);
|
||||
}
|
||||
|
||||
rt_alpha = fabs(rt) * pSeat->m_flDamageAlpha;
|
||||
if (rt > 0.25f) {
|
||||
drawpic(center + [70,-64], g_damage_spr_r,
|
||||
[48,128], [1,1,1], rt_alpha, DRAWFLAG_ADDITIVE);
|
||||
} else if (rt < -0.25f) {
|
||||
drawpic(center + [-102,-64], g_damage_spr_l,
|
||||
[48,128], [1,1,1], rt_alpha, DRAWFLAG_ADDITIVE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// and now for the screen-wide pain flash.
|
||||
void
|
||||
drawPainFlash(void)
|
||||
{
|
||||
|
||||
//drawfill( video_mins, video_res, clrRed, VGUI_WINDOW_FGALPHA );
|
||||
//drawfill( video_mins, video_res, clrRed, arg_opac - 0.60f, DRAWFLAG_NORMAL );
|
||||
|
||||
float drawAlpha;
|
||||
float filteredAlpha = pSeat->m_flDamageAlpha * 0.49;
|
||||
|
||||
if(filteredAlpha > 0.32){
|
||||
drawAlpha = 0.32;
|
||||
}else{
|
||||
drawAlpha = filteredAlpha;
|
||||
}
|
||||
|
||||
drawfill( video_mins, video_res, clrRed, drawAlpha, DRAWFLAG_NORMAL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ClientGame_DamageDraw(void){
|
||||
|
||||
if (pSeat->m_flDamageAlpha <= 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawPainArrows();
|
||||
drawPainFlash();
|
||||
|
||||
// Nuclide's default had no modifier on clframetime ( * 1).
|
||||
pSeat->m_flDamageAlpha -= clframetime * 1.7;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
37
src/client/entities.qc
Normal file
37
src/client/entities.qc
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
int
|
||||
ClientGame_EntityUpdate(float id, float new)
|
||||
{
|
||||
switch (id) {
|
||||
|
||||
case ENT_POWERUP:
|
||||
Powerup_Parse();
|
||||
break;
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
ClientGame_EntityRemove(void)
|
||||
{
|
||||
if (self.classname == "player")
|
||||
Player_DestroyWeaponModel((base_player) self);
|
||||
}
|
211
src/client/game_event.qc
Normal file
211
src/client/game_event.qc
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void
|
||||
ClientGame_EventParse(float fHeader)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
vector vecOrigin;
|
||||
|
||||
int iSequence;
|
||||
float fDuration;
|
||||
float fIdleEndOffset;
|
||||
|
||||
int iType;
|
||||
|
||||
switch (fHeader) {
|
||||
|
||||
case EVENT_TS::PLAYER_DEATH:
|
||||
// Require a tiny amount of time and a mouse release before a respawn, so that dying
|
||||
// with the mouse held down isn't enough to trigger a respawn request.
|
||||
pSeatLocal->m_bNeedPrimaryRelease = TRUE;
|
||||
pSeatLocal->m_flReleaseTime = time + 0.15;
|
||||
break;
|
||||
|
||||
case EV_OBITUARY:
|
||||
Obituary_Parse();
|
||||
break;
|
||||
case EV_SPARK:
|
||||
vector vSparkPos, vSparkAngle;
|
||||
vSparkPos[0] = readcoord();
|
||||
vSparkPos[1] = readcoord();
|
||||
vSparkPos[2] = readcoord();
|
||||
vSparkAngle[0] = readcoord();
|
||||
vSparkAngle[1] = readcoord();
|
||||
vSparkAngle[2] = readcoord();
|
||||
FX_Spark(vSparkPos, vSparkAngle);
|
||||
break;
|
||||
case EV_GIBHUMAN:
|
||||
vector vGibPos;
|
||||
vGibPos[0] = readcoord();
|
||||
vGibPos[1] = readcoord();
|
||||
vGibPos[2] = readcoord();
|
||||
FX_GibHuman(vGibPos);
|
||||
break;
|
||||
case EV_BLOOD:
|
||||
vector vBloodPos;
|
||||
vector vBloodColor;
|
||||
|
||||
vBloodPos[0] = readcoord();
|
||||
vBloodPos[1] = readcoord();
|
||||
vBloodPos[2] = readcoord();
|
||||
|
||||
vBloodColor[0] = readbyte() / 255;
|
||||
vBloodColor[1] = readbyte() / 255;
|
||||
vBloodColor[2] = readbyte() / 255;
|
||||
|
||||
FX_Blood(vBloodPos, vBloodColor);
|
||||
break;
|
||||
case EV_EXPLOSION:
|
||||
vector vExploPos;
|
||||
|
||||
vExploPos[0] = readcoord();
|
||||
vExploPos[1] = readcoord();
|
||||
vExploPos[2] = readcoord();
|
||||
|
||||
FX_Explosion(vExploPos);
|
||||
break;
|
||||
case EV_MODELGIB:
|
||||
vector vecPos;
|
||||
vecPos[0] = readcoord();
|
||||
vecPos[1] = readcoord();
|
||||
vecPos[2] = readcoord();
|
||||
|
||||
vector vSize;
|
||||
vSize[0] = readcoord();
|
||||
vSize[1] = readcoord();
|
||||
vSize[2] = readcoord();
|
||||
|
||||
float fStyle = readbyte();
|
||||
int count = readbyte();
|
||||
FX_BreakModel(count, vecPos, vSize, [0,0,0], fStyle);
|
||||
break;
|
||||
case EV_IMPACT:
|
||||
//int iType;
|
||||
vector vOrigin, vNormal;
|
||||
|
||||
iType = (int)readbyte();
|
||||
vOrigin[0] = readcoord();
|
||||
vOrigin[1] = readcoord();
|
||||
vOrigin[2] = readcoord();
|
||||
|
||||
vNormal[0] = readcoord();
|
||||
vNormal[1] = readcoord();
|
||||
vNormal[2] = readcoord();
|
||||
|
||||
FX_Impact(iType, vOrigin, vNormal);
|
||||
break;
|
||||
|
||||
//TAGGG - NEW
|
||||
case EVENT_TS::EV_IMPACT_MELEE:
|
||||
int iType3;
|
||||
vector vOrigin3;
|
||||
vector vNormal3;
|
||||
|
||||
iType3 = (int)readbyte();
|
||||
vOrigin3[0] = readcoord();
|
||||
vOrigin3[1] = readcoord();
|
||||
vOrigin3[2] = readcoord();
|
||||
|
||||
vNormal3[0] = readcoord();
|
||||
vNormal3[1] = readcoord();
|
||||
vNormal3[2] = readcoord();
|
||||
|
||||
FX_Impact_Melee(iType3, vOrigin3, vNormal3);
|
||||
break;
|
||||
|
||||
case EV_CHAT:
|
||||
float fSender = readbyte();
|
||||
float fTeam = readbyte();
|
||||
string sMessage = readstring();
|
||||
|
||||
CSQC_Parse_Print(sprintf("%s: %s", getplayerkeyvalue(fSender, "name"), sMessage), PRINT_CHAT);
|
||||
break;
|
||||
case EV_CHAT_TEAM:
|
||||
float fSender2 = readbyte();
|
||||
float fTeam2 = readbyte();
|
||||
string sMessage2 = readstring();
|
||||
|
||||
CSQC_Parse_Print(sprintf("[TEAM] %s: %s", getplayerkeyvalue(fSender2, "name"), sMessage2), PRINT_CHAT);
|
||||
break;
|
||||
case EV_CHAT_VOX:
|
||||
Vox_Play(readstring());
|
||||
break;
|
||||
case EV_VIEWMODEL:
|
||||
View_PlayAnimation(readbyte());
|
||||
break;
|
||||
case EV_WEAPON_PICKUP:
|
||||
int w = readbyte();
|
||||
|
||||
//TAGGG - NOTE!
|
||||
// Phase me out, or use me instead. Whichever.
|
||||
// Redundant with... oh? Nothing here clearly by name.
|
||||
// Ah well, somewhere there's completly TS independent pickup logic
|
||||
// that really shouldn't be when some portions of weapons_common.qc and
|
||||
// an existing weapon pickup object exists, I think.
|
||||
|
||||
/*
|
||||
if (autocvar_cl_autoweaponswitch == 1) {
|
||||
sendevent("PlayerSwitchWeapon", "i", w);
|
||||
}
|
||||
*/
|
||||
|
||||
HUD_WeaponPickupNotify(w);
|
||||
break;
|
||||
case EVENT_TS::SPAWN:
|
||||
//time to read the config to send in the weapons one-by-one.
|
||||
deployConfig();
|
||||
|
||||
// so that any choice of weapon, same as before or even nothing, will still
|
||||
// let client/view.qc do the whole viewmodel routine again
|
||||
pSeat->m_iLastWeapon = -2;
|
||||
break;
|
||||
case EVENT_TS::RESET_VIEW_MODEL:
|
||||
EV_TS_resetViewModel();
|
||||
break;
|
||||
case EVENT_TS::RESET_PLAYER:
|
||||
int resetInventory = readbyte();
|
||||
EV_TS_resetPlayer(pl, resetInventory);
|
||||
break;
|
||||
//case EVENT_TS::PLAY_INSERT_SHELL_SND:
|
||||
// EV_TS_PlayInsertShellSound(pl);
|
||||
// break;
|
||||
case EVENT_TS::EFFECT_EXPLOSION:
|
||||
vecOrigin[0] = readcoord();
|
||||
vecOrigin[1] = readcoord();
|
||||
vecOrigin[2] = readcoord();
|
||||
EV_Effect_Explosion( vecOrigin );
|
||||
break;
|
||||
case EVENT_TS::EFFECT_SHAKE:
|
||||
iType = readbyte();
|
||||
EV_Effect_ScreenShake( iType );
|
||||
break;
|
||||
case EVENT_TS::TEST:
|
||||
//printfline("EVENT_TS::TEST HAPPENED");
|
||||
//clearscene();
|
||||
break;
|
||||
/*
|
||||
//can this even happen ...?
|
||||
case EVENT_TS::DROP_WEAPON:
|
||||
EV_TS_playerDropWeapon(pl);
|
||||
break;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
5
src/client/hud.h
Normal file
5
src/client/hud.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
// why not? used to be client/defs.h (of this gamemod)
|
||||
var string g_hud1_spr;
|
||||
|
165
src/client/hud.qc
Normal file
165
src/client/hud.qc
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void HUD_DrawWeaponSelect(void);
|
||||
|
||||
// Nope, HUD/UI-related precaches in client/precache.qc instead
|
||||
// ...mostly, keeping this one here?
|
||||
void
|
||||
HUD_Init(void)
|
||||
{
|
||||
g_hud1_spr = spriteframe("sprites/640hud1.spr", 0, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
/* weapon/ammo pickup notifications */
|
||||
void
|
||||
HUD_DrawNotify(void)
|
||||
{
|
||||
// Nope, pretty sure TS had nothing like that.
|
||||
/*
|
||||
vector pos;
|
||||
|
||||
if (pSeatLocal->m_flPickupAlpha <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
pos = g_hudmins + [g_hudres[0] - 192, g_hudres[1] - 128];
|
||||
Weapons_HUDPic(pSeatLocal->m_iPickupWeapon, 1, pos, pSeatLocal->m_flPickupAlpha);
|
||||
pSeatLocal->m_flPickupAlpha -= clframetime;
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
HUD_WeaponPickupNotify(int w)
|
||||
{
|
||||
//pSeatLocal->m_iPickupWeapon = w;
|
||||
//pSeatLocal->m_flPickupAlpha = 1.0f;
|
||||
}
|
||||
|
||||
/* main entry */
|
||||
void
|
||||
HUD_Draw(void)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
g_hud_color = autocvar_con_color * (1 / 255);
|
||||
|
||||
/* little point in not drawing these, even if you don't have a suit */
|
||||
Weapons_DrawCrosshair();
|
||||
HUD_DrawWeaponSelect();
|
||||
Obituary_Draw();
|
||||
Textmenu_Draw();
|
||||
|
||||
|
||||
//TAGGG - NEw
|
||||
//////////////////////////////////////////////////////////////
|
||||
//View_HandleZoom();
|
||||
|
||||
//printfline("SCOPE LEVEL %.2f", pl.flZoomLevel);
|
||||
if(pl.flZoomLevel < 0.5){ //is this < 40? yes.
|
||||
HUD_DrawScope();
|
||||
}else{
|
||||
// We'll leave details like extra details for the lasersight and the weight bars at a bare minimum
|
||||
// (should be drawn at all times, oversight in TS 2.1 that they're missing from melee views
|
||||
// like with knives, katana, corrected in 3.0 of all things)
|
||||
HUD_DrawCrosshair();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
GameClient_PlayerInputRaw();
|
||||
|
||||
//TAGGG - NEw
|
||||
//////////////////////////////////////////////////////////////
|
||||
drawTimer();
|
||||
drawPlayerStats();
|
||||
//TAGGG - CRITICAL. Nope, Weapons_DrawCrosshair actually calls a weapon's custom HUD drawing method.
|
||||
// Odd name, but yes, it's not actually focused on just crosshairs. TS uses the more generic
|
||||
// "HUD_DrawCrosshair" call above.
|
||||
//drawPlayerCurrentWeaponStats();
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// TEST! Just for nuclide, doesn't matter what m_iHUDWeaponSelected is exactly,
|
||||
// just 0 or non-zero has significance in it.
|
||||
pSeat->m_iHUDWeaponSelected = (pl.weaponSelectHighlightID != -1);
|
||||
|
||||
|
||||
TS_keyRefTapped = 0; //reset.
|
||||
|
||||
HUD_DrawNotify();
|
||||
// Nuclide provided method, draws the HL pain arrows
|
||||
// Nope! Replaced with a completely new version that does that and more for more control.
|
||||
// And not even calling from here, leaving that to PostDraw (draw.qc) instead.
|
||||
// Might stop the pain flash from affecting the color of HUD draw's.
|
||||
//Damage_Draw();
|
||||
//ClientGame_DamageDraw();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
string g_specmodes[] = {
|
||||
"Free Camera",
|
||||
"Third Person",
|
||||
"First Person"
|
||||
};
|
||||
*/
|
||||
// specatator main entry
|
||||
void
|
||||
HUD_DrawSpectator(void)
|
||||
{
|
||||
|
||||
Textmenu_Draw();
|
||||
|
||||
spectator spec = (spectator)pSeat->m_ePlayer;
|
||||
|
||||
drawfont = FONT_20;
|
||||
/*
|
||||
vector vecPos;
|
||||
string strText;
|
||||
|
||||
// No need to display these.
|
||||
strText = sprintf("Tracking: %s", getplayerkeyvalue(spec.spec_ent - 1, "name"));
|
||||
vecPos[0] = g_hudmins[0] + (g_hudres[0] / 2) - (stringwidth(strText, TRUE, [20,20]) / 2);
|
||||
vecPos[1] = g_hudmins[1] + g_hudres[1] - 60;
|
||||
drawstring(vecPos, strText, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
strText = sprintf("Mode: %s", g_specmodes[spec.spec_mode]);
|
||||
vecPos[0] = g_hudmins[0] + (g_hudres[0] / 2) - (stringwidth(strText, TRUE, [20,20]) / 2);
|
||||
vecPos[1] = g_hudmins[1] + g_hudres[1] - 40;
|
||||
drawstring(vecPos, strText, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
*/
|
||||
|
||||
|
||||
// TAGGG - could have some message from server-to-client on changing from player to spectator
|
||||
// to call this only then, but I think doing this every frame for spectator is harmless anyway.
|
||||
// Changing the FOV isn't necessary, that already comes with the spec/player change, some
|
||||
// things are nicely defaulted for us in FTE.
|
||||
setsensitivityscaler(1.0f);
|
||||
|
||||
|
||||
GameClient_SpectatorInputRaw();
|
||||
|
||||
|
||||
drawTimer();
|
||||
|
||||
//TAGGG - Moved over! Is it wise for this to go here?
|
||||
// Links to drawing the MoTD and buymenu when appropriate
|
||||
//TAGGG - CRITICAL. "self" is a spectator, not a player!!
|
||||
// Send over the above "spec" instead too!
|
||||
CSQC_VGUI_Draw( (player)self );
|
||||
|
||||
}
|
266
src/client/hud_crosshair.qc
Normal file
266
src/client/hud_crosshair.qc
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2019 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
//it's ok for this to go here, right?
|
||||
CREATE_imageFileRef_t(img_player_stuntmeter1, "sprites/player/divestept.spr", 40, 80)
|
||||
CREATE_imageFileRef_t(img_player_stuntmeter2, "sprites/player/divestepc.spr", 40, 80)
|
||||
CREATE_imageFileRef_t(img_player_stuntmeter3, "sprites/player/divestepb.spr", 40, 80)
|
||||
|
||||
CREATE_imageFileRef_raw_t(img_quadgraphic, "textures/cross2.tga", 64, 64)
|
||||
|
||||
/*
|
||||
=================
|
||||
HUD_DrawCrosshair
|
||||
|
||||
Draws the cursor every frame, unless spectator
|
||||
=================
|
||||
*/
|
||||
|
||||
|
||||
void HUD_DrawCrosshair(void) {
|
||||
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
weapondynamic_t dynaRef;
|
||||
weapondata_gun_t* gunPointer;
|
||||
weapondata_gun_t gunRef;
|
||||
|
||||
vector vCenter = video_mins + [video_res[0]/2, video_res[1]/2];
|
||||
|
||||
// only TRUE for guns, melee stuff doesn't get this.
|
||||
BOOL hasWeaponCrosshair = FALSE;
|
||||
// only TRUE if the gun has lasersight, of course.
|
||||
BOOL hasLasersight = FALSE;
|
||||
int drawLaserDots = 0;
|
||||
|
||||
BOOL trackLaserDots = FALSE; //only applicable with lasersight on of course.
|
||||
|
||||
if(pl.inventoryEquippedIndex != -1){
|
||||
//get that weapon.
|
||||
dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
|
||||
if(dynaRef.weaponTypeID == WEAPONDATA_TYPEID_GUN || dynaRef.weaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
gunPointer = (weapondata_gun_t*) pl.getEquippedWeaponData();
|
||||
gunRef = *(gunPointer);
|
||||
// We probably have a "drawCrosshair" variable in our weapon struct.
|
||||
// probaly not even necessary really, since we could infer that property fine anyway from the
|
||||
// weaponTypeID.
|
||||
// One more check... we can explicitly forbid drawing the crosshair ever for a weapon too.
|
||||
// Example is the barrett sniper rifle, even hip-fire has no crosshair like other weapons.
|
||||
if(gunRef.fDrawCrosshair){
|
||||
hasWeaponCrosshair = TRUE;
|
||||
}
|
||||
|
||||
//we could very well need the lasersight drawn without the crosshair
|
||||
// (example: barrett sniper rifle)
|
||||
int iBitsUpgradeSafe_on = dynaRef.iBitsUpgrade_on & (dynaRef.iBitsUpgrade & gunRef.iBitsUpgrade);
|
||||
|
||||
//do we have a lasersight though?
|
||||
if(iBitsUpgradeSafe_on & BITS_WEAPONOPT_LASERSIGHT){
|
||||
// why yes, we got it
|
||||
hasLasersight = TRUE;
|
||||
|
||||
int reloadSeq = -1;
|
||||
//what is my reload sequence?
|
||||
if(dynaRef.weaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
//it's from the
|
||||
weapondata_ironsight_t* ironsightPointer = (weapondata_ironsight_t*)gunPointer;
|
||||
weapondata_ironsight_t ironsightRef = (*ironsightPointer);
|
||||
if(!dynaRef.iIronSight){
|
||||
reloadSeq = ironsightRef.iAnim_Reload_Index;
|
||||
}else{
|
||||
reloadSeq = ironsightRef.ironsightdata.iAnim_Reload_Index;
|
||||
}
|
||||
}else{
|
||||
reloadSeq = gunRef.iAnim_Reload_Index;
|
||||
}
|
||||
|
||||
if(!pl.weaponEquippedAkimbo){
|
||||
drawLaserDots = 1;
|
||||
}else{
|
||||
drawLaserDots = 2;
|
||||
}
|
||||
|
||||
// Is this a condition that forbids drawing hte laser dot on the screen?
|
||||
if(
|
||||
!pl.isReloading &&
|
||||
!(pSeat->m_eViewModel.frame == gunRef.iAnim_Deploy_Index && pl.w_attack_next > 0) &&
|
||||
!pl.lasersightUnlockTime
|
||||
){
|
||||
trackLaserDots = FALSE; // force them to the center on the screen.
|
||||
}else{
|
||||
// Track their positions on the ingame world. Weird, I know.
|
||||
trackLaserDots = TRUE;
|
||||
}
|
||||
}//END OF lasersight check
|
||||
|
||||
|
||||
}
|
||||
}//END OF inventoryEquippedIndex check
|
||||
|
||||
|
||||
// Draw some things unconditionally.
|
||||
// (we only even reached this point if not zoomed through a scope with the overlay of course)
|
||||
/*
|
||||
0 - 21 slots: all thirds clear.
|
||||
22 - 41 slots: top third orange.
|
||||
42 - 71 slots: top, middle thirds orange.
|
||||
72 - 81 slots: all thirds orange.
|
||||
*/
|
||||
|
||||
int thirdBelongingTo = 0;
|
||||
|
||||
float ratioNumber = getViewPitchRelativeRatio(pl.pitch);
|
||||
float newDegreeRad = (ratioNumber * (270 - 90) + 90) * (M_PI/180);
|
||||
|
||||
//printfline("MY ANGLE? %.2f", (ratioNumber * (270 - 90) + 90) );
|
||||
|
||||
//check for the degree, set thirdBelongingTo...
|
||||
if(ratioNumber <= 0.3333){
|
||||
thirdBelongingTo = 0;
|
||||
}else if(ratioNumber <= 0.6667){
|
||||
thirdBelongingTo = 1;
|
||||
}else{ // <= 1.0
|
||||
thirdBelongingTo = 2;
|
||||
}
|
||||
|
||||
// we COULD make this a property (ts/main.c) to be set by the client and read by the server,
|
||||
// but this might be really inefficient. May as well use the v_angle already available serverside to determine
|
||||
// what third we're looking at.
|
||||
|
||||
// top = 90 degrees for safePlayerViewPitch of -31.
|
||||
// bottom = 270 degrees for safePlayerViewPitch of 30.
|
||||
|
||||
//Max upwards: -31
|
||||
//mid: 0
|
||||
//Max downwards: 30
|
||||
|
||||
//check for belingong to each of the thirds for which to light up, if applicable. Above that is.
|
||||
|
||||
float modder;
|
||||
vector clrColor;
|
||||
|
||||
modder = 0.56f;
|
||||
if(pl.iTotalSlots <= 21){clrColor = clrPaleBlue;if(thirdBelongingTo==0){modder = 1.0f;}}else{clrColor = clrFailedSlotbarColor;}
|
||||
DRAW_IMAGE_ADDITIVE(img_player_stuntmeter1, ([vCenter[0] - 40, vCenter[1] - 40]), clrColor, modder)
|
||||
|
||||
modder = 0.56f;
|
||||
if(pl.iTotalSlots <= 41){clrColor = clrPaleBlue;if(thirdBelongingTo==1){modder = 1.0f;}}else{clrColor = clrFailedSlotbarColor;}
|
||||
DRAW_IMAGE_ADDITIVE(img_player_stuntmeter2, ([vCenter[0] - 40, vCenter[1] - 40]), clrColor, modder)
|
||||
|
||||
modder = 0.56f;
|
||||
if(pl.iTotalSlots <= 71){clrColor = clrPaleBlue;if(thirdBelongingTo==2){modder = 1.0f;}}else{clrColor = clrFailedSlotbarColor;}
|
||||
DRAW_IMAGE_ADDITIVE(img_player_stuntmeter3, ([vCenter[0] - 40, vCenter[1] - 40]), clrColor, modder)
|
||||
|
||||
vector resultArcPoint = vCenter + [cos(newDegreeRad), -sin(newDegreeRad)] * 32;
|
||||
drawfill( resultArcPoint, [2, 2], clrPaleBlue, 0.7f, DRAWFLAG_NORMAL );
|
||||
|
||||
|
||||
if(hasWeaponCrosshair){
|
||||
|
||||
int startCrosshairDist;
|
||||
int maxCrosshairDelta; //how much the crosshair can move outwards at most. 0 is no change!
|
||||
int crosshairDist;
|
||||
|
||||
if(!hasLasersight){
|
||||
startCrosshairDist = 22;
|
||||
maxCrosshairDelta = 15;
|
||||
}else{
|
||||
startCrosshairDist = 12;
|
||||
maxCrosshairDelta = 14;
|
||||
}
|
||||
//can expand up to +14 beyond.
|
||||
|
||||
|
||||
float facto = (bound(0, pl.fAccuracyKickback, 0.1) ) / 0.1;
|
||||
|
||||
//scale it.
|
||||
crosshairDist = startCrosshairDist + facto * maxCrosshairDelta;
|
||||
|
||||
//printfline("pl.fAccuracyKickback:%.4f", pl.fAccuracyKickback);
|
||||
|
||||
clrColor = [0/255.0f, 0/255.0f, 0/255.0f];
|
||||
DRAW_IMAGE_NORMAL(img_quadgraphic, ([vCenter[0] - 32, vCenter[1] - 32]), clrColor, 1.00f)
|
||||
|
||||
|
||||
// * 0.86 ???
|
||||
drawfill( [vCenter[0] - crosshairDist, vCenter[1] - 1], [5, 2], clrPaleBlue, 0.70f, DRAWFLAG_NORMAL );
|
||||
drawfill( [vCenter[0] + (crosshairDist - 5), vCenter[1] - 1], [5, 2], clrPaleBlue, 0.70f, DRAWFLAG_NORMAL );
|
||||
|
||||
drawfill( [vCenter[0] - 1, vCenter[1] - crosshairDist], [2, 5], clrPaleBlue, 0.7f, DRAWFLAG_NORMAL );
|
||||
drawfill( [vCenter[0] - 1, vCenter[1] + (crosshairDist - 5)], [2, 5], clrPaleBlue, 0.70f, DRAWFLAG_NORMAL );
|
||||
|
||||
|
||||
}//END OF ranged weapon check
|
||||
|
||||
|
||||
if(hasLasersight){
|
||||
|
||||
|
||||
if(!trackLaserDots){
|
||||
// draw it straight at the HUD center.
|
||||
// TODO - add a very slight, slow random offset from the given point.
|
||||
// Why? BECAUSE KHORNE MUST BE PLEASED.
|
||||
// ...I mean, because original TS did it. okay.
|
||||
vector lasDot;
|
||||
|
||||
if(drawLaserDots == 1){
|
||||
// dot in the center and the range number fresh from the server, probably.
|
||||
// -1. 5. 2.
|
||||
lasDot = vCenter + [-1, -1];
|
||||
drawfill( lasDot, [2, 2], clrLaserHUDDot, 0.83f, DRAWFLAG_NORMAL );
|
||||
}else if(drawLaserDots == 2){
|
||||
lasDot = vCenter + [-1 + -4, -1];
|
||||
drawfill( lasDot, [2, 2], clrLaserHUDDot, 0.83f, DRAWFLAG_NORMAL );
|
||||
|
||||
lasDot = vCenter + [-1 + 4, -1];
|
||||
drawfill( lasDot, [2, 2], clrLaserHUDDot, 0.83f, DRAWFLAG_NORMAL );
|
||||
}
|
||||
}else{
|
||||
if(pl.recentLaserHitPosSet){
|
||||
|
||||
if(drawLaserDots == 1){
|
||||
// dot in the center and the range number fresh from the server, probably.
|
||||
// -1. 5. 2.
|
||||
|
||||
//trust "recentLaserHit" then!
|
||||
lasDot = project(pl.recentLaserHitPos);
|
||||
drawfill( lasDot, [2, 2], clrLaserHUDDot, 0.83f, DRAWFLAG_NORMAL );
|
||||
}else if(drawLaserDots == 2){
|
||||
lasDot = project(pl.recentLaserHitPos);
|
||||
drawfill( lasDot, [2, 2], clrLaserHUDDot, 0.83f, DRAWFLAG_NORMAL );
|
||||
|
||||
lasDot = project(pl.recentLaserHitPos2);
|
||||
drawfill( lasDot, [2, 2], clrLaserHUDDot, 0.83f, DRAWFLAG_NORMAL );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(drawLaserDots != 0){
|
||||
if(hasWeaponCrosshair){
|
||||
if(pl.recentLaserHitPosSet){
|
||||
vector vDistNumberLoc = vCenter + [28, -8];
|
||||
drawSpriteNumber(ary_LCD_numberSet, vDistNumberLoc.x, vDistNumberLoc.y, pl.recentLaserDistanceDisplay, 3, BITS_DIGITOPT_NONE, clrPaleBlue, 0.91f);
|
||||
}
|
||||
}
|
||||
}//END OF flat drawLaserDots check
|
||||
|
||||
}//END OF lasersight check
|
||||
|
||||
|
||||
}//END OF HUD_DrawCrosshair
|
190
src/client/hud_scope.qc
Normal file
190
src/client/hud_scope.qc
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2019 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
float fSBOffset;
|
||||
float fSBScale;
|
||||
|
||||
//var vector g_vecLightColor;
|
||||
|
||||
|
||||
CREATE_imageFileRef_raw_t(img_scope_quad, "textures/quarterscope.tga", 256, 256)
|
||||
CREATE_imageFileRef_raw_t(img_scope_cross_plus, "textures/scope_cross_plus_odd.tga", 19, 19) //was 20, 20.. shoulda been 19,19 tho
|
||||
//CREATE_imageFileRef_t(img_???, "sprites/player/stand.spr", 64, 48)
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
HUD_DrawScope_Pic
|
||||
|
||||
The scope borders are split up into multiple parts.
|
||||
We want to fill the screen, so we gotta do some hacking.
|
||||
=================
|
||||
*/
|
||||
void HUD_DrawScope_Pic( vector vPos, vector vSize, string sSprite ) {
|
||||
drawpic( ( vPos * fSBScale ) + [ fSBOffset, 0 ], sSprite, vSize * fSBScale, '1 1 1', 1.0f );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
HUD_DrawScope
|
||||
|
||||
Tries to draw a scope whenever viewzoom < 1.0f
|
||||
=================
|
||||
*/
|
||||
void HUD_DrawScope( void ) {
|
||||
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if(pl == NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTICE - rest of the logic doesn't refer to vCenter except for the lasersight,
|
||||
// copied from hudcrosshair.
|
||||
vector vCenter = video_mins + [video_res[0]/2, video_res[1]/2];
|
||||
|
||||
// PREDICTION call??
|
||||
/*
|
||||
if (self != world) {
|
||||
//Player_Predict();
|
||||
//viewClient.vecPlayerOrigin = self.origin;
|
||||
//viewClient.vecPlayerVelocity = self.velocity;
|
||||
//viewClient.flMoveFlags = self.pmove_flags;
|
||||
//viewClient.flGameFlags = self.gflags;
|
||||
|
||||
//TAGGG - we get this apparently.
|
||||
//g_vecLightColor = getlight(viewClient.vecPlayerOrigin) / 255;
|
||||
|
||||
//... pSeat->m_vecPredictedOrigin = pl.origin;
|
||||
|
||||
g_vecLightColor = getlight(pSeat->m_vecPredictedOrigin) / 255;
|
||||
}
|
||||
*/
|
||||
|
||||
BOOL hasLasersight = FALSE;
|
||||
|
||||
if(pl.inventoryEquippedIndex != -1){
|
||||
//get that weapon.
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_weaponData[dynaRef.weaponID];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
int myWeaponTypeID = basicRef.typeID;
|
||||
if(myWeaponTypeID == WEAPONDATA_TYPEID_GUN || myWeaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
weapondata_gun_t gunRef = *((weapondata_gun_t*)(basicPointer));
|
||||
// We probably have a "drawCrosshair" variable in our weapon struct.
|
||||
// probaly not even necessary really, since we could infer that property fine anyway from the
|
||||
// typeID.
|
||||
// One more check... we can explicitly forbid drawing the crosshair ever for a weapon too.
|
||||
// Example is the barrett sniper rifle, even hip-fire has no crosshair like other weapons.
|
||||
|
||||
//if(basicRef.fDrawCrosshair){
|
||||
//hasWeaponCrosshair = TRUE;
|
||||
int iBitsUpgradeSafe_on = dynaRef.iBitsUpgrade_on & (dynaRef.iBitsUpgrade & basicRef.iBitsUpgrade);
|
||||
|
||||
//do we have a lasersight though?
|
||||
if(iBitsUpgradeSafe_on & BITS_WEAPONOPT_LASERSIGHT){
|
||||
// why yes, we got it
|
||||
hasLasersight = TRUE;
|
||||
}
|
||||
//}//END OF fDrawCrosshair check.
|
||||
}
|
||||
}//END OF inventoryEquippedIndex check
|
||||
|
||||
|
||||
|
||||
//How many times does our scope graphic fit half the height of the screen?
|
||||
float flScopeGraphicScale = (video_res[1]/2) / img_scope_quad.h * 1.6;
|
||||
|
||||
vector screenMid = [video_res[0]/2 - flScopeGraphicScale*img_scope_quad.w/2, video_res[1]/2 - flScopeGraphicScale*img_scope_quad.h/2];
|
||||
|
||||
const vector vecSca = [flScopeGraphicScale,flScopeGraphicScale];
|
||||
|
||||
//Determine whether we need to fill any of the left/right parts of the screen,
|
||||
//in case the user is using an ultra-wide screen.
|
||||
float anticipatedSize = flScopeGraphicScale*img_scope_quad.h;
|
||||
|
||||
float crosshairX_Offset = 0;
|
||||
|
||||
|
||||
// the "-5" is in case of some overlap with transparent pixels from scaling.
|
||||
if(anticipatedSize-5 >= video_res[0]/2){
|
||||
//the graphic will exceed half the screen's width. no need to do anything else.
|
||||
}else{
|
||||
float toFillWidth = (video_res[0]/2) - (anticipatedSize);
|
||||
crosshairX_Offset = toFillWidth;
|
||||
drawfill( [0,0], [ toFillWidth+5, video_res[1] ], [0,0,0], 1.0f );
|
||||
drawfill( [ video_res[0]/2 + anticipatedSize-5, 0 ], [video_res[0]/2 + -(anticipatedSize-5-1), video_res[1] ], [0,0,0], 1.0f );
|
||||
}
|
||||
|
||||
if(anticipatedSize-5 >= video_res[1]/2){
|
||||
//the graphic will exceed half the screen's width. no need to do anything else.
|
||||
}else{
|
||||
float toFillHeight = (video_res[1]/2) - (anticipatedSize);
|
||||
drawfill( [crosshairX_Offset-1,0], [ video_res[0] - (crosshairX_Offset-1)*2, toFillHeight+5 ], [0,0,0], 1.0f );
|
||||
drawfill( [crosshairX_Offset-1, video_res[1]/2 + anticipatedSize-5 ], [video_res[0] - (crosshairX_Offset-1)*2, video_res[1]/2 + -(anticipatedSize-5-1) ], [0,0,0], 1.0f );
|
||||
}
|
||||
|
||||
|
||||
|
||||
float centerCrosshairOpac;
|
||||
if(video_res[1] < 300){
|
||||
centerCrosshairOpac = 0; //don't draw
|
||||
}else{
|
||||
centerCrosshairOpac = max(0.27f, min(1, (video_res[1] - 300) / (1200 - 300) )* 0.45 ) ;
|
||||
}
|
||||
|
||||
vector vecInset = [0,0];
|
||||
|
||||
vector directCenter = [video_res[0]/2, video_res[1]/2];
|
||||
//DRAW_IMAGE_EXPER(img_scope_cross_plus, directCenter, '0 0', 0, vecSca*0.6, '0 0 0', centerCrosshairOpac );
|
||||
DRAW_IMAGE_EXPER2(img_scope_cross_plus, directCenter, vecSca, vecInset, '0 0 0', centerCrosshairOpac );
|
||||
|
||||
//NOTICE - the drawoption choice of 1 like The Wastes uses only works because the raw color of the
|
||||
// graphic is expected. But in our case, we actually still want even a black color to be solid.
|
||||
// The drawoption choice of 1 makes blackness get interpreted as transparency.
|
||||
// Anything else, just say 0, at least lets us color the white parts black.
|
||||
// Had to make the black parts still in the image transparent through Gimp, since we changed to a .tga format.
|
||||
// (the specialists original quarterscope.spr asset just doesn't work)
|
||||
|
||||
DRAW_IMAGE_EXPER(img_scope_quad, screenMid, '128 128', 0, vecSca, '0 0 0', 1.0f );
|
||||
DRAW_IMAGE_EXPER(img_scope_quad, screenMid, '128 128', 90, vecSca, '0 0 0', 1.0f );
|
||||
DRAW_IMAGE_EXPER(img_scope_quad, screenMid, '128 128', 180, vecSca, '0 0 0', 1.0f );
|
||||
DRAW_IMAGE_EXPER(img_scope_quad, screenMid, '128 128', 270, vecSca, '0 0 0', 1.0f );
|
||||
|
||||
|
||||
|
||||
int magAmount = (int)(1.0f / (pl.flZoomLevel ));
|
||||
//int xOffset = drawSpriteNumber(ary_LCD_numberSet, 24, 24, magAmount, 3, BITS_DIGITOPT_NONE, clrPaleBlue, 0.88f);
|
||||
//[24 + xOffset + 1, 24 + 4]
|
||||
Gfx_Text( [24, 27], sprintf("%ix", magAmount), vFontArialScopeMag, clrPaleBlue, 0.95f, DRAWFLAG_ADDITIVE, FONT_ARIAL_SCOPEMAG );
|
||||
|
||||
|
||||
if(hasLasersight){
|
||||
vector lasDot;
|
||||
//draw em if ya gottem
|
||||
lasDot = vCenter + [-1, -1];
|
||||
drawfill( lasDot, [2, 2], clrRed, 0.70f, DRAWFLAG_NORMAL );
|
||||
|
||||
|
||||
if(pl.recentLaserHitPosSet){
|
||||
vector vDistNumberLoc = vCenter + [28, -8 - 8];
|
||||
drawSpriteNumber(ary_LCD_numberSet, vDistNumberLoc.x, vDistNumberLoc.y, pl.recentLaserDistanceDisplay, 3, BITS_DIGITOPT_NONE, clrPaleBlue, 0.91f);
|
||||
}
|
||||
}//END OF hasLasersight check
|
||||
|
||||
|
||||
}
|
9
src/client/hud_weaponselect.h
Normal file
9
src/client/hud_weaponselect.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
//NEW FILE.
|
||||
|
||||
//whatever needs to be accessed out of order.
|
||||
|
||||
|
||||
BOOLEAN HUD_DrawWeaponSelect_CheckClick(void);
|
||||
|
||||
BOOLEAN HUD_CloseWeaponSelect(BOOL playOffSound);
|
884
src/client/hud_weaponselect.qc
Normal file
884
src/client/hud_weaponselect.qc
Normal file
|
@ -0,0 +1,884 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
vector g_vecHUDNums[6] =
|
||||
{
|
||||
[168 / 256, 72 / 128],
|
||||
[188 / 256, 72 / 128],
|
||||
[208 / 256, 72 / 128],
|
||||
[168 / 256, 92 / 128],
|
||||
[188 / 256, 92 / 128],
|
||||
[208 / 256, 92 / 128]
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
var float fHUDWeaponLast;
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
string sSprite;
|
||||
vector vOrigin;
|
||||
} weaponsymbolinfo_t;
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
HUD_DrawWeaponSelect_PreviousItem
|
||||
|
||||
Checks and returns the next slot with a weapon in it
|
||||
=================
|
||||
*/
|
||||
|
||||
//TAGGG - REPLACEMENT.
|
||||
//Now deal with int's (type) instead.
|
||||
//Also, this takes two parameters now: what index in the player weapon list to start looking for
|
||||
//the next weapon from, and whether finding the next/previous weapon is restricted
|
||||
//to some slot in particular, like pressing the "2" key to go through weapons in slot 2
|
||||
//(on reaching the last weapon, the 1st weapon in slot 2 is selected again, don't move to slot 3).
|
||||
//Note that this is the same display slot. There is no slot 0.
|
||||
//A slot of "-1" will be code for, no restriction, feel free to move between slots on trying to go
|
||||
//to the previous weapon on the first of a slot, or the next weapon on the last of a slot.
|
||||
//The last optional parameter, neede if freeSlotMovement is false, is what slot the weapon must belong to.
|
||||
//If we pick a slot different of the currently selected weapon, we start over in the new slot.
|
||||
int
|
||||
HUD_DrawWeaponSelect_PreviousItem(int weaponID_toStartFrom, BOOLEAN freeSlotMovement, optional int forcedSlot)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
|
||||
// set to TRUE if we start at an invalid range, so that we may include the first valid of any sort.
|
||||
BOOLEAN canAcceptSame = FALSE;
|
||||
|
||||
|
||||
//printfline("HUD_DrawWeaponSelect_PreviousItem: weaponID_toStartFrom:%i pl.ary_myWeapons_softMax:%i", weaponID_toStartFrom, pl.ary_myWeapons_softMax);
|
||||
//TAGGG CRITICAL - why wasn't this ">=" and just ">" ???
|
||||
if(weaponID_toStartFrom >= pl.ary_myWeapons_softMax){
|
||||
//shouldn't be possible but just in case.
|
||||
canAcceptSame = TRUE;
|
||||
return pl.ary_myWeapons_softMax-1;
|
||||
}
|
||||
|
||||
weapondynamic_t dynaRef;
|
||||
|
||||
weapondata_basic_t* weaponBasicP;
|
||||
weapondata_basic_t weaponBasicRef;
|
||||
|
||||
int currentSlot = -1;
|
||||
|
||||
|
||||
|
||||
if(weaponID_toStartFrom != -1){
|
||||
// Get what slot the currently equipped weapon is on.
|
||||
// If it doesn't match the forcedSlot (if applicable: 1-5 keys forcing one),
|
||||
// we start over at a weapon of ID -1 for the next one.
|
||||
// This happens on switching slots (picking a different 1-5 key) while weapon select
|
||||
// is open in a different slot, like having some weapon selected ins lot 2 but pressing 5.
|
||||
// Being in akimbo and requesting a lost other than 5 is also enough to force a reset of weaponID
|
||||
// to start from.
|
||||
dynaRef = pl.ary_myWeapons[weaponID_toStartFrom];
|
||||
|
||||
|
||||
|
||||
if(!freeSlotMovement ){
|
||||
|
||||
//That is, if the player was selecting a weapon in akimbo, but the slot is no longer 5..
|
||||
if(pl.weaponSelectHighlightAkimbo){
|
||||
if(forcedSlot!=5){
|
||||
weaponID_toStartFrom = -1; //hack to start at #0 instead
|
||||
canAcceptSame = FALSE;
|
||||
//dynaRef = pl.ary_myWeapons[0];
|
||||
}
|
||||
}else{
|
||||
//pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
weapondata_basic_t* weaponBasicInvGetP = ary_weaponData[dynaRef.weaponID];
|
||||
weapondata_basic_t weaponBasicInvGetRef = *weaponBasicInvGetP;
|
||||
currentSlot = weaponBasicInvGetRef.iInventorySlot;
|
||||
|
||||
//notice: picking an akimbo weapon choice while a non-akimbo weapon is selected
|
||||
// will trigger this too, since the forcedSlot will be 5. non-akimbo weapons never have that.
|
||||
|
||||
|
||||
if(currentSlot != forcedSlot){
|
||||
weaponID_toStartFrom = -1; //hack to start at #0 instead
|
||||
canAcceptSame = FALSE;
|
||||
//dynaRef = pl.ary_myWeapons[0];
|
||||
}
|
||||
}
|
||||
pl.weaponSelectHighlightAkimbo = (forcedSlot == 5);
|
||||
currentSlot = forcedSlot;
|
||||
}else{
|
||||
|
||||
|
||||
|
||||
if(!pl.weaponSelectHighlightAkimbo){
|
||||
|
||||
//So we have a place to start looking from. Off we go, find the next weapon that belongs to this same slot.
|
||||
//If we can't find it, we have to decide whether to loop back to the 1st/last weapon of this same slot,
|
||||
//or move on to the next/previous slot's first/last weapon.
|
||||
weaponBasicP = ary_weaponData[dynaRef.weaponID];
|
||||
weaponBasicRef = *weaponBasicP;
|
||||
currentSlot = weaponBasicRef.iInventorySlot;
|
||||
|
||||
}else{
|
||||
weapondata_basic_t* weaponBasicBaseP = ary_weaponData[dynaRef.weaponID];
|
||||
weapondata_basic_t weaponBasicBaseRef = *weaponBasicBaseP;
|
||||
if(weaponBasicBaseRef.iAkimboID > 0 && dynaRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO){
|
||||
|
||||
weaponBasicP = ary_akimboUpgradeData[weaponBasicBaseRef.iAkimboID];
|
||||
weaponBasicRef = *weaponBasicP;
|
||||
//Should always be 5 for akimbo weapons regardless.
|
||||
//currentSlot = weaponBasicRef.iInventorySlot;
|
||||
currentSlot = 5;
|
||||
|
||||
}else{
|
||||
//??? clearly can't go through this weapon as akimbo.
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
|
||||
weaponBasicP = ary_weaponData[dynaRef.weaponID];
|
||||
weaponBasicRef = *weaponBasicP;
|
||||
currentSlot = weaponBasicRef.iInventorySlot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//weaponID_toStartFrom is -1? ok.
|
||||
canAcceptSame = FALSE;
|
||||
//dynaRef = pl.ary_myWeapons[0];
|
||||
pl.weaponSelectHighlightAkimbo = (forcedSlot == 5);
|
||||
currentSlot = forcedSlot;
|
||||
}
|
||||
|
||||
|
||||
//we only use the toStartFrom offset for finding the next/previous weapon in the same slot.
|
||||
//After that we start from the beginning/end of the next/previous slot accordingly for searching.
|
||||
int weaponID_toStartFromThisSlot = weaponID_toStartFrom;
|
||||
if(canAcceptSame && weaponID_toStartFromThisSlot != -1){
|
||||
weaponID_toStartFromThisSlot -= 1;
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN scheduleTermination = FALSE;
|
||||
//int lastWeaponToCheck = 0;
|
||||
|
||||
for(int slotOffset = 0; slotOffset <= 5; slotOffset++){
|
||||
|
||||
int slotToMatch = ((currentSlot + slotOffset - 1) % 5) + 1;
|
||||
|
||||
if(slotToMatch < 5){
|
||||
for(int i = weaponID_toStartFromThisSlot+1; i < pl.ary_myWeapons_softMax; i++){
|
||||
//where does a weapon match our slot? pick it.
|
||||
weapondynamic_t dynaOtherRef;
|
||||
dynaOtherRef = pl.ary_myWeapons[i];
|
||||
|
||||
weapondata_basic_t* weaponBasicOtherP = ary_weaponData[dynaOtherRef.weaponID];
|
||||
weapondata_basic_t weaponBasicOtherRef = *weaponBasicOtherP;
|
||||
|
||||
if(weaponBasicOtherRef.iInventorySlot == slotToMatch){
|
||||
//woohoo
|
||||
//foundNewWeapon = TRUE;
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
//printfline("FOUND NON-AKIMBO WEAPO %i, ITS NAME: %s", i, weaponBasicOtherRef.sDisplayName);
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//oh my
|
||||
//if(pl.weaponSelectHighlightAkimbo){
|
||||
// //don't reset toStartFrom. Offset is intentional.
|
||||
//}
|
||||
|
||||
for(int i = weaponID_toStartFromThisSlot+1; i < pl.ary_myWeapons_softMax; i++){
|
||||
//where does a weapon match our slot? pick it.
|
||||
weapondynamic_t dynaOtherRef;
|
||||
dynaOtherRef = pl.ary_myWeapons[i];
|
||||
|
||||
weapondata_basic_t* weaponBasicOtherP = ary_weaponData[dynaOtherRef.weaponID];
|
||||
weapondata_basic_t weaponBasicOtherRef = *weaponBasicOtherP;
|
||||
|
||||
if(weaponBasicOtherRef.iAkimboID > 0 && dynaOtherRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO){
|
||||
//this weapon is akimbo, yay.
|
||||
pl.weaponSelectHighlightAkimbo = TRUE;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
if(weaponBasicOtherRef.iInventorySlot == slotToMatch){
|
||||
//To handle stand-alone akimbo weapons found.
|
||||
|
||||
if(dynaOtherRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO){
|
||||
pl.weaponSelectHighlightAkimbo = TRUE;
|
||||
}else{
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Moving on to check a different slot? Reset weaponID_toStartFromThisSlot then
|
||||
weaponID_toStartFromThisSlot = -1; //hack to make it start at 0
|
||||
|
||||
if(scheduleTermination){
|
||||
//last shot we got.
|
||||
break;
|
||||
}
|
||||
|
||||
if(freeSlotMovement == TRUE){
|
||||
|
||||
}else{
|
||||
slotOffset -= 1; //keep the slot exactly the same
|
||||
|
||||
scheduleTermination = TRUE;
|
||||
}
|
||||
|
||||
}//END OF LOOP THROUGH SLOTS
|
||||
|
||||
//printfline("END. weaponID_toStartFrom:%i", weaponID_toStartFrom);
|
||||
return weaponID_toStartFrom; //uhhhh. what?? guess there would be no change in this odd case
|
||||
}//HUD_DrawWeaponSelect_PreviousItem
|
||||
|
||||
int
|
||||
HUD_DrawWeaponSelect_NextItem(int weaponID_toStartFrom, BOOLEAN freeSlotMovement, optional int forcedSlot)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
// set to TRUE if we start at an invalid range, so that we may include the first valid of any sort.
|
||||
BOOLEAN canAcceptSame = FALSE;
|
||||
|
||||
if(weaponID_toStartFrom >= pl.ary_myWeapons_softMax){
|
||||
//shouldn't be possible but just in case.
|
||||
canAcceptSame = TRUE;
|
||||
return pl.ary_myWeapons_softMax-1;
|
||||
}
|
||||
|
||||
|
||||
weapondynamic_t dynaRef;
|
||||
|
||||
weapondata_basic_t* weaponBasicP;
|
||||
weapondata_basic_t weaponBasicRef;
|
||||
|
||||
int currentSlot = -1;
|
||||
|
||||
|
||||
//printfline("HUD_DrawWeaponSelect_NextItem weaponID_toStartFrom:%i", weaponID_toStartFrom);
|
||||
|
||||
|
||||
if(weaponID_toStartFrom != -1){
|
||||
dynaRef = pl.ary_myWeapons[weaponID_toStartFrom];
|
||||
|
||||
if(!freeSlotMovement ){
|
||||
//That is, if the player was selecting a weapon in akimbo, but the slot is no longer 5..
|
||||
|
||||
if(pl.weaponSelectHighlightAkimbo){
|
||||
if(forcedSlot!=5){
|
||||
weaponID_toStartFrom = 1; //hack to start at #max instead
|
||||
canAcceptSame = FALSE;
|
||||
//dynaRef = pl.ary_myWeapons[0];
|
||||
}
|
||||
}else{
|
||||
//pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
weapondata_basic_t* weaponBasicInvGetP = ary_weaponData[dynaRef.weaponID];
|
||||
weapondata_basic_t weaponBasicInvGetRef = *weaponBasicInvGetP;
|
||||
currentSlot = weaponBasicInvGetRef.iInventorySlot;
|
||||
|
||||
//notice: picking an akimbo weapon choice while a non-akimbo weapon is selected
|
||||
// will trigger this too, since the forcedSlot will be 5. non-akimbo weapons never have that.
|
||||
if(currentSlot != forcedSlot){
|
||||
weaponID_toStartFrom = 1; //hack to start at #max instead
|
||||
canAcceptSame = FALSE;
|
||||
//dynaRef = pl.ary_myWeapons[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(pl.weaponSelectHighlightAkimbo != (forcedSlot==5) ){
|
||||
weaponID_toStartFrom = 1; //hack to start at #max instead
|
||||
canAcceptSame = FALSE;
|
||||
//dynaRef = pl.ary_myWeapons[0];
|
||||
}else{
|
||||
|
||||
|
||||
}
|
||||
pl.weaponSelectHighlightAkimbo = (forcedSlot == 5);
|
||||
currentSlot = forcedSlot;
|
||||
}else{
|
||||
|
||||
|
||||
if(!pl.weaponSelectHighlightAkimbo){
|
||||
|
||||
//So we have a place to start looking from. Off we go, find the next weapon that belongs to this same slot.
|
||||
//If we can't find it, we have to decide whether to loop back to the 1st/last weapon of this same slot,
|
||||
//or move on to the next/previous slot's first/last weapon.
|
||||
weaponBasicP = ary_weaponData[dynaRef.weaponID];
|
||||
weaponBasicRef = *weaponBasicP;
|
||||
currentSlot = weaponBasicRef.iInventorySlot;
|
||||
|
||||
}else{
|
||||
weapondata_basic_t* weaponBasicBaseP = ary_weaponData[dynaRef.weaponID];
|
||||
weapondata_basic_t weaponBasicBaseRef = *weaponBasicBaseP;
|
||||
if(weaponBasicBaseRef.iAkimboID > 0 && dynaRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO){
|
||||
|
||||
weaponBasicP = ary_akimboUpgradeData[weaponBasicBaseRef.iAkimboID];
|
||||
weaponBasicRef = *weaponBasicP;
|
||||
//Should always be 5 for akimbo weapons regardless.
|
||||
//currentSlot = weaponBasicRef.iInventorySlot;
|
||||
currentSlot = 5;
|
||||
|
||||
}else{
|
||||
//??? clearly can't go through this weapon as akimbo.
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
|
||||
weaponBasicP = ary_weaponData[dynaRef.weaponID];
|
||||
weaponBasicRef = *weaponBasicP;
|
||||
currentSlot = weaponBasicRef.iInventorySlot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// is.. this even possible?
|
||||
//weaponID_toStartFrom is -1? ok.
|
||||
|
||||
// was ... = 1; ?
|
||||
weaponID_toStartFrom = pl.ary_myWeapons_softMax; //hack to start at #max instead
|
||||
|
||||
canAcceptSame = FALSE;
|
||||
//dynaRef = pl.ary_myWeapons[0];
|
||||
pl.weaponSelectHighlightAkimbo = (forcedSlot == 5);
|
||||
currentSlot = forcedSlot;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//we only use the toStartFrom offset for finding the next/previous weapon in the same slot.
|
||||
//After that we start from the beginning/end of the next/previous slot accordingly for searching.
|
||||
int weaponID_toStartFromThisSlot = weaponID_toStartFrom;
|
||||
if(canAcceptSame){
|
||||
weaponID_toStartFromThisSlot += 1;
|
||||
}
|
||||
|
||||
BOOLEAN scheduleTermination = FALSE;
|
||||
//int lastWeaponToCheck = 0;
|
||||
|
||||
|
||||
//printfline("weaponID_toStartFromThisSlot:%i, canAcceptSame:%d", weaponID_toStartFromThisSlot, canAcceptSame);
|
||||
|
||||
for(int slotOffset = 0; slotOffset <= 5; slotOffset++){
|
||||
|
||||
//int slotToMatch = ((currentSlot - slotOffset - 1) % 5) + 1;
|
||||
int slotToMatch = (currentSlot - slotOffset - 1);
|
||||
if(slotToMatch < 0){
|
||||
slotToMatch += 5; //easy way to loop around if we go under the minimum slot
|
||||
}
|
||||
//printfline("what slot %i", slotToMatch);
|
||||
|
||||
slotToMatch = (int)((((float)slotToMatch) % 5)) + 1;
|
||||
|
||||
if(slotToMatch < 5){
|
||||
for(int i = weaponID_toStartFromThisSlot-1; i >= 0; i--){
|
||||
//where does a weapon match our slot? pick it.
|
||||
weapondynamic_t dynaOtherRef;
|
||||
dynaOtherRef = pl.ary_myWeapons[i];
|
||||
|
||||
weapondata_basic_t* weaponBasicOtherP = ary_weaponData[dynaOtherRef.weaponID];
|
||||
weapondata_basic_t weaponBasicOtherRef = *weaponBasicOtherP;
|
||||
|
||||
if(weaponBasicOtherRef.iInventorySlot == slotToMatch){
|
||||
//woohoo
|
||||
//foundNewWeapon = TRUE;
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
//printfline("HUD_DrawWeaponSelect_NextItem NON-AKIMBO WEAPON %i, ITS NAME: %s", i, weaponBasicOtherRef.sDisplayName);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//oh my
|
||||
//if(pl.weaponSelectHighlightAkimbo){
|
||||
// //don't reset toStartFrom. Offset is intentional.
|
||||
//}
|
||||
|
||||
for(int i = weaponID_toStartFromThisSlot-1; i >= 0; i--){
|
||||
// where does a weapon match our slot? pick it.
|
||||
weapondynamic_t dynaOtherRef;
|
||||
dynaOtherRef = pl.ary_myWeapons[i];
|
||||
|
||||
weapondata_basic_t* weaponBasicOtherP = ary_weaponData[dynaOtherRef.weaponID];
|
||||
weapondata_basic_t weaponBasicOtherRef = *weaponBasicOtherP;
|
||||
|
||||
if(weaponBasicOtherRef.iAkimboID > 0 && dynaOtherRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO){
|
||||
//this weapon is akimbo, yay.
|
||||
pl.weaponSelectHighlightAkimbo = TRUE;
|
||||
return i;
|
||||
}
|
||||
|
||||
if(weaponBasicOtherRef.iInventorySlot == slotToMatch){
|
||||
//To handle stand-alone akimbo weapons found.
|
||||
|
||||
if(dynaOtherRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO){
|
||||
pl.weaponSelectHighlightAkimbo = TRUE;
|
||||
}else{
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//Moving on to check a different slot? Reset weaponID_toStartFromThisSlot then
|
||||
// hack to make it start at ary_myWeapons_softMax. That is not having "- 1".
|
||||
weaponID_toStartFromThisSlot = pl.ary_myWeapons_softMax;
|
||||
|
||||
if(scheduleTermination){
|
||||
//last shot we got.
|
||||
break;
|
||||
}
|
||||
|
||||
if(freeSlotMovement == TRUE){
|
||||
|
||||
}else{
|
||||
slotOffset -= 1; //keep the slot exactly the same
|
||||
|
||||
scheduleTermination = TRUE;
|
||||
}
|
||||
}//END OF LOOP THROUGH SLOTS
|
||||
|
||||
|
||||
//printfline("END. weaponID_toStartFrom:%i", weaponID_toStartFrom);
|
||||
// is "- 1" always a good idea here?
|
||||
return weaponID_toStartFrom - 1; //uhhhh. what?? guess there would be no change in this odd case
|
||||
}//HUD_DrawWeaponSelect_NextItem
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
HUD_DrawWeaponSelect_Forward(void)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if(pl.weaponSelectHighlightID == -1){
|
||||
//pick the weapon after then
|
||||
pl.weaponSelectHighlightAkimbo = pl.weaponEquippedAkimbo;
|
||||
pl.weaponSelectHighlightID = HUD_DrawWeaponSelect_NextItem(pl.inventoryEquippedIndex, TRUE);
|
||||
|
||||
//printfline("HUD_DrawWeaponSelect_Forward A? %i", pl.weaponSelectHighlightID);
|
||||
|
||||
if(pl.weaponSelectHighlightID != -1){
|
||||
sound(pl, CHAN_ITEM, "common/wpn_hudon.wav", 0.5, ATTN_NONE);
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
}else{
|
||||
pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
}
|
||||
}else{
|
||||
|
||||
//printfline("HUD_DrawWeaponSelect_Forward B? %i", pl.weaponSelectHighlightID);
|
||||
|
||||
pl.weaponSelectHighlightID = HUD_DrawWeaponSelect_NextItem(pl.weaponSelectHighlightID, TRUE);
|
||||
if(pl.weaponSelectHighlightID != -1){
|
||||
sound(pl, CHAN_ITEM, "common/wpn_moveselect.wav", 0.5, ATTN_NONE);
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
}else{
|
||||
pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
HUD_DrawWeaponSelect_Back(void)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if(pl.weaponSelectHighlightID == -1){
|
||||
//pick the weapon after then
|
||||
pl.weaponSelectHighlightAkimbo = pl.weaponEquippedAkimbo;
|
||||
pl.weaponSelectHighlightID = HUD_DrawWeaponSelect_PreviousItem(pl.inventoryEquippedIndex, TRUE);
|
||||
|
||||
//printfline("HUD_DrawWeaponSelect_Back C? %i", pl.weaponSelectHighlightID);
|
||||
|
||||
if(pl.weaponSelectHighlightID != -1){
|
||||
sound(pl, CHAN_ITEM, "common/wpn_hudon.wav", 0.5, ATTN_NONE);
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
}else{
|
||||
pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
}
|
||||
}else{
|
||||
|
||||
//printfline("HUD_DrawWeaponSelect_Back D? %i", pl.weaponSelectHighlightID);
|
||||
|
||||
pl.weaponSelectHighlightID = HUD_DrawWeaponSelect_PreviousItem(pl.weaponSelectHighlightID, TRUE);
|
||||
if(pl.weaponSelectHighlightID != -1){
|
||||
sound(pl, CHAN_ITEM, "common/wpn_moveselect.wav", 0.5, ATTN_NONE);
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
}else{
|
||||
pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO!!! also for this to be supported instead of HUD_DrawWeaponSelect_CheckClick,
|
||||
// need to involve "m_iHUDWeaponSelected" elsewhere in this file properly!
|
||||
// _Trigger is not even called if iHUDWeaponSelected is FALSE !
|
||||
void
|
||||
HUD_DrawWeaponSelect_Trigger(void)
|
||||
{
|
||||
//TAGGG - TODO - was commented out in old FreeTS, doing so here too.
|
||||
// But figure out what it needs to do, this likely wasn't pointless
|
||||
/*
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
pl.setInventoryEquippedIndex(pSeat->m_iHUDWeaponSelected);
|
||||
sendevent("PlayerSwitchWeapon", "i", pSeat->m_iHUDWeaponSelected);
|
||||
sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_select.wav", 0.5f, ATTN_NONE);
|
||||
pSeat->m_iHUDWeaponSelected = pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
*/
|
||||
|
||||
// Redirect!
|
||||
// ACTUALLY not yet. This redirect won't work until script
|
||||
// involving m_iHUDWeaponSelected is properly hooked up!
|
||||
|
||||
// NEVERMIND. Attempt to stop holding down primary fire while pushing number keys
|
||||
// to open weapon-select instantly picking a weapon.
|
||||
// It works, but can be annoying as this completly blocks the ability to tell
|
||||
// what is or isn't a fresh key press during that time. Not worth it.
|
||||
/*
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
if(pSeat->m_flInputBlockTime > time){
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
HUD_DrawWeaponSelect_CheckClick();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Cheap way to instantly close the weapon select area
|
||||
BOOLEAN
|
||||
HUD_CloseWeaponSelect(BOOL playOffSound)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if(pSeat->m_flHUDWeaponSelectTime != -1){
|
||||
|
||||
if(playOffSound && getplayerkeyvalue(player_localnum, "*spec") == "0" ){
|
||||
sound(pl, CHAN_ITEM, "common/wpn_hudoff.wav", 0.5, ATTN_NONE);
|
||||
}
|
||||
//pSeat->fHUDWeaponSelected = 0; //no harm but no need now
|
||||
|
||||
//TAGGG - only makes sense.
|
||||
pl.weaponSelectHighlightID = -1;
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
|
||||
pSeat->m_flHUDWeaponSelectTime = -1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE; //did not.
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
HUD_DrawWeaponSelect
|
||||
|
||||
Drawn every frame through HUD.c
|
||||
=================
|
||||
*/
|
||||
void
|
||||
HUD_DrawWeaponSelect(void)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if ( pSeat->m_flHUDWeaponSelectTime < time) {
|
||||
//if (pSeat->fHUDWeaponSelected) {
|
||||
|
||||
// only need to play the sound the first time, but keep blocking
|
||||
// the below methods.
|
||||
if(pSeat->m_flHUDWeaponSelectTime != -1){
|
||||
pSeat->m_flHUDWeaponSelectTime = -1;
|
||||
|
||||
//printfline("HUD_DrawWeaponSelect IM closin my weapon select 1");
|
||||
|
||||
HUD_CloseWeaponSelect(TRUE);
|
||||
}
|
||||
|
||||
//}
|
||||
return;
|
||||
}
|
||||
|
||||
//TAGGG - TEST THIS!!! Does the compile order allow this??
|
||||
drawPlayerInventory_buy(FALSE);
|
||||
|
||||
drawPlayerInventory_TopBar(-1, FALSE);
|
||||
|
||||
// FreeHL WAY!
|
||||
/*
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
if (!pl.inventoryEquippedIndex) {
|
||||
return;
|
||||
}
|
||||
if (pSeat->m_flHUDWeaponSelectTime < time) {
|
||||
if (pSeat->m_iHUDWeaponSelected) {
|
||||
sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_hudoff.wav", 0.5, ATTN_NONE);
|
||||
pSeat->m_iHUDWeaponSelected = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
vector vecPos = g_hudmins + [16,16];
|
||||
|
||||
int b;
|
||||
int wantslot = g_weapons[pSeat->m_iHUDWeaponSelected].slot;
|
||||
int wantpos = g_weapons[pSeat->m_iHUDWeaponSelected].slot_pos;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int slot_selected = 0;
|
||||
vecPos[1] = g_hudmins[1] + 16;
|
||||
HUD_DrawWeaponSelect_Num(vecPos, i);
|
||||
vecPos[1] += 20;
|
||||
for (int x = 0; x < 32; x++) {
|
||||
if (i == wantslot) {
|
||||
slot_selected = TRUE;
|
||||
if (x == wantpos) {
|
||||
// Selected Sprite
|
||||
Weapons_HUDPic(pSeat->m_iHUDWeaponSelected, 1, vecPos, 1.0f);
|
||||
drawsubpic(vecPos, [170,45], g_hud3_spr,
|
||||
[0,180/256], [170/256,45/256], g_hud_color, 1, DRAWFLAG_ADDITIVE);
|
||||
vecPos[1] += 50;
|
||||
} else if ((b=HUD_InSlotPos(i, x)) != -1) {
|
||||
// Unselected Sprite
|
||||
Weapons_HUDPic(b, 0, vecPos, 1.0f);
|
||||
vecPos[1] += 50;
|
||||
}
|
||||
} else if (HUD_InSlotPos(i, x) != -1) {
|
||||
HUD_DrawWeaponSelect_Num(vecPos, 5);
|
||||
vecPos[1] += 25;
|
||||
}
|
||||
}
|
||||
|
||||
if (slot_selected == TRUE) {
|
||||
vecPos[0] += 175;
|
||||
} else {
|
||||
vecPos[0] += 25;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}//END OF HUD_DrawWeaponSelect
|
||||
|
||||
|
||||
|
||||
|
||||
//if the user clicked (by calling this mehtod we assume they did), we see
|
||||
//if the user was in weapon select. If so, pick it.
|
||||
BOOLEAN
|
||||
HUD_DrawWeaponSelect_CheckClick(void)
|
||||
{
|
||||
if(getplayerkeyvalue(player_localnum, "*spec") != "0"){
|
||||
//some form of spectator? NOT ALLOWED.
|
||||
//printfline("HEY there closing my weapon select 2");
|
||||
HUD_CloseWeaponSelect(FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if(pl.weaponSelectHighlightID != -1){
|
||||
// clicking picks it then.
|
||||
|
||||
pSeat->m_flHUDWeaponSelectTime = -1;
|
||||
|
||||
// don't get overridden by the draw sound (CHAN_AUTO, not CHAN_ITEM)
|
||||
// ...or really, just don't play this at all. The TS weapon is much more silent anyway,
|
||||
// needs more sound changes. Removals?
|
||||
//sound(pl, CHAN_AUTO, "common/wpn_select.wav", 0.5f, ATTN_NONE);
|
||||
|
||||
|
||||
TS_playerEquippedWeapon(pl, pl.weaponSelectHighlightID, pl.weaponSelectHighlightAkimbo);
|
||||
|
||||
pl.weaponSelectHighlightID = -1;
|
||||
pl.weaponSelectHighlightAkimbo = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}else{
|
||||
return FALSE; //didn't change the weapon.
|
||||
}
|
||||
}//HUD_DrawWeaponSelect_CheckClick
|
||||
|
||||
|
||||
void
|
||||
HUD_DrawWeaponSelect_Last(void)
|
||||
{
|
||||
//TAGGG - TODO. Support this.
|
||||
/*
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
if (pl.g_items & g_weapons[pSeat->m_iOldWeapon].id) {
|
||||
pl.setInventoryEquippedIndex(pSeat->m_iOldWeapon);
|
||||
sendevent("PlayerSwitchWeapon", "i", pSeat->m_iOldWeapon);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
void
|
||||
HUD_DrawWeaponSelect_Num(vector vecPos, float fValue)
|
||||
{
|
||||
drawsubpic(vecPos, [20,20], g_hud7_spr, g_vecHUDNums[fValue], [20/256, 20/128], g_hud_color, 1, DRAWFLAG_ADDITIVE);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
int
|
||||
HUD_InSlotPos(int slot, int pos)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
for (int i = 1; i < g_weapons.length; i++) {
|
||||
if (g_weapons[i].slot == slot && g_weapons[i].slot_pos == pos) {
|
||||
if (pl.g_items & g_weapons[i].id) {
|
||||
return i;
|
||||
} else {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
TS_SelectSlot(int slotPicked)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
// Keep track of the currently open slot. Navigate through it...
|
||||
// move to another slot if needed...
|
||||
|
||||
|
||||
//printfline("weapon slot currently highlighted: %i", pl.weaponSelectHighlightID);
|
||||
|
||||
if(pl.weaponSelectHighlightID == -1){
|
||||
//pick the weapon after then
|
||||
|
||||
pl.weaponSelectHighlightAkimbo = pl.weaponEquippedAkimbo;
|
||||
|
||||
|
||||
pl.weaponSelectHighlightID = HUD_DrawWeaponSelect_PreviousItem(-1, FALSE, slotPicked);
|
||||
if(pl.weaponSelectHighlightID != -1){
|
||||
sound(pl, CHAN_ITEM, "common/wpn_hudon.wav", 0.5, ATTN_NONE);
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
}else{
|
||||
pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
}
|
||||
}else{
|
||||
// uhhhh... try going previous of -1 then? Hits the first weapon we got I think.
|
||||
pl.weaponSelectHighlightID = HUD_DrawWeaponSelect_PreviousItem(pl.weaponSelectHighlightID, FALSE, slotPicked);
|
||||
if(pl.weaponSelectHighlightID != -1){
|
||||
sound(pl, CHAN_ITEM, "common/wpn_moveselect.wav", 0.5, ATTN_NONE);
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
}else{
|
||||
pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}//TS_SelectSlot
|
||||
|
||||
|
||||
void
|
||||
HUD_SlotSelect(int slot)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
//printlinef("HUD_SlotSelect: %i maxweap:%i classname:%s\n", slot, pl.ary_myWeapons_softMax, pl.classname);
|
||||
|
||||
if(getplayerkeyvalue(player_localnum, "*spec") != "0"){
|
||||
// A spectator? Stop.
|
||||
return;
|
||||
}
|
||||
|
||||
//TAGGG - redirect.
|
||||
TS_SelectSlot(slot + 1);
|
||||
|
||||
|
||||
/*
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
int curslot = g_weapons[pSeat->m_iHUDWeaponSelected].slot;
|
||||
int i;
|
||||
|
||||
if (g_textmenu != "") {
|
||||
Textmenu_Input(slot);
|
||||
return;
|
||||
}
|
||||
|
||||
// hack to see if we have ANY weapons at all.
|
||||
if (!pl.inventoryEquippedIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pSeat->m_flHUDWeaponSelectTime < time) {
|
||||
sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_hudon.wav", 0.5, ATTN_NONE);
|
||||
} else {
|
||||
sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_moveselect.wav", 0.5, ATTN_NONE);
|
||||
}
|
||||
|
||||
// weren't in that slot? select the first one then
|
||||
if (curslot != slot) {
|
||||
for (i = 1; i < g_weapons.length; i++) {
|
||||
if (g_weapons[i].slot == slot && pl.g_items & g_weapons[i].id) {
|
||||
pSeat->m_iHUDWeaponSelected = i;
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int first = -1;
|
||||
for (i = 1; i < g_weapons.length; i++) {
|
||||
if (g_weapons[i].slot == slot && pl.g_items & g_weapons[i].id) {
|
||||
if (i < pSeat->m_iHUDWeaponSelected && first == -1) {
|
||||
first = i;
|
||||
} else if (i > pSeat->m_iHUDWeaponSelected) {
|
||||
first = -1;
|
||||
pSeat->m_iHUDWeaponSelected = i;
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first > 0) {
|
||||
pSeat->m_iHUDWeaponSelected = first;
|
||||
pSeat->m_flHUDWeaponSelectTime = time + 3;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
188
src/client/init.qc
Normal file
188
src/client/init.qc
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
// should this even be defaulted?
|
||||
var float numclientseats_highest = 0;
|
||||
|
||||
/*
|
||||
=================
|
||||
ClientGame_Init
|
||||
|
||||
Comparable to worldspawn in SSQC in that it's mostly used for precaches
|
||||
=================
|
||||
*/
|
||||
void
|
||||
ClientGame_Init(float apilevel, string enginename, float engineversion)
|
||||
{
|
||||
int s;
|
||||
printfline("---ClientGame_Init---");
|
||||
|
||||
printf("What is numclientseats? %d\n", numclientseats);
|
||||
|
||||
// !!!
|
||||
// Use g_seats.length instead to fill all seats, even those not intended for use, or
|
||||
// in case of a change of numclientseats (was 1, but someone joins for multiplayer on
|
||||
// the same machine maybe)? I have no idea how or even if that could happen in the
|
||||
// same run.
|
||||
// src/client/view.qc's View_Init works only with the current number of numclientseats
|
||||
// at least, for reference.
|
||||
// Although it is called by CSQC_RendererRestarted, which may happen anytime
|
||||
// numclientseats is adjusted.
|
||||
// Moved to our ClientGame_RendererRestart for safety, change how this works if my
|
||||
// understanding of pSeat isn't quite there.
|
||||
|
||||
// safety
|
||||
if(numclientseats > g_seats.length){numclientseats = g_seats.length;}
|
||||
for(s = numclientseats-1; s >= 0; s--){
|
||||
pSeat = &g_seats[s];
|
||||
pSeatLocal = &g_seatslocal[s];
|
||||
pSeatLocal_init();
|
||||
ClientInfo_init(&pSeatLocal->m_clientinfo);
|
||||
}
|
||||
numclientseats_highest = numclientseats;
|
||||
|
||||
|
||||
//TAGGG - NEW.
|
||||
SharedGame_Init();
|
||||
// Nope! Not calling ClientGame_Precache here, use "ClientGame_RendererRestart"
|
||||
// where all the other precaches are now. It is called at init as well, and
|
||||
// called later as needed, unlike here.
|
||||
|
||||
|
||||
Obituary_Init();
|
||||
|
||||
// over stuff removed
|
||||
//precache_pic( sprintf( "overviews/%s.bmp", mapname ) );
|
||||
|
||||
registercommand("getorigin");
|
||||
registercommand("getangle");
|
||||
|
||||
registercommand("firemode");
|
||||
registercommand("useitems");
|
||||
registercommand("usepowerup");
|
||||
registercommand("+alt1");
|
||||
registercommand("-alt1");
|
||||
registercommand("+alt2");
|
||||
registercommand("-alt2");
|
||||
|
||||
|
||||
registercommand("dev_testorbituary");
|
||||
registercommand("minimap");
|
||||
registercommand("overview_test");
|
||||
|
||||
registercommand("buy");
|
||||
registercommand("motd");
|
||||
//registercommand("chooseteam");
|
||||
|
||||
// NO NEED! See CSEv_DropWeapon in nuclide's src/server/weapons.qc, that gets called.
|
||||
// I think? Might want to verify.
|
||||
registercommand("drop");
|
||||
|
||||
|
||||
// QUESTION - good luck with however this is supposed to varry between multiple
|
||||
// split-screen clients (g_seat[#]) across the same game client!
|
||||
// Can CVars even be per-splitscreen client, yet it feels like some are
|
||||
// begging to be.
|
||||
cvar_set("fov", ftos(autocvar_fov_default));
|
||||
|
||||
|
||||
// WARNING! Any font-stuff below may be rendered completley obsolete by
|
||||
// using the new VGUI approach.
|
||||
// That also includes mentions of fonts in ts/src/client/vgui.qc
|
||||
|
||||
//TAGGG - INCLUSION.
|
||||
// Also see ts/src/client/vgui.qc where CSQC_VGUI_Draw checks to see if the screen
|
||||
// size has been changed or this is the first time drawing (some FONTs having ID -1).
|
||||
// In either case, they're loaded and sized per screen height over there.
|
||||
FONT_ARIAL = -1; //specify me at draw startup instead, if this is safe.
|
||||
// This allows this to adjust for screen size.
|
||||
FONT_ARIAL_TITLE = -1;
|
||||
// ALSO, beware. arialbd.ttf can be included in projects, (check common gfx/shell
|
||||
// directories, like within any .pk3dir folders).
|
||||
// As of this time, the only place should be
|
||||
// platform/menu_fonts.pk3dir/gfx/shell/arialbd.ttf
|
||||
// Even so, this reference to the file does not involve the "gfx/shell/" portion,
|
||||
// so it will only search system libraries for it instead.
|
||||
// As for how to substitute with the local copy of arialbd.ttf if it is detected
|
||||
// to be missing from the OS, no idea, because even figuring that out seems trick.
|
||||
// Any "loadfont" call returns an int that isn't -1 actually, even if it finds
|
||||
// nothing at all. And consistently, subsequent calls to the exact same file/path
|
||||
// for "loadfont" will also return the exact same number, as a remembered "type"
|
||||
// of missing? No idea. (missing uses the FTE default font, which is much more
|
||||
// sprite/pixel-y looking than most other fonts in lack of a better term).
|
||||
// BLABLABLA. "arialbd.ttf" without the "gfx/shell/" in front is searching the OS,
|
||||
// not your own gamemod or platform directories (which ought to have it in gfx/shell).
|
||||
// Forget it! I'm using our own platform supplied one, "gfx/shell/" it is.
|
||||
FONT_ARIAL_STD = loadfont( "", "gfx/shell/arialbd.ttf", "14", -1 );
|
||||
FONT_ARIAL_NUMSLASH = loadfont( "", "gfx/shell/arialbd.ttf", "16", -1 );
|
||||
FONT_ARIAL_SCOPEMAG = loadfont( "", "gfx/shell/arialbd.ttf", "17", -1 );
|
||||
|
||||
// safe default for whatever doesn't specify it?
|
||||
// Beware that draw calls that don't set the drawfont will still rely
|
||||
// on the previously set one, although that could be intentional too.
|
||||
drawfont = FONT_CON;
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
CSQC_VGUI_Init();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ClientGame_InitDone(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ClientGame_RendererRestart(string rstr)
|
||||
{
|
||||
int s;
|
||||
|
||||
printfline("---ClientGame_RendererRestart---");
|
||||
printf("What is numclientseats? %d\n", numclientseats);
|
||||
|
||||
// safety
|
||||
if(numclientseats > g_seats.length){numclientseats = g_seats.length;}
|
||||
|
||||
|
||||
// Did numclientseats change, and it's higher than the previous choice?
|
||||
// Just init the additional ones then.
|
||||
if(numclientseats > numclientseats_highest){
|
||||
for(s = numclientseats-1; s >= numclientseats_highest; s--){
|
||||
pSeat = &g_seats[s];
|
||||
pSeatLocal = &g_seatslocal[s];
|
||||
pSeatLocal_init();
|
||||
ClientInfo_init(&pSeatLocal->m_clientinfo);
|
||||
}
|
||||
numclientseats_highest = numclientseats;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TAGGG - Hook into precache.qc
|
||||
ClientGame_Precache();
|
||||
|
||||
|
||||
Obituary_Precache();
|
||||
|
||||
FX_Blood_Init();
|
||||
FX_BreakModel_Init();
|
||||
FX_Explosion_Init();
|
||||
FX_GibHuman_Init();
|
||||
FX_Spark_Init();
|
||||
FX_Impact_Init();
|
||||
|
||||
}
|
4
src/client/input.h
Normal file
4
src/client/input.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
void GameClient_SpectatorInputRaw(void)
|
||||
void GameClient_PlayerInputRaw(void);
|
190
src/client/input.qc
Normal file
190
src/client/input.qc
Normal file
|
@ -0,0 +1,190 @@
|
|||
|
||||
// NEW FILE.
|
||||
|
||||
// WARNING: Do not get this file mixed up with <game>/src/shared/input.qc, that one is
|
||||
// only called when the player is spawned (collision, seen by others, etc.).
|
||||
// This is for checking to see if the user performed some action that does not need
|
||||
// to be checked by the server, such as INPUT_BUTTON0 (primary fire) while not in any
|
||||
// VGUI choice (blank screen) to send a message to the server to swawn the player.
|
||||
// This is reached through draw-calls (root of the calls is method CSQC_UpdateView).
|
||||
// It works.
|
||||
|
||||
// ALSO, this is called continually to check for user-provided input, it is not only
|
||||
// called when user input is detected. Be aware of that.
|
||||
|
||||
|
||||
// Also, this version is for while in spectator. See further down for the "for-player"
|
||||
// version.
|
||||
void
|
||||
GameClient_SpectatorInputRaw(void)
|
||||
{
|
||||
// If in spectator with nothing open (no MoTD, no buyside menu),
|
||||
// go ahead and spawn ingame.
|
||||
|
||||
if(pSeatLocal->fVGUI_Display == VGUI_SCREEN::NONE && pSeatLocal->m_flPrevVGUI != VGUI_SCREEN::NONE){
|
||||
// Current display is NONE, yet the previous wasn't (Recent change to NONE)?
|
||||
// Set that.
|
||||
pSeatLocal->m_bNeedPrimaryRelease = TRUE;
|
||||
pSeatLocal->m_flReleaseTime = time + 0.15f;
|
||||
}
|
||||
|
||||
pSeatLocal->m_flPrevVGUI = pSeatLocal->fVGUI_Display;
|
||||
|
||||
// OKAY. So little issue.
|
||||
// Modern Nuclide does not offer a way to read "input_buttons" in the usual places
|
||||
// (CSQC_Input_Frame), can they be re-gathered?
|
||||
|
||||
// COPIED FROM src/client/predict.qc, for scraping through
|
||||
// all queued input frames for sending (or not yet verified to have
|
||||
// been received by the server. I think?).
|
||||
// Or use the one at clientcommandframe only. Hmm.
|
||||
|
||||
// quote from fteextensions.qc:
|
||||
// The sequence number range used for prediction should normally be
|
||||
// servercommandframe < sequence <= clientcommandframe.
|
||||
|
||||
//printf("WHAT are the client/server comm frames? %d %d\n", clientcommandframe, servercommandframe);
|
||||
//for (int i = pl.sequence + 1; i <= clientcommandframe; i++) {
|
||||
|
||||
/*
|
||||
bool wasButtonPushedThisFrame = FALSE;
|
||||
for (int i = servercommandframe+1; i <= clientcommandframe; i++) {
|
||||
float flSuccess = getinputstate(i);
|
||||
if (flSuccess == FALSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//if (i==clientcommandframe){
|
||||
// CSQC_Input_Frame();
|
||||
//}
|
||||
|
||||
if (input_timelength == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if((input_buttons & INPUT_BUTTON0) != 0){
|
||||
//printfline("IM here man %d\n", (input_buttons & INPUT_BUTTON0) != 0);
|
||||
// any frame says I got pushed? Treat it as such.
|
||||
wasButtonPushedThisFrame = TRUE;
|
||||
}
|
||||
|
||||
//input_sequence = i;
|
||||
}
|
||||
*/
|
||||
|
||||
float flSuccess = getinputstate(clientcommandframe);
|
||||
if (flSuccess) {
|
||||
//printf("BUT HOW? %d - %d\n", (pSeatLocal->m_bNeedPrimaryRelease), (( input_buttons & INPUT_BUTTON0)!=0) );
|
||||
|
||||
// IDEA: could we just do this?
|
||||
/*
|
||||
if(pSeatLocal->m_bNeedPrimaryRelease){
|
||||
if(!wasButtonPushedThisFrame)){
|
||||
pSeatLocal->m_bNeedPrimaryRelease = FALSE;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// INSTEAD OF THIS
|
||||
///////////////////////////////////////////////////////
|
||||
if(pSeatLocal->m_bNeedPrimaryRelease){
|
||||
// yay.
|
||||
if(!(input_buttons & INPUT_BUTTON0)){
|
||||
// not pushed? Check it
|
||||
if(time >= pSeatLocal->m_flReleaseTime){
|
||||
// okay! Not touched for enough time.
|
||||
pSeatLocal->m_bNeedPrimaryRelease = FALSE;
|
||||
}
|
||||
}else{
|
||||
// Touched? Oh.
|
||||
pSeatLocal->m_flReleaseTime = time + 0.15f;
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// primary fire?
|
||||
if(!pSeatLocal->m_bNeedPrimaryRelease && (input_buttons & INPUT_BUTTON0) ){
|
||||
|
||||
if(
|
||||
pSeatLocal->fVGUI_Display == VGUI_SCREEN::NONE &&
|
||||
getplayerkeyvalue(player_localnum, "*spec") != "0"
|
||||
){
|
||||
// && getstati(STAT_RULE_ALLOW_SPAWN))
|
||||
// just send the intention we want to spawn, the server will see if a delay is needed.
|
||||
// And only work if we're not in some other screen AND not spawned. Clicking to spawn while ingame (die) would be irritating.
|
||||
|
||||
// Check is no longer needed, only the spectator would have reached this method to begin with.
|
||||
//if( stof(getplayerkeyvalue(player_localnum, "*team")) == TEAM_SPECTATOR)
|
||||
|
||||
//TAGGG - TODO - should some minimum cooldown before respawning be enforced,
|
||||
// and the countdown shows up if the user clicks too soon since a respawn?
|
||||
// Print this if the client suspects that will be the case, or let spawn-delay
|
||||
// be some serverstat that is known here at all times.
|
||||
//CSQC_Parse_CenterPrint("Spawning soon...\n");
|
||||
|
||||
sendevent( "GamePlayerSpawn", "");
|
||||
|
||||
VGUI_ChangeScreen(VGUI_SCREEN::NONE);
|
||||
|
||||
// probably unnecessary?
|
||||
EV_TS_resetViewModel();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}//GameClient_SpectatorInputRaw
|
||||
|
||||
|
||||
// While a player.
|
||||
void
|
||||
GameClient_PlayerInputRaw(void)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
// weapon select extra.
|
||||
if(pl == NULL){
|
||||
// ???
|
||||
return;
|
||||
}
|
||||
|
||||
// This was removed? Legacy VGUI
|
||||
// If we are inside a VGUI, don't let the client do stuff outside
|
||||
if ((pSeatLocal->fVGUI_Display != VGUI_SCREEN::NONE)) {
|
||||
pSeat->m_flInputBlockTime = time + 0.2;
|
||||
}
|
||||
|
||||
// we're changing this up a little.
|
||||
// We'll call a method to let weapon-select related script determine whether
|
||||
// the player is going through weapon selection when they clicked, not here.
|
||||
// That is we just call it if a click was detected, it does the rest.
|
||||
// It will return TRUE if it caused the current weapon to be changed
|
||||
// (since we don't want to send a fire order below like left-clicking usually does)
|
||||
// NO NEED FOR THIS CHECK, Nuclide does HUD_DrawWeaponSelect_Trigger on detecting a
|
||||
// click while something is selected in weapon select.
|
||||
// WAIT! Keep for now, other things have to be changed internally for _Trigger to
|
||||
// work right.
|
||||
|
||||
/*
|
||||
// Nuclide calls the Trigger method fine again
|
||||
if(input_buttons & INPUT_BUTTON0){
|
||||
if(HUD_DrawWeaponSelect_CheckClick()){
|
||||
input_buttons = 0;
|
||||
pSeat->m_flInputBlockTime = time + 0.2;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//TAGGG - this block is all new. apparently this is right-click.
|
||||
if(pSeat->m_iInputAttack2){ //input_buttons & INPUT_BUTTON3){
|
||||
if(HUD_CloseWeaponSelect(TRUE)){
|
||||
pSeat->m_flInputBlockTime = time + 0.2;
|
||||
input_impulse = 0;
|
||||
input_buttons = 0;
|
||||
pSeat->m_iInputAttack2 = FALSE;
|
||||
}else{
|
||||
//pSeat->m_iInputAttack2 = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
}//GameClient_PlayerInputRaw
|
||||
|
||||
|
70
src/client/inventory_logic_draw.h
Normal file
70
src/client/inventory_logic_draw.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
|
||||
|
||||
|
||||
//sample weapon. Fill its sFilePath with whatever weapon to draw and use.
|
||||
//Better than storing 40+ copies of width/height numbers that never change?
|
||||
var imageFileRef_t img_weapon = {"", 128, 48};
|
||||
|
||||
|
||||
|
||||
|
||||
CREATE_imageFileRef_t(img_item_installed, "sprites/weapons/item_installed.spr", 16, 16)
|
||||
//item_not_installed.spr ???
|
||||
CREATE_imageFileRef_t(img_health, "sprites/player/health.spr", 32, 24)
|
||||
CREATE_imageFileRef_t(img_kevlar, "sprites/player/kevlar.spr", 32, 24)
|
||||
|
||||
|
||||
|
||||
CREATE_imageFileRef_t(img_player_stand, "sprites/player/stand.spr", 64, 48)
|
||||
CREATE_imageFileRef_t(img_player_crouch, "sprites/player/crouched.spr", 64, 48)
|
||||
CREATE_imageFileRef_t(img_player_prone, "sprites/player/prone.spr", 64, 48)
|
||||
CREATE_imageFileRef_t(img_player_jump, "sprites/player/movement.spr", 48, 48)
|
||||
|
||||
CREATE_imageFileRef_t(img_player_kungfu, "sprites/player/kungfu.spr", 48, 48)
|
||||
|
||||
|
||||
|
||||
//CREATE_imageFileRef_t(img_numbers, "sprites/numbers.spr_0.tga", 112, 16)
|
||||
CREATE_imageFileRef_t(img_numbers, "sprites/numbers.spr", 112, 16)
|
||||
//CREATE_imageCropBounds_t(spr_number0, img_numbers, 5, 4, 22, 7);
|
||||
CREATE_imageCropBounds_t(spr_number0, img_numbers, 1, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number1, img_numbers, 11, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number2, img_numbers, 21, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number3, img_numbers, 32, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number4, img_numbers, 44, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number5, img_numbers, 55, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number6, img_numbers, 67, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number7, img_numbers, 79, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number8, img_numbers, 90, 0, 10, 16)
|
||||
CREATE_imageCropBounds_t(spr_number9, img_numbers, 102, 0, 10, 16)
|
||||
|
||||
|
||||
imageCropBounds_t ary_LCD_numberSet[] = {
|
||||
spr_number0,
|
||||
spr_number1,
|
||||
spr_number2,
|
||||
spr_number3,
|
||||
spr_number4,
|
||||
spr_number5,
|
||||
spr_number6,
|
||||
spr_number7,
|
||||
spr_number8,
|
||||
spr_number9
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
void drawWeaponOptionBar(vector arg_vDrawOrigin, string arg_sOptionName, BOOLEAN arg_fBrightLight, float arg_opac);
|
||||
void drawPlayerInventory_TopBar(int arg_iSlotSelected, BOOL arg_fBuyMode);
|
||||
void drawPlayerInventory_buy(BOOL arg_fBuyMode);
|
||||
|
||||
void drawPlayerInventory_place(int arg_iSlot, int arg_iRow, string arg_sWeaponSpritePath, string arg_sSelectedWeaponDisplayName, BOOL arg_fBuyMode, optional int ammoCount, optional BOOL hasAnyAmmo, optional int bitsUpgradeOpts);
|
||||
|
||||
void drawPlayerCurrentWeaponStats(void);
|
||||
void drawPlayerStats(void);
|
||||
|
||||
void drawTimer(void);
|
||||
|
||||
|
||||
|
491
src/client/inventory_logic_draw.qc
Normal file
491
src/client/inventory_logic_draw.qc
Normal file
|
@ -0,0 +1,491 @@
|
|||
|
||||
|
||||
// at the bottom-right. Used to show which of the silencer, lasersight, scope, or flashlight was purchased.
|
||||
void
|
||||
drawWeaponOptionBar(
|
||||
vector arg_vDrawOrigin, string arg_sOptionName, BOOLEAN arg_fBrightLight,
|
||||
float arg_opac
|
||||
)
|
||||
{
|
||||
drawfill( arg_vDrawOrigin, [128, 19], clrPaleBlue, arg_opac - 0.55f );
|
||||
Gfx_Text( [arg_vDrawOrigin.x + 2, arg_vDrawOrigin.y + 4], arg_sOptionName, vButtonFontSize, clrPaleBlue, 0.90f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
|
||||
//arg_vDrawOrigin.y -= 20;
|
||||
//will not work.
|
||||
vector tempVec = [arg_vDrawOrigin.x + 112, arg_vDrawOrigin.y + 1];
|
||||
|
||||
if(arg_fBrightLight){
|
||||
DRAW_IMAGE_ADDITIVE(img_item_installed, tempVec, clrPaleBlue, arg_opac + 0.15f)
|
||||
}else{
|
||||
DRAW_IMAGE_ADDITIVE(img_item_installed, tempVec, clrPaleBlue, arg_opac - 0.50f)
|
||||
}
|
||||
}//drawWeaponOptionBar
|
||||
|
||||
|
||||
void
|
||||
drawPlayerInventory_TopBar(int arg_iSlotSelected, BOOL arg_fBuyMode)
|
||||
{
|
||||
vector vWeaponsBar_drawLoc;
|
||||
vector vWeaponsBar_drawBase;
|
||||
|
||||
if(arg_fBuyMode){
|
||||
// draw the pre-bar
|
||||
drawfill( [8, 8], [640, 19], clrPaleBlue, 0.96f - 0.60f );
|
||||
vWeaponsBar_drawBase = [8, 28];
|
||||
|
||||
// TODO - fetch these globally
|
||||
if(!getstati(STAT_RULE_MONEYALLOWED)){
|
||||
Gfx_Text( [8, 8 + 2], sprintf("Order Value: %i", pSeatLocal->m_clientinfo.weaponconfig_temp.iTotalPrice), vButtonFontSize, clrPaleBlue, 0.93f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
}else{
|
||||
Gfx_Text( [8, 8 + 2], sprintf("Cash: %i (Order Cost: %i)", getstati(STAT_MONEY), pSeatLocal->m_clientinfo.weaponconfig_temp.iTotalPrice), vButtonFontSize, clrPaleBlue, 0.93f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
|
||||
}
|
||||
Gfx_Text( [8 + 128*2, 8 + 2], sprintf("Weight Slots: %i / %i", pSeatLocal->m_clientinfo.weaponconfig_temp.iTotalSlots, getstati(STAT_RULE_MAXWEIGHTSLOTS)), vButtonFontSize, clrPaleBlue, 0.93f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
}else{
|
||||
vWeaponsBar_drawBase = [8, 8];
|
||||
}
|
||||
|
||||
|
||||
drawfill( vWeaponsBar_drawBase, [640, 20], clrPaleBlue, 0.96f - 0.60f );
|
||||
|
||||
vWeaponsBar_drawLoc.x = vWeaponsBar_drawBase.x + 11 + 128*0;
|
||||
vWeaponsBar_drawLoc.y = vWeaponsBar_drawBase.y + 0;
|
||||
DRAW_IMAGE_CROPPED_ADDITIVE(spr_number1, vWeaponsBar_drawLoc, clrPaleBlue, 0.89f)
|
||||
|
||||
vWeaponsBar_drawLoc.x = vWeaponsBar_drawBase.x + 11 + 128*1;
|
||||
vWeaponsBar_drawLoc.y = vWeaponsBar_drawBase.y + 0;
|
||||
DRAW_IMAGE_CROPPED_ADDITIVE(spr_number2, vWeaponsBar_drawLoc, clrPaleBlue, 0.89f)
|
||||
|
||||
vWeaponsBar_drawLoc.x = vWeaponsBar_drawBase.x + 11 + 128*2;
|
||||
vWeaponsBar_drawLoc.y = vWeaponsBar_drawBase.y + 0;
|
||||
DRAW_IMAGE_CROPPED_ADDITIVE(spr_number3, vWeaponsBar_drawLoc, clrPaleBlue, 0.89f)
|
||||
|
||||
vWeaponsBar_drawLoc.x = vWeaponsBar_drawBase.x + 11 + 128*3;
|
||||
vWeaponsBar_drawLoc.y = vWeaponsBar_drawBase.y + 0;
|
||||
DRAW_IMAGE_CROPPED_ADDITIVE(spr_number4, vWeaponsBar_drawLoc, clrPaleBlue, 0.89f)
|
||||
|
||||
vWeaponsBar_drawLoc.x = vWeaponsBar_drawBase.x + 11 + 128*4;
|
||||
vWeaponsBar_drawLoc.y = vWeaponsBar_drawBase.y + 0;
|
||||
DRAW_IMAGE_CROPPED_ADDITIVE(spr_number5, vWeaponsBar_drawLoc, clrPaleBlue, 0.89f)
|
||||
|
||||
}//drawPlayerInventory_TopBar
|
||||
|
||||
// While in the buy screen, draw each slot according to the current temp config.
|
||||
// This includes using the #1 slot for the text
|
||||
// ...We're going to take advantage of the fact that weaponconfig_weapon_t
|
||||
void
|
||||
drawPlayerInventory_buy(BOOL arg_fBuyMode)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
// Basic version (has things common to both cases). To be set soon for buy menu and
|
||||
// ingame (spawned - inventory).
|
||||
// !!! - No longer the case!
|
||||
// Don't do casting a ary_myWeapons[i] to weapondynamic_t. weaponconfic_weapon_t is
|
||||
// now a struct so they aren't so easily transferrable.
|
||||
// Get the info you need from the weapondynamic_t or weaponconfig_weapon_t by the
|
||||
// same fBuyMode check, don't trust anything else.
|
||||
weaponconfig_weapon_t* weapon_configRef;
|
||||
int currentWeaponID;
|
||||
int currentWeaponCount;
|
||||
int bitsUpgradeOpts;
|
||||
int listMax;
|
||||
BOOL hasAnyAmmo;
|
||||
|
||||
// 1 through 5. there is no slot 0.
|
||||
//
|
||||
// TODO CRITICAL - check the compile warning here. Why does this happen??
|
||||
// What row has been drawn yet for each of the player inventory slots? Use to know
|
||||
// where to draw the next weapon in that slow (below the previously drawn one...
|
||||
// next row).
|
||||
int ary_slotRowYet[INVENTORY_SLOT_COUNT];
|
||||
//ary_slotRowYet = (int) memalloc(INVENTORY_SLOT_COUNT-1);
|
||||
for(int i = 0; i < INVENTORY_SLOT_COUNT; i++){
|
||||
ary_slotRowYet[i] = 0;
|
||||
}
|
||||
//memfree(ary_slotRowYet);
|
||||
|
||||
|
||||
// Not set for the Buy Menu (data related to clip, which buyOpts are toggled on, etc.
|
||||
// is not available). The idea is, we can use "weapon_dynamicRef" to get more info
|
||||
// about the weapon for displaying too in the inventory as needed that the buy menu
|
||||
// just doesn't have. Example: ingame inv has clip left, but the buy menu does not.
|
||||
weapondynamic_t weapon_dynamicRef = NULL;
|
||||
weapondata_gun_t tempGunRef;
|
||||
|
||||
if(arg_fBuyMode){
|
||||
listMax = pSeatLocal->m_clientinfo.weaponconfig_temp.ary_myWeapons_softMax;
|
||||
}else{
|
||||
listMax = pl.ary_myWeapons_softMax;
|
||||
}
|
||||
|
||||
for(int i = 0; i < listMax; i++){
|
||||
|
||||
if(arg_fBuyMode){
|
||||
weapon_configRef = (weaponconfig_weapon_t*) &pSeatLocal->m_clientinfo.weaponconfig_temp.ary_myWeapons[i];
|
||||
currentWeaponID = weapon_configRef->weaponID;
|
||||
currentWeaponCount = weapon_configRef->iCount;
|
||||
bitsUpgradeOpts = weapon_configRef->iBitsUpgrade;
|
||||
}else{
|
||||
weapon_dynamicRef = (weapondynamic_t) pl.ary_myWeapons[i];
|
||||
currentWeaponID = weapon_dynamicRef.weaponID;
|
||||
currentWeaponCount = weapon_dynamicRef.iCount;
|
||||
bitsUpgradeOpts = weapon_dynamicRef.iBitsUpgrade;
|
||||
}
|
||||
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_weaponData[currentWeaponID];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
// If there isn't any type of count (throwables stacked, ammo), this default of
|
||||
// -1 stays to mean, don't draw any number.
|
||||
int ammoCount = -1;
|
||||
|
||||
if(basicRef.typeID == WEAPONDATA_TYPEID_GUN || basicRef.typeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
//and the type of ammo is?
|
||||
weapondata_gun_t gunRef = *((weapondata_gun_t*)basicPointer);
|
||||
|
||||
if(arg_fBuyMode){
|
||||
ammoCount = pSeatLocal->m_clientinfo.weaponconfig_temp.ary_ammoTotal[gunRef.iAmmoDataID];
|
||||
}else{
|
||||
ammoCount = pl.ary_ammoTotal[gunRef.iAmmoDataID];
|
||||
}
|
||||
|
||||
}else if(basicRef.typeID == WEAPONDATA_TYPEID_THROWABLE){
|
||||
weapondata_throwable_t throwableRef = *((weapondata_throwable_t*)basicPointer);
|
||||
//throwableRef.iMaxCount
|
||||
ammoCount = currentWeaponCount;
|
||||
}
|
||||
|
||||
// Note that this remains blank if the weapon is not selected. And for ingame (not buy menu) only.
|
||||
string sWeaponDisplayName = "";
|
||||
|
||||
if(!arg_fBuyMode && pl.weaponSelectHighlightID == i){
|
||||
if(!pl.weaponSelectHighlightAkimbo || basicRef.iAkimboID <= 0){
|
||||
// Lacking an akimbo link means we ARE the weapon to be selected.
|
||||
sWeaponDisplayName = basicRef.sDisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
if(!arg_fBuyMode){
|
||||
// only a possible consideration ingame, whether to color the weapon icon
|
||||
// red or not.
|
||||
if(basicRef.typeID == WEAPONDATA_TYPEID_GUN || basicRef.typeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
tempGunRef = *((weapondata_gun_t*)basicPointer);
|
||||
if(bitsUpgradeOpts & BITS_WEAPONOPT_AKIMBO && basicRef.iAkimboID == WEAPON_AKIMBO_UPGRADE_ID::NONE){
|
||||
// is akimbo, BUT not a separate choice (see below if that is the
|
||||
// case)? "iClipAkimboLeft" can count too.
|
||||
hasAnyAmmo = (weapon_dynamicRef.iClipLeft > 0 || weapon_dynamicRef.iClipAkimboLeft > 0 || pl.ary_ammoTotal[tempGunRef.iAmmoDataID] > 0);
|
||||
}else{
|
||||
// not akimbo or it's linked as a separate choice? singular clip
|
||||
// only.
|
||||
hasAnyAmmo = (weapon_dynamicRef.iClipLeft > 0 || pl.ary_ammoTotal[tempGunRef.iAmmoDataID] > 0);
|
||||
}
|
||||
}else{
|
||||
// not a gun? ok, assume usable.
|
||||
hasAnyAmmo = TRUE;
|
||||
}
|
||||
}else{
|
||||
// always normal color, can't be 'out of ammo'.
|
||||
hasAnyAmmo = TRUE;
|
||||
}
|
||||
|
||||
drawPlayerInventory_place(basicRef.iInventorySlot-1, ary_slotRowYet[basicRef.iInventorySlot-1], basicRef.sIconFilePath, sWeaponDisplayName, arg_fBuyMode, ammoCount, hasAnyAmmo, bitsUpgradeOpts );
|
||||
ary_slotRowYet[basicRef.iInventorySlot-1]++; //next row for the next weapon that gets that same slot.
|
||||
|
||||
// NOTICE - "basicRef.iBitsUpgrade" is whether this weapon supports akimbo or
|
||||
// not (as an upgrade at least).
|
||||
// TODO - the ingame inventory will just go ahead and make akimbo its own
|
||||
// separate dynamic weapon data to enter the list. Or will it??
|
||||
// of player weapons. Much less expensive than re-checking all weapons to see
|
||||
// what has akimbo or not every time we move up or down a weapon's choice.
|
||||
// In short, we won't be doing the automatic akimbo-version generation here
|
||||
// ingame. It will happen naturally because akimbo will be its own weapon.
|
||||
|
||||
weapondata_basic_t* akimboPointer = NULL;
|
||||
|
||||
if(bitsUpgradeOpts & BITS_WEAPONOPT_AKIMBO && basicRef.iAkimboID > 0){
|
||||
// draw the akimbo variant.basicRef.iAkimboID
|
||||
akimboPointer = (weapondata_basic_t*) ary_akimboUpgradeData[basicRef.iAkimboID];
|
||||
}
|
||||
|
||||
// We have akimbo data? show it.
|
||||
if(akimboPointer != NULL){
|
||||
weapondata_basic_t akimboRef = *(akimboPointer);
|
||||
|
||||
if(!arg_fBuyMode && pl.weaponSelectHighlightID == i && pl.weaponSelectHighlightAkimbo){
|
||||
sWeaponDisplayName = akimboRef.sDisplayName;
|
||||
}else{
|
||||
sWeaponDisplayName = "";
|
||||
}
|
||||
|
||||
if(!arg_fBuyMode){
|
||||
// only a possible consideration ingame, whether to color the weapon
|
||||
// icon red or not.
|
||||
if(
|
||||
basicRef.typeID == WEAPONDATA_TYPEID_GUN ||
|
||||
basicRef.typeID == WEAPONDATA_TYPEID_IRONSIGHT
|
||||
){
|
||||
// probably had to be a gun to get here, but eh.
|
||||
// Clearly must be akimbo by link (has a singular form that says
|
||||
// to render this separately)
|
||||
tempGunRef = *((weapondata_gun_t*)basicPointer);
|
||||
hasAnyAmmo = (weapon_dynamicRef.iClipLeft > 0 || weapon_dynamicRef.iClipAkimboLeft > 0 || pl.ary_ammoTotal[tempGunRef.iAmmoDataID] > 0);
|
||||
}else{
|
||||
// not a gun? ok, assume usable.
|
||||
hasAnyAmmo = TRUE;
|
||||
}
|
||||
}else{
|
||||
// always normal color, can't be 'out of ammo'.
|
||||
hasAnyAmmo = TRUE;
|
||||
}
|
||||
|
||||
// akimbo version always goes to slot #5.
|
||||
// whatever just leave it up to what the weapondata says. Should refer to
|
||||
// slot #5 regardless.
|
||||
drawPlayerInventory_place(akimboRef.iInventorySlot-1, ary_slotRowYet[akimboRef.iInventorySlot-1], akimboRef.sIconFilePath, sWeaponDisplayName, arg_fBuyMode, ammoCount, hasAnyAmmo, bitsUpgradeOpts );
|
||||
ary_slotRowYet[akimboRef.iInventorySlot-1]++; //ditto for the akimbo slot.
|
||||
}
|
||||
}//END OF for all ary_myWeapons (within softMax)
|
||||
}//END OF drawPlayerInveotry_buy
|
||||
|
||||
|
||||
// "hasAnyAmmo" is, whether this weapon has any ammo in the clip or the ammo pool.
|
||||
// Original TS behavior is to color red at a clip of 0, even if ammo is in the pool
|
||||
// but this seems more like an oversight.
|
||||
// For non-gun/ironsight weapons (knives, etc.) just force this to TRUE to always be
|
||||
// colored normally.
|
||||
void
|
||||
drawPlayerInventory_place
|
||||
(
|
||||
int arg_iSlot, int arg_iRow, string arg_sWeaponSpritePath,
|
||||
string arg_sSelectedWeaponDisplayName, BOOL arg_fBuyMode,
|
||||
optional int ammoCount, optional BOOL hasAnyAmmo, optional int bitsUpgradeOpts
|
||||
){
|
||||
// Where do I start drawing weapons from? Below the top-most bar that's expected
|
||||
// to be in place.
|
||||
// In buymode, there are two top-bars. Account for this by drawing an extra 20
|
||||
// pixels below.
|
||||
vector vDrawOrigin;
|
||||
if(!arg_fBuyMode){
|
||||
vDrawOrigin = [8, 8+20];
|
||||
}else{
|
||||
vDrawOrigin = [8, 8+40];
|
||||
}
|
||||
|
||||
vector vDrawPos = [vDrawOrigin.x + arg_iSlot*128, vDrawOrigin.y + arg_iRow*48 ];
|
||||
|
||||
float fOpac;
|
||||
if(arg_sSelectedWeaponDisplayName != ""){
|
||||
// If the SelectedWeaponDisplayName was provided, this is the selected weapon.
|
||||
// This text will be drawn to the right of the weapon drawn.
|
||||
// TODO - this can also just be done separately outside of all inventory item
|
||||
// weapon icon drawing.
|
||||
// Then just make arg_sSelectedWeaponDisplayName into arg_fIsSelected.
|
||||
fOpac = 1.00f;
|
||||
|
||||
Gfx_Text( [vDrawPos.x + 128, vDrawPos.y + 4], arg_sSelectedWeaponDisplayName, vButtonFontSize, clrPaleBlue, 0.95f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
}else{
|
||||
if(arg_fBuyMode){
|
||||
// always bright?
|
||||
fOpac = 0.94f;
|
||||
}else{
|
||||
fOpac = 0.82f;
|
||||
}
|
||||
}
|
||||
|
||||
if(arg_fBuyMode && ammoCount != -1){
|
||||
// draw how much ammo it has (ammo pool only, not the clip... could add it
|
||||
// first if wanted though but it's not part of the price)
|
||||
drawSpriteNumber(ary_LCD_numberSet, vDrawPos.x + 3, vDrawPos.y + 3, ammoCount, 3, BITS_DIGITOPT_DEFAULT, clrPaleBlue, 0.88f);
|
||||
}
|
||||
if(arg_fBuyMode){
|
||||
//draw some buy upgrades as three letters each.
|
||||
string upgradeString = "";
|
||||
if(bitsUpgradeOpts & BITS_WEAPONOPT_SILENCER){
|
||||
if(upgradeString == ""){
|
||||
upgradeString = sprintf("%s", "Sil");
|
||||
}else{
|
||||
upgradeString = sprintf("%s,%s", upgradeString, "Sil");
|
||||
}
|
||||
}
|
||||
if(bitsUpgradeOpts & BITS_WEAPONOPT_LASERSIGHT){
|
||||
if(upgradeString == ""){
|
||||
upgradeString = sprintf("%s", "Las");
|
||||
}else{
|
||||
upgradeString = sprintf("%s,%s", upgradeString, "Las");
|
||||
}
|
||||
}
|
||||
if(bitsUpgradeOpts & BITS_WEAPONOPT_FLASHLIGHT){
|
||||
if(upgradeString == ""){
|
||||
upgradeString = sprintf("%s", "Fla");
|
||||
}else{
|
||||
upgradeString = sprintf("%s,%s", upgradeString, "Fla");
|
||||
}
|
||||
}
|
||||
if(bitsUpgradeOpts & BITS_WEAPONOPT_SCOPE){
|
||||
if(upgradeString == ""){
|
||||
upgradeString = sprintf("%s", "Sco");
|
||||
}else{
|
||||
upgradeString = sprintf("%s,%s", upgradeString, "Sco");
|
||||
}
|
||||
}
|
||||
|
||||
if(upgradeString != ""){
|
||||
//if there is anything to draw...
|
||||
Gfx_Text( [vDrawPos.x + 3, vDrawPos.y + 3 + 16], sprintf("(%s)", upgradeString), vButtonFontSize, clrPaleBlue, 0.96f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
}
|
||||
|
||||
}//buymodecheck FOR drawing the upgrade options short-hand near the weapon.
|
||||
|
||||
img_weapon.sFilePath = sprintf("%s%s", arg_sWeaponSpritePath, "_0.tga");
|
||||
|
||||
if(hasAnyAmmo){
|
||||
DRAW_IMAGE_ADDITIVE(img_weapon, vDrawPos, clrPaleBlue, fOpac)
|
||||
}else{
|
||||
// Still selectable unlike in HL (original TS behavior), but just a warning not
|
||||
// to pick me.
|
||||
DRAW_IMAGE_ADDITIVE(img_weapon, vDrawPos, clrHUDWeaponEmpty, fOpac)
|
||||
}
|
||||
|
||||
}//drawPlayerInventory_place
|
||||
|
||||
|
||||
void
|
||||
drawPlayerCurrentWeaponStats(void)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if(pl.inventoryEquippedIndex == -1){
|
||||
return;
|
||||
}
|
||||
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) pl.getEquippedWeaponData();
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
return basicRef.vOnDrawHUD(pl, dynaRef);
|
||||
}//drawPlayerCurrentWeaponStats
|
||||
|
||||
|
||||
// See screenshots for what the bottom-left corner of the HUD ingame should look like.
|
||||
// The text on the very top is the name of the team you're in. It may often coincide
|
||||
// with player model though. TEAM_PLAY makes it obvious that it's not necessarily that
|
||||
// always though.
|
||||
|
||||
// Draw the player-specific info that always shows up when spawned.
|
||||
// Team name (if applicable), health, standing/crouch/prone icon, money/slots, etc.
|
||||
void
|
||||
drawPlayerStats(void)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
int drawerX = 8;
|
||||
int drawerY = video_res[1] - 48;
|
||||
|
||||
// TODO intermediate - fetch whether the player is on a team before doing this
|
||||
// we'll need some sort of "getstati(STAT_RULE_...)" to go through game rules to see
|
||||
// if we even support teams someplace, but maybe not here.
|
||||
// We can always let being a member of team "-1" suggest this gamemode has no teams
|
||||
// too.
|
||||
// Healthy compromise could be using a getstati to fetch "sRule_TeamNames". If it's
|
||||
// 0, this gamemode has no teams. Easy peasy.
|
||||
//if(player.iTeam != TS_Team::TEAM_NONE){
|
||||
//string myTeamName = ... //can we even fetch from an array using getstati?
|
||||
string myTeamName = "team name here";
|
||||
// like pointer vars setup in server/main.c to refer to each index? It might just work.
|
||||
drawfill( [drawerX, drawerY - 20], [127, 19], clrPaleBlue, 0.96f - 0.60f );
|
||||
Gfx_Text( [drawerX + 2, drawerY - 20 + 4], myTeamName, vButtonFontSize, clrPaleBlue, 0.96f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// TODO - heart symbol to the right, color red instead of the health is under 50
|
||||
// (or at 50 too? unconfirmed yet)
|
||||
drawfill( [drawerX, drawerY], [127, 39], clrPaleBlue, 0.96f - 0.60f );
|
||||
|
||||
vector clrHealth;
|
||||
if(pl.health >= 50){
|
||||
clrHealth = clrPaleBlue;
|
||||
}else{
|
||||
clrHealth = clrMedRed;
|
||||
}
|
||||
drawSpriteNumber(ary_LCD_numberSet, drawerX + 67 - 6, drawerY + 4 - 2, pl.health, 3, BITS_DIGITOPT_DEFAULT, clrHealth, 0.92f);
|
||||
DRAW_IMAGE_ADDITIVE(img_health, ([drawerX + 96, drawerY - 2]), clrHealth, 0.96f)
|
||||
|
||||
|
||||
// don't render if it's 0.
|
||||
if(pl.armor > 0){
|
||||
drawSpriteNumber(ary_LCD_numberSet, drawerX + 67 - 6, drawerY + 24 - 2, pl.armor, 3, BITS_DIGITOPT_DEFAULT, clrPaleBlue, 0.92f);
|
||||
DRAW_IMAGE_ADDITIVE(img_kevlar, ([drawerX + 96, drawerY + 20 - 1]), clrPaleBlue, 0.96f)
|
||||
}
|
||||
|
||||
|
||||
// Player status icon (as in standing, crouched, prone, etc.)
|
||||
// the animated running one looks to be unused. Though I would've seen it by now.
|
||||
// Regardless...
|
||||
// TODO. This needs to be synched up to the state of the player, particularly during stunts.
|
||||
// For now it's static.
|
||||
|
||||
// CRITICAL TODO. Support prone / mid-air graphics!
|
||||
if(self.flags & FL_CROUCHING){
|
||||
DRAW_IMAGE_ADDITIVE(img_player_crouch, ([drawerX, drawerY - 4]), clrPaleBlue, 0.8f)
|
||||
}else{
|
||||
DRAW_IMAGE_ADDITIVE(img_player_stand, ([drawerX, drawerY - 4]), clrPaleBlue, 0.8f)
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
drawerX = 136;
|
||||
drawerY = video_res[1] - 48;
|
||||
int offsetRight = 0;
|
||||
|
||||
if(getstati(STAT_RULE_MONEYALLOWED)){
|
||||
drawfill( [drawerX, drawerY], [127, 19], clrPaleBlue, 0.96f - 0.60f );
|
||||
offsetRight = drawSpriteNumber(ary_LCD_numberSet, drawerX + 8, drawerY + 2, getstati(STAT_MONEY), 8, BITS_DIGITOPT_NONE, clrPaleBlue, 0.92f);
|
||||
Gfx_Text( [drawerX + 8 + offsetRight + 6, drawerY + 4], "Credits", vButtonFontSize, clrPaleBlue, 0.96f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
}//money allowed check
|
||||
|
||||
drawfill( [drawerX, drawerY + 20], [127, 19], clrPaleBlue, 0.96f - 0.60f );
|
||||
offsetRight = drawSpriteNumber(ary_LCD_numberSet, drawerX + 8, drawerY + 20 + 2, getstati(STAT_RULE_MAXWEIGHTSLOTS) - pl.iTotalSlots, 8, BITS_DIGITOPT_NONE, clrPaleBlue, 0.92f);
|
||||
Gfx_Text( [drawerX + 8 + offsetRight + 6, drawerY + 20 + 4], "free slots", vButtonFontSize, clrPaleBlue, 0.96f, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
|
||||
}//drawPlayerStats
|
||||
|
||||
|
||||
void
|
||||
drawTimer(void)
|
||||
{
|
||||
int iMinutes;
|
||||
int iSeconds;
|
||||
|
||||
//video_mins ?
|
||||
int drawerX = video_res[0] - (60 + 22);
|
||||
int drawerY = 14 - 2;
|
||||
|
||||
if (getstatf(STAT_GAMETIME) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
float gameTime = getstatf(STAT_GAMETIME);
|
||||
|
||||
iMinutes = gameTime / 60;
|
||||
iSeconds = gameTime - 60 * iMinutes;
|
||||
|
||||
vector clrDraw;
|
||||
float fOpac;
|
||||
float numberFontWidth = ary_LCD_numberSet[0].vSize.x;
|
||||
|
||||
if(gameTime < 30){
|
||||
clrDraw = clrMedRed;
|
||||
fOpac = 0.98f;
|
||||
}else{
|
||||
clrDraw = clrPaleBlue;
|
||||
fOpac = 0.92f;
|
||||
}
|
||||
|
||||
drawSpriteNumber(ary_LCD_numberSet, drawerX, drawerY, iMinutes, 3, BITS_DIGITOPT_DEFAULT, clrDraw, fOpac);
|
||||
Gfx_Text( [drawerX + numberFontWidth*3 + 4, drawerY + 1], ":", vFontSizeNumSlash, clrDraw, fOpac, DRAWFLAG_ADDITIVE, FONT_ARIAL_NUMSLASH );
|
||||
drawSpriteNumber(ary_LCD_numberSet, drawerX + numberFontWidth*4, drawerY, iSeconds, 2, BITS_DIGITOPT_FILLER0, clrDraw, fOpac);
|
||||
|
||||
}//drawTimer
|
||||
|
||||
|
48
src/client/obituary.h
Normal file
48
src/client/obituary.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define OBITUARY_LINES 4
|
||||
#define OBITUARY_TIME 5
|
||||
|
||||
/* imagery */
|
||||
typedef struct {
|
||||
string name; /* name of the weapon/type, e.g. d_crowbar */
|
||||
string sprite; /* name of the spritesheet it's from */
|
||||
float size[2]; /* on-screen size in pixels */
|
||||
float src_pos[2]; /* normalized position in the sprite sheet */
|
||||
float src_size[2]; /* normalized size in the sprite sheet */
|
||||
string src_sprite; /* precaching reasons */
|
||||
} obituaryimg_t;
|
||||
|
||||
obituaryimg_t *g_obtypes;
|
||||
int g_obtype_count;
|
||||
|
||||
/* actual obituary storage */
|
||||
typedef struct
|
||||
{
|
||||
string attacker;
|
||||
string victim;
|
||||
int icon;
|
||||
} obituary_t;
|
||||
|
||||
obituary_t g_obituary[OBITUARY_LINES];
|
||||
int g_obituary_count;
|
||||
float g_obituary_time;
|
||||
|
||||
void Obituary_Init(void);
|
||||
void Obituary_Precache(void);
|
||||
void Obituary_Draw(void);
|
||||
void Obituary_Parse(void);
|
221
src/client/obituary.qc
Normal file
221
src/client/obituary.qc
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void
|
||||
Obituary_Init(void)
|
||||
{
|
||||
int c;
|
||||
int i;
|
||||
filestream fh;
|
||||
string line;
|
||||
vector tmp;
|
||||
|
||||
if (g_obtype_count > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_obtype_count = 0;
|
||||
i = 0;
|
||||
|
||||
fh = fopen("sprites/hud.txt", FILE_READ);
|
||||
if (fh < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* count valid entries */
|
||||
while ((line = fgets(fh))) {
|
||||
if (substring(line, 0, 2) == "d_") {
|
||||
c = tokenize(line);
|
||||
if (c == 7 && argv(1) == "640") {
|
||||
g_obtype_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_obtypes = memalloc(sizeof(obituaryimg_t) * g_obtype_count);
|
||||
|
||||
fseek(fh, 0);
|
||||
|
||||
/* read them in */
|
||||
while ((line = fgets(fh))) {
|
||||
if (substring(line, 0, 2) == "d_") {
|
||||
c = tokenize(line);
|
||||
|
||||
/* we only care about the high-res (640) variants. the 320
|
||||
* HUD is useless to us. Just use the builtin scaler */
|
||||
if (c == 7 && argv(1) == "640") {
|
||||
g_obtypes[i].name = substring(argv(0), 2, -1);
|
||||
g_obtypes[i].src_sprite = sprintf("sprites/%s.spr", argv(2));
|
||||
precache_model(g_obtypes[i].src_sprite);
|
||||
g_obtypes[i].sprite = spriteframe(sprintf("sprites/%s.spr", argv(2)), 0, 0.0f);
|
||||
g_obtypes[i].size[0] = stof(argv(5));
|
||||
g_obtypes[i].size[1] = stof(argv(6));
|
||||
tmp = drawgetimagesize(g_obtypes[i].sprite);
|
||||
g_obtypes[i].src_pos[0] = stof(argv(3)) / tmp[0];
|
||||
g_obtypes[i].src_pos[1] = stof(argv(4)) / tmp[1];
|
||||
g_obtypes[i].src_size[0] = g_obtypes[i].size[0] / tmp[0];
|
||||
g_obtypes[i].src_size[1] = g_obtypes[i].size[1] / tmp[1];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
}
|
||||
|
||||
void
|
||||
Obituary_Precache(void)
|
||||
{
|
||||
for (int i = 0; i < g_obtype_count; i++)
|
||||
precache_model(g_obtypes[i].src_sprite);
|
||||
}
|
||||
|
||||
void
|
||||
Obituary_KillIcon(int id, float w)
|
||||
{
|
||||
if (w > 0)
|
||||
for (int i = 0; i < g_obtype_count; i++) {
|
||||
if (g_weapons[w].name == g_obtypes[i].name) {
|
||||
g_obituary[id].icon = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* look for skull instead */
|
||||
for (int i = 0; i < g_obtype_count; i++) {
|
||||
if (g_obtypes[i].name == "skull") {
|
||||
g_obituary[id].icon = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Obituary_Add(string attacker, string victim, float weapon, float flags)
|
||||
{
|
||||
int i;
|
||||
int x, y;
|
||||
x = OBITUARY_LINES;
|
||||
|
||||
/* we're not full yet, so fill up the buffer */
|
||||
if (g_obituary_count < x) {
|
||||
y = g_obituary_count;
|
||||
g_obituary[y].attacker = attacker;
|
||||
g_obituary[y].victim = victim;
|
||||
Obituary_KillIcon(y, weapon);
|
||||
g_obituary_count++;
|
||||
} else {
|
||||
for (i = 0; i < (x-1); i++) {
|
||||
g_obituary[i].attacker = g_obituary[i+1].attacker;
|
||||
g_obituary[i].victim = g_obituary[i+1].victim;
|
||||
g_obituary[i].icon = g_obituary[i+1].icon;
|
||||
}
|
||||
/* after rearranging, add the newest to the bottom. */
|
||||
g_obituary[x-1].attacker = attacker;
|
||||
g_obituary[x-1].victim = victim;
|
||||
Obituary_KillIcon(x-1, weapon);
|
||||
}
|
||||
|
||||
g_obituary_time = OBITUARY_TIME;
|
||||
|
||||
if (g_weapons[weapon].deathmsg) {
|
||||
string conprint = g_weapons[weapon].deathmsg();
|
||||
|
||||
if (conprint != "") {
|
||||
print(sprintf(conprint, attacker, victim));
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Obituary_Draw(void)
|
||||
{
|
||||
int i;
|
||||
vector pos;
|
||||
vector item;
|
||||
drawfont = FONT_CON;
|
||||
pos = g_hudmins + [g_hudres[0] - 18, 56];
|
||||
|
||||
if (g_obituary_time <= 0 && g_obituary_count > 0) {
|
||||
for (i = 0; i < (OBITUARY_LINES-1); i++) {
|
||||
g_obituary[i].attacker = g_obituary[i+1].attacker;
|
||||
g_obituary[i].victim = g_obituary[i+1].victim;
|
||||
g_obituary[i].icon = g_obituary[i+1].icon;
|
||||
}
|
||||
g_obituary[OBITUARY_LINES-1].attacker = "";
|
||||
|
||||
g_obituary_time = OBITUARY_TIME;
|
||||
g_obituary_count--;
|
||||
}
|
||||
|
||||
if (g_obituary_count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
item = pos;
|
||||
for (i = 0; i < OBITUARY_LINES; i++) {
|
||||
string a, v;
|
||||
|
||||
if (!g_obituary[i].attacker) {
|
||||
break;
|
||||
}
|
||||
|
||||
item[0] = pos[0];
|
||||
|
||||
v = g_obituary[i].victim;
|
||||
drawstring_r(item + [0,2], v, [12,12], [1,1,1], 1.0f, 0);
|
||||
item[0] -= stringwidth(v, TRUE, [12,12]) + 4;
|
||||
item[0] -= g_obtypes[g_obituary[i].icon].size[0];
|
||||
|
||||
drawsubpic(
|
||||
item,
|
||||
[g_obtypes[g_obituary[i].icon].size[0], g_obtypes[g_obituary[i].icon].size[1]],
|
||||
g_obtypes[g_obituary[i].icon].sprite,
|
||||
[g_obtypes[g_obituary[i].icon].src_pos[0],g_obtypes[g_obituary[i].icon].src_pos[1]],
|
||||
[g_obtypes[g_obituary[i].icon].src_size[0],g_obtypes[g_obituary[i].icon].src_size[1]],
|
||||
g_hud_color,
|
||||
1.0f,
|
||||
DRAWFLAG_ADDITIVE
|
||||
);
|
||||
|
||||
a = g_obituary[i].attacker;
|
||||
drawstring_r(item + [-4,2], a, [12,12], [1,1,1], 1.0f, 0);
|
||||
item[1] += 18;
|
||||
}
|
||||
|
||||
g_obituary_time = max(0, g_obituary_time - clframetime);
|
||||
}
|
||||
|
||||
void
|
||||
Obituary_Parse(void)
|
||||
{
|
||||
string attacker;
|
||||
string victim;
|
||||
float weapon;
|
||||
float flags;
|
||||
|
||||
attacker = readstring();
|
||||
victim = readstring();
|
||||
weapon = readbyte();
|
||||
flags = readbyte();
|
||||
|
||||
if (!attacker) {
|
||||
return;
|
||||
}
|
||||
|
||||
Obituary_Add(attacker, victim, weapon, flags);
|
||||
}
|
18
src/client/particles.h
Normal file
18
src/client/particles.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// no, I think not.
|
||||
//var float BEAM_TRIPMINE;
|
579
src/client/player.qc
Normal file
579
src/client/player.qc
Normal file
|
@ -0,0 +1,579 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
string g_pbones[] =
|
||||
{
|
||||
"Bip01",
|
||||
"Bip01 Footsteps",
|
||||
"Bip01 Pelvis",
|
||||
"Bip01 L Leg",
|
||||
"Bip01 L Leg1",
|
||||
"Bip01 L Foot",
|
||||
"Bip01 L Toe0",
|
||||
"Bip01 L Toe01",
|
||||
"Bip01 L Toe02",
|
||||
"Dummy16",
|
||||
"Bip01 R Leg",
|
||||
"Bip01 R Leg1",
|
||||
"Bip01 R Foot",
|
||||
"Bip01 R Toe0",
|
||||
"Bip01 R Toe01",
|
||||
"Bip01 R Toe02",
|
||||
"Dummy11",
|
||||
"Bip01 Spine",
|
||||
"Bip01 Spine1",
|
||||
"Bip01 Spine2",
|
||||
"Bip01 Spine3",
|
||||
"Bip01 Neck",
|
||||
"Bip01 Head",
|
||||
"Dummy21",
|
||||
"Dummy08",
|
||||
"Bone02",
|
||||
"Bone03",
|
||||
"Bone04",
|
||||
"Dummy05",
|
||||
"Bone09",
|
||||
"Bone10",
|
||||
"Dummy04",
|
||||
"Bone05",
|
||||
"Bone06",
|
||||
"Dummy03",
|
||||
"Bone07",
|
||||
"Bone08",
|
||||
"Dummy09",
|
||||
"Bone11",
|
||||
"Bone12",
|
||||
"Dummy10",
|
||||
"Bone13",
|
||||
"Bone14",
|
||||
"Bone15",
|
||||
"Bip01 L Arm",
|
||||
"Bip01 L Arm1",
|
||||
"Bip01 L Arm2",
|
||||
"Bip01 L Hand",
|
||||
"Bip01 L Finger0",
|
||||
"Bip01 L Finger01",
|
||||
"Bip01 L Finger02",
|
||||
"Dummy06",
|
||||
"Bip01 L Finger1",
|
||||
"Bip01 L Finger11",
|
||||
"Bip01 L Finger12",
|
||||
"Dummy07",
|
||||
"Bip01 R Arm",
|
||||
"Bip01 R Arm1",
|
||||
"Bip01 R Arm2",
|
||||
"Bip01 R Hand",
|
||||
"Bip01 R Finger0",
|
||||
"Bip01 R Finger01",
|
||||
"Bip01 R Finger02",
|
||||
"Dummy01",
|
||||
"Bip01 R Finger1",
|
||||
"Bip01 R Finger11",
|
||||
"Bip01 R Finger12",
|
||||
"Dummy02",
|
||||
"Box02",
|
||||
"Bone08",
|
||||
"Bone15"
|
||||
};
|
||||
|
||||
void
|
||||
Player_HandleWeaponModel(base_player pp, float thirdperson)
|
||||
{
|
||||
player pl = (player)pp;
|
||||
|
||||
/* if we don't exist, create us */
|
||||
if (!pl.p_model) {
|
||||
pl.p_model = spawn();
|
||||
}
|
||||
|
||||
/* only make it visible when it's a thirdperson drawcall */
|
||||
pl.p_model.drawmask = (thirdperson) ? MASK_ENGINE:0;
|
||||
|
||||
/* let's not waste any time doing bone calculations then */
|
||||
if (pl.p_model.drawmask == 0)
|
||||
return;
|
||||
|
||||
/* what's the current weapon model supposed to be anyway? */
|
||||
string wmodel = Weapons_GetPlayermodel(pl.activeweapon);
|
||||
|
||||
/* we changed weapons, update skeletonindex */
|
||||
if (pl.p_model.model != wmodel) {
|
||||
/* free memory */
|
||||
if (pl.p_model.skeletonindex)
|
||||
skel_delete(pl.p_model.skeletonindex);
|
||||
|
||||
/* set the new model and mark us updated */
|
||||
setmodel(pl.p_model, wmodel);
|
||||
pl.p_model.model = wmodel;
|
||||
|
||||
/* set the new skeletonindex */
|
||||
pl.p_model.skeletonindex = skel_create(pl.p_model.modelindex);
|
||||
|
||||
/* hack this thing in here FIXME: this should be done when popping in/out of a pvs */
|
||||
if (autocvar(cl_himodels, 1, "Use high-quality player models over lower-definition ones"))
|
||||
setcustomskin(self, "", "geomset 0 2\n");
|
||||
else
|
||||
setcustomskin(self, "", "geomset 0 1\n");
|
||||
}
|
||||
|
||||
/* follow player at all times */
|
||||
setorigin(pl.p_model, pl.origin);
|
||||
pl.p_model.angles = pl.angles;
|
||||
skel_build(pl.p_model.skeletonindex, pl.p_model, pl.p_model.modelindex,0, 0, -1);
|
||||
|
||||
/* we have to loop through all valid bones of the weapon model and match them
|
||||
* to the player one */
|
||||
for (float i = 0; i < g_pbones.length; i++) {
|
||||
vector bpos;
|
||||
float pbone = gettagindex(pl, g_pbones[i]);
|
||||
float wbone = gettagindex(pl.p_model, g_pbones[i]);
|
||||
|
||||
/* if the bone doesn't ignore in either skeletal mesh, ignore */
|
||||
if (wbone <= 0 || pbone <= 0)
|
||||
continue;
|
||||
|
||||
bpos = gettaginfo(pl, pbone);
|
||||
|
||||
/* the most expensive bit */
|
||||
skel_set_bone_world(pl.p_model, wbone, bpos, v_forward, v_right, v_up);
|
||||
}
|
||||
}
|
||||
|
||||
/* we need to call this when a player entity gets removed */
|
||||
void
|
||||
Player_DestroyWeaponModel(entity pp)
|
||||
{
|
||||
player pl = (player)pp;
|
||||
if (pl.p_model)
|
||||
remove(pl.p_model);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//TAGGG - NOTE! In case it ever matters, parameter "base_player pl" renamed to "base_player pp".
|
||||
// Doubt it ever should, no idea if FTE would complain about a discrepency between prototype and
|
||||
// implementation parameter names if that ever happened.
|
||||
void
|
||||
Player_PreDraw(base_player pp, int thirdperson)
|
||||
{
|
||||
//int thirdperson = (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum);
|
||||
//base_player pp = (base_player)this;
|
||||
|
||||
BOOL canRenderFlashlight = FALSE;
|
||||
BOOL canRenderLaserSight = FALSE;
|
||||
|
||||
player pl = (player)pp;
|
||||
//we're going to use the buyopts of our current weapon + the one actually turned on, yah?
|
||||
|
||||
// DEBUG: printouts about the other player.
|
||||
// Start a server with over 1 max players allowed in one window,
|
||||
// connect to it in another window. Boom, read printouts.
|
||||
/*
|
||||
if(entnum != player_localentnum){
|
||||
// so other player's "pl.weaponEquippedID" are not sent over to our clientside copies of them... I guess?
|
||||
// At least that's not confusing.
|
||||
printfline("It is I, the other player! What do I have? %i weapon count: %i", pl.weaponEquippedID, pl.ary_myWeapons_softMax);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
if(pl.inventoryEquippedIndex != -1){
|
||||
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
|
||||
if(dynaRef.weaponTypeID == WEAPONDATA_TYPEID_GUN || dynaRef.weaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
weapondata_basic_t* basePRef = pl.getEquippedWeaponData();
|
||||
weapondata_basic_t baseRef = *basePRef;
|
||||
|
||||
|
||||
// We must have the flashlight bit on, AND support it on our weapon, AND it be a possibility according to weapon data.
|
||||
int legalBuyOpts_on = (dynaRef.iBitsUpgrade_on & (dynaRef.iBitsUpgrade & baseRef.iBitsUpgrade));
|
||||
|
||||
if(legalBuyOpts_on & BITS_WEAPONOPT_FLASHLIGHT){
|
||||
canRenderFlashlight = TRUE;
|
||||
}
|
||||
if(legalBuyOpts_on & BITS_WEAPONOPT_LASERSIGHT){
|
||||
canRenderLaserSight = TRUE;
|
||||
}
|
||||
}///END OF _GUN or _IRONSIGHT type checks
|
||||
}//END OF weaponEquippedID check
|
||||
|
||||
|
||||
|
||||
vector posView;
|
||||
vector angView;
|
||||
|
||||
//for glock it is 40.
|
||||
//vector gunpos = gettaginfo(pSeat->eViewModel, 33);
|
||||
|
||||
float daDrawAlphahz = 1.0;
|
||||
const vector lasColor = [1.0, 0, 0];
|
||||
const vector fsize = [2,2];
|
||||
const vector fsizeDot = [18,18];
|
||||
const vector fsizeFlashlightMuzzleGlow = [3, 3];
|
||||
|
||||
vector flashPos;
|
||||
vector gunpos;
|
||||
vector gunpos2 = [0,0,0];
|
||||
vector gunpos_tempEnd;
|
||||
|
||||
|
||||
vector dirGun = [0,0,0];
|
||||
vector dirGun2 = [0,0,0];
|
||||
vector angGun = [0,0,0];
|
||||
vector angGun2 = [0,0,0];
|
||||
|
||||
//TAGGG - IMPORTANT NOTE!!!
|
||||
// BEWARE "view_angles", it is a client global (what a confusing term), meaning it pertains only to THIS
|
||||
// client (local player), no matter what player is being rendered by this call.
|
||||
// wait... shouldn't we do the third-person check for the flash-light check above too?
|
||||
|
||||
BOOL canDrawAkimboLaser = FALSE;
|
||||
pl.recentLaserHitPosSet = TRUE;
|
||||
|
||||
|
||||
|
||||
// TAGGG - QUESTION: Is it better to use the bool "thirdperson"
|
||||
// (see check below involving cl_thirdperson)
|
||||
// OR a raw "pl.entnum != player_localentnum" check?
|
||||
// The former is a more concrete check for, "Am I the local player
|
||||
// looking in 1st person, yes or no?".
|
||||
// The latter will still treat the player being the local player the
|
||||
// same as firstperson, even if the local player is forcing themselves
|
||||
// to be viewed in third person.
|
||||
// I am inclined to think the former is the better choice, but
|
||||
// valve/src/client/flashlight.qc uses the latter way. Why?
|
||||
|
||||
// thirdperson
|
||||
// True IF (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum)
|
||||
// False IF (autocvar_cl_thirdperson == FALSE && this.entnum == player_localentnum)
|
||||
if(!thirdperson){
|
||||
|
||||
//TAGGG - Old way!
|
||||
//posView = getproperty(VF_ORIGIN) + [0,0,-8];
|
||||
//angView = getproperty(VF_CL_VIEWANGLES);
|
||||
|
||||
posView = pSeat->m_vecPredictedOrigin + [0,0,-8];
|
||||
angView = view_angles;
|
||||
|
||||
// CHECK: is "getproperty(VF_CL_VIEWANGLES)" always the same as "viewangles"?
|
||||
|
||||
if(!pl.weaponEquippedAkimbo){
|
||||
// first-person and this is the local player?
|
||||
// We can get more specific info from the visible viewmodel, do so!
|
||||
gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i);
|
||||
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i);
|
||||
// should shell casings come from a bit behind the firing point here when needed?
|
||||
//gunpos += v_right * 0.8; //why is this 'up'??
|
||||
// not this one maybe... what the hell is this direction at all.
|
||||
//gunpos += v_forward * -1.8;
|
||||
|
||||
dirGun = normalize(gunpos_tempEnd - gunpos);
|
||||
angGun = vectoangles(dirGun);
|
||||
|
||||
|
||||
}else{
|
||||
canDrawAkimboLaser = TRUE;
|
||||
|
||||
gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i);
|
||||
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 1i);
|
||||
dirGun = normalize(gunpos_tempEnd - gunpos);
|
||||
angGun = vectoangles(dirGun);
|
||||
|
||||
gunpos2 = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 2i);
|
||||
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i);
|
||||
dirGun2 = normalize(gunpos_tempEnd - gunpos2);
|
||||
angGun2 = vectoangles(dirGun2);
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
posView = pl.origin + pl.view_ofs;
|
||||
angView = [pl.pitch, pl.angles[1], pl.angles[2]];
|
||||
gunpos = posView;
|
||||
angGun = angView;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(canRenderLaserSight && pl.entnum == player_localentnum){
|
||||
// The lasersight displays a number of distance to show how long the laser goes.
|
||||
//makevectors(view_angles);
|
||||
makevectors(angView);
|
||||
traceline(posView, posView + v_forward * 2048*4, MOVE_HITMODEL, pl);
|
||||
pl.recentLaserDistanceDisplay = (int)(vlen(trace_endpos - posView) / 40 );
|
||||
}
|
||||
|
||||
|
||||
// DEBUG!!!
|
||||
//canRenderLaserSight = TRUE;
|
||||
//canRenderFlashlight = TRUE;
|
||||
|
||||
if(canRenderFlashlight){
|
||||
//TAGGG - FLASHLIGHT STUFF HERE..
|
||||
// oh wait a comment above already said that
|
||||
// HOWEVER... in TS flashlights have a range limit. Up to so far they have max brightness,
|
||||
// then it lowers with a bit of range, then it's nothing.
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
int flashlightRangeMax = 1070;
|
||||
float flashlightBrightnessFactor = 1.0;
|
||||
float rangeDimPriorStart = 170; //rangeMax minus this is where I start dimming.
|
||||
|
||||
makevectors(angView);
|
||||
|
||||
traceline(posView, posView + (v_forward * flashlightRangeMax), MOVE_NORMAL, pl);
|
||||
int traceDist = trace_fraction * flashlightRangeMax;
|
||||
|
||||
//printfline("%.2f %d",, trace_fraction, trace_inopen);
|
||||
|
||||
//TODO - not here but elsewhere, draw the muzzle flashlight effect, some sprite on that gun attachment where am uzzleflash would go should do it.
|
||||
// ALSO, go ahead and use this line trace to tell the lasersight how far the laser went.
|
||||
// And just draw the lasersight (and dot if necessary), IF this render is for the local player.
|
||||
// If not the local player, a slightly larger red dot (actual sprite) goes at the point the
|
||||
// player is looking, likely not influenced by animations / view-model stuff.
|
||||
|
||||
|
||||
if(trace_fraction == 1.0){
|
||||
//uh-oh.
|
||||
flashlightBrightnessFactor = 0;
|
||||
}if(traceDist >= flashlightRangeMax - rangeDimPriorStart){
|
||||
//the flashlight gets dimmer the further it is at this point.
|
||||
// rangeDimPriorStart from the end: max bright still.
|
||||
// very end: 0% bright.
|
||||
flashlightBrightnessFactor = (-flashlightRangeMax * (trace_fraction + -1)) / rangeDimPriorStart;
|
||||
}
|
||||
|
||||
if(flashlightBrightnessFactor > 0){
|
||||
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
|
||||
dynamiclight_add(trace_endpos + (v_forward * -2), 128 * flashlightBrightnessFactor, [1,1,1]);
|
||||
} else {
|
||||
float p = dynamiclight_add(posView, 512 * flashlightBrightnessFactor, [1,1,1], 0, "textures/flashlight");
|
||||
dynamiclight_set(p, LFIELD_ANGLES, angView);
|
||||
dynamiclight_set(p, LFIELD_FLAGS, 3);
|
||||
}
|
||||
}//END OF brightness check
|
||||
|
||||
|
||||
}//END OF "flashlight is on" criteria
|
||||
|
||||
|
||||
if(canRenderLaserSight || canRenderFlashlight){
|
||||
// TRY IT SOMEHOW? RF_DEPTHHACK
|
||||
//pSeat->m_eViewModel.renderflags = RF_DEPTHHACK;
|
||||
//if (alpha <= 0.0f) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
makevectors(angGun);
|
||||
|
||||
//rotatevectorsbyangle( [-0.42, 0.75, 0] );
|
||||
//rotatevectorsbyangle( [-0.52, 0.85, 0] );
|
||||
rotatevectorsbyangle( [-0.45, 0.27, 0] );
|
||||
|
||||
|
||||
flashPos = gunpos + v_up * -0.08 + v_right * 0.06;
|
||||
|
||||
|
||||
vector shortForwardEnd = gunpos;
|
||||
shortForwardEnd += v_forward * -1;
|
||||
shortForwardEnd += v_up * (0.22); //why is this 'up'??
|
||||
shortForwardEnd += v_right * (0.35); //why is this 'up'??
|
||||
|
||||
//makevectors(m_vecAngle); really now
|
||||
//makevectors(input_angles); //maybe this if we need to do this.
|
||||
|
||||
// v_up * 2. or.. 1.6, for size [3,3] at least.
|
||||
// for size [5,5], we need v_up*3, v_right*2. I DONT KNOW.
|
||||
// for size [2, 2], we want v_up*5, v_right*5. Go figure that one out.
|
||||
|
||||
// Keep in mind, the global vectors (v_up, etc.) are set to the orientation of the recently received
|
||||
// viewmodel attachment (gettaginfo above). Don't 'makevectors' at all to simply rely on that.
|
||||
// ...unfortunately the orientation we get back is not great either. oh well.
|
||||
|
||||
// NEW TEST. Can we even get a straight line from the player's center to the gunpos?
|
||||
|
||||
traceline(posView, shortForwardEnd, FALSE, pl);
|
||||
if(trace_fraction >= 1.0){
|
||||
//woohoo!
|
||||
|
||||
traceline(shortForwardEnd, shortForwardEnd + v_forward * 1024, MOVE_HITMODEL, pl);
|
||||
|
||||
// other places care about this.
|
||||
if (pl.entnum == player_localentnum) {
|
||||
pl.recentLaserHitPos = trace_endpos;
|
||||
}
|
||||
|
||||
if(canRenderLaserSight){
|
||||
|
||||
// In original TS, the 'laster' does not render for the local player if they are
|
||||
// in thirdperson to see their own playermodel. I... don't really understand that,
|
||||
// feels like a mistake. The lasers from other players are visible.
|
||||
|
||||
// TAGGG - TODO - SUPER LOW PRIORITY
|
||||
// Lasers only for not in 3rd person and local player.
|
||||
// Could work for the third-person model or other players if there were a way to determine
|
||||
// the muzzle-end point for player models. Unsure if that is possible.
|
||||
// To see it in third-person anyway, just change the "!thirdperson" condition below
|
||||
// to "TRUE"; always do it. There is no separate place that does only first-person lasers
|
||||
// to worry about being redundant with.
|
||||
// Note the laser will try to face the direction the player model is, which may not
|
||||
// necessarily be where the player is looking, although that same issue would come up
|
||||
// with firing anyway; you would look like you're firing sideways anyway, this would be
|
||||
// just as "off".
|
||||
if (!thirdperson) {
|
||||
//makevectors(view_angles); //??? it seems we do not need this perhaps...
|
||||
// IN SHORT, we're riding the prior "makevectors(angGun);".
|
||||
R_BeginPolygon("sprites/laserbeam.spr_0.tga", 1, 0);
|
||||
R_PolygonVertex(gunpos + v_right * fsize[0] - v_up * fsize[1], [1,1], lasColor, daDrawAlphahz);
|
||||
R_PolygonVertex(gunpos - v_right * fsize[0] - v_up * fsize[1], [0,1], lasColor, daDrawAlphahz);
|
||||
R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], [0,0], lasColor, daDrawAlphahz);
|
||||
R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], [1,0], lasColor, daDrawAlphahz);
|
||||
R_EndPolygon();
|
||||
}
|
||||
|
||||
// Draw the laser sprite effect at where the laser is hitting, but ONLY for every other player
|
||||
// except this one. That's because, for the local player, we already are drawing the laserdot
|
||||
// projected onto the screen in the HUD logic.
|
||||
|
||||
if (pl.entnum != player_localentnum) {
|
||||
makevectors(angView);
|
||||
|
||||
trace_endpos += trace_plane_normal * fsizeDot[0]/6;
|
||||
R_BeginPolygon("sprites/laserdot.spr_0.tga", 1, 0);
|
||||
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] - v_up * fsizeDot[1], [1,1], lasColor, 0.80f);
|
||||
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] - v_up * fsizeDot[1], [0,1], lasColor, 0.80f);
|
||||
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] + v_up * fsizeDot[1], [0,0], lasColor, 0.80f);
|
||||
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] + v_up * fsizeDot[1], [1,0], lasColor, 0.80f);
|
||||
R_EndPolygon();
|
||||
}
|
||||
|
||||
}//END OF canRenderLaserSight
|
||||
|
||||
|
||||
// glow effect on top of the gun muzzle while the flashlight is on?
|
||||
if(canRenderFlashlight){
|
||||
if(!thirdperson){
|
||||
makevectors(angView);
|
||||
|
||||
//trace_endpos += trace_plane_normal * fsizeDot[0]/6;
|
||||
R_BeginPolygon("sprites/new/glow02.spr_0.tga", 1, 0);
|
||||
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [1,1], [1,1,1], 0.45f);
|
||||
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [0,1], [1,1,1], 0.45f);
|
||||
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [0,0], [1,1,1], 0.45f);
|
||||
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [1,0], [1,1,1], 0.45f);
|
||||
R_EndPolygon();
|
||||
}
|
||||
}
|
||||
}//END OF trace pre-check
|
||||
|
||||
|
||||
// This requires the current weapon to be akimbo, player is in first person,
|
||||
// and the local player is being rendered.
|
||||
if(canDrawAkimboLaser){
|
||||
// test for the 2nd gun's laser too then.
|
||||
// makevectors(theDir);
|
||||
makevectors(angGun2);
|
||||
rotatevectorsbyangle( [-0.45, 0.27, 0] );
|
||||
|
||||
//gunpos2 += v_forward * -18;
|
||||
|
||||
flashPos = gunpos2 + v_up * -0.08 + v_right * 0.06;
|
||||
|
||||
shortForwardEnd = gunpos2;
|
||||
shortForwardEnd += v_forward * -1;
|
||||
shortForwardEnd += v_up * (0.22); //why is this 'up'??
|
||||
shortForwardEnd += -v_right * (0.35); //why is this 'up'??
|
||||
|
||||
|
||||
traceline(posView, shortForwardEnd, FALSE, pl);
|
||||
if(trace_fraction >= 1.0){
|
||||
|
||||
traceline(shortForwardEnd, shortForwardEnd + v_forward * 1024, MOVE_HITMODEL, pl);
|
||||
|
||||
// other places care about this.
|
||||
if (pl.entnum == player_localentnum) {
|
||||
pl.recentLaserHitPos2 = trace_endpos;
|
||||
}
|
||||
|
||||
if(canRenderLaserSight){
|
||||
if (!thirdperson) {
|
||||
// ONLY render the polygon
|
||||
// makevectors(view_angles); //??? it seems we do not need this perhaps...
|
||||
// IN SHORT, we're riding the prior "makevectors(angGun2);".
|
||||
|
||||
R_BeginPolygon("sprites/laserbeam.spr_0.tga", 1, 0);
|
||||
R_PolygonVertex(gunpos2 + v_right * fsize[0] - v_up * fsize[1], [1,1], lasColor, daDrawAlphahz);
|
||||
R_PolygonVertex(gunpos2 - v_right * fsize[0] - v_up * fsize[1], [0,1], lasColor, daDrawAlphahz);
|
||||
R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], [0,0], lasColor, daDrawAlphahz);
|
||||
R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], [1,0], lasColor, daDrawAlphahz);
|
||||
R_EndPolygon();
|
||||
}
|
||||
|
||||
|
||||
if (pl.entnum != player_localentnum) {
|
||||
//makevectors(view_angles);
|
||||
makevectors(angView);
|
||||
|
||||
trace_endpos += trace_plane_normal * fsizeDot[0]/6;
|
||||
R_BeginPolygon("sprites/laserdot.spr_0.tga", 1, 0);
|
||||
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] - v_up * fsizeDot[1], [1,1], lasColor, 0.80f);
|
||||
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] - v_up * fsizeDot[1], [0,1], lasColor, 0.80f);
|
||||
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] + v_up * fsizeDot[1], [0,0], lasColor, 0.80f);
|
||||
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] + v_up * fsizeDot[1], [1,0], lasColor, 0.80f);
|
||||
R_EndPolygon();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(canRenderFlashlight){
|
||||
if(!thirdperson){
|
||||
//makevectors(view_angles);
|
||||
makevectors(angView);
|
||||
|
||||
//trace_endpos += trace_plane_normal * fsizeDot[0]/6;
|
||||
R_BeginPolygon("sprites/new/glow02.spr_0.tga", 1, 0);
|
||||
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [1,1], [1,1,1], 0.45f);
|
||||
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [0,1], [1,1,1], 0.45f);
|
||||
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [0,0], [1,1,1], 0.45f);
|
||||
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [1,0], [1,1,1], 0.45f);
|
||||
R_EndPolygon();
|
||||
}
|
||||
}
|
||||
|
||||
}//END OF trace pre-check
|
||||
}//END OF akimbo check
|
||||
|
||||
/*
|
||||
if (m_iBeams == 0) {
|
||||
alpha -= clframetime * 3;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
}else{
|
||||
pl.recentLaserHitPosSet = FALSE;
|
||||
}//END OF canRenderLaserSight || canRenderFlashlight
|
||||
|
||||
|
||||
pl.Physics_SetViewParms();
|
||||
Animation_PlayerUpdate((player)pl);
|
||||
Animation_TimerUpdate((player)pl, clframetime);
|
||||
Player_HandleWeaponModel(pl, thirdperson);
|
||||
}
|
8
src/client/precache.h
Normal file
8
src/client/precache.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
|
||||
var int PART_EXPLOSION;
|
||||
|
||||
|
||||
void ClientGame_Precache(void);
|
||||
|
||||
|
182
src/client/precache.qc
Normal file
182
src/client/precache.qc
Normal file
|
@ -0,0 +1,182 @@
|
|||
|
||||
|
||||
void ClientGame_Precache(void){
|
||||
// Yes, this seems to be happening twice at startup since the
|
||||
// hook from RendererRestart.
|
||||
// IDEA: test in FreeHL / FreeCS. Do they call RendererRestart twice at startup too?
|
||||
printfline("***ClientGame_Precache called***");
|
||||
SharedGame_Precache();
|
||||
|
||||
|
||||
|
||||
//TAGGG - From FreeHL. Anything that ends up unused by FreeTS can be removed,
|
||||
// including sound config files that are then unused.
|
||||
////////////////////////////////////////////////////////////
|
||||
precache_model("models/shell.mdl");
|
||||
precache_model("models/shotgunshell.mdl");
|
||||
precache_model("sprites/muzzleflash1.spr");
|
||||
precache_model("sprites/muzzleflash2.spr");
|
||||
precache_model("sprites/muzzleflash3.spr");
|
||||
|
||||
Sound_Precache("modelevent_shell.land");
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
precache_sound("weapons/draw.wav");
|
||||
|
||||
|
||||
|
||||
PART_EXPLOSION = particleeffectnum("explosion.explosion_grenade");
|
||||
|
||||
|
||||
precache_model("sprites/mapsprites/ts_gpc1.spr");
|
||||
precache_model("sprites/mapsprites/ts_gpc2.spr");
|
||||
|
||||
precache_model("sprites/player/crouched.spr");
|
||||
precache_model("sprites/player/divestepb.spr");
|
||||
precache_model("sprites/player/divestepc.spr");
|
||||
precache_model("sprites/player/divestept.spr");
|
||||
precache_model("sprites/player/health.spr");
|
||||
precache_model("sprites/player/kevlar.spr");
|
||||
precache_model("sprites/player/kungfu.spr");
|
||||
precache_model("sprites/player/movement.spr");
|
||||
precache_model("sprites/player/prone.spr");
|
||||
precache_model("sprites/player/run.spr");
|
||||
precache_model("sprites/player/stand.spr");
|
||||
|
||||
|
||||
for(int i = 1; i < WEAPON_ID::LAST_ID; i++){
|
||||
if(ary_weaponData[i] != NULL){
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_weaponData[i];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
if(basicRef.sIconFilePath != NULL){
|
||||
precache_model(basicRef.sIconFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 1; i < WEAPON_AKIMBO_UPGRADE_ID::LAST_ID; i++){
|
||||
if(ary_akimboUpgradeData[i] != NULL){
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_akimboUpgradeData[i];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
if(basicRef.sIconFilePath != NULL){
|
||||
precache_model(basicRef.sIconFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
precache_model("sprites/weapons/item_installed.spr");
|
||||
precache_model("sprites/weapons/item_not_installed.spr");
|
||||
|
||||
|
||||
precache_model("sprites/wires/bwire.spr");
|
||||
precache_model("sprites/wires/bwirecutted.spr");
|
||||
precache_model("sprites/wires/gwire.spr");
|
||||
precache_model("sprites/wires/gwirecutted.spr");
|
||||
precache_model("sprites/wires/rwire.spr");
|
||||
precache_model("sprites/wires/rwirecutted.spr");
|
||||
|
||||
precache_model("sprites/3dm_branch.spr");
|
||||
precache_model("sprites/3dm_leaves1.spr");
|
||||
precache_model("sprites/3dm_leaves2.spr");
|
||||
precache_model("sprites/3dm_leaves3.spr");
|
||||
precache_model("sprites/3dm_leaves4.spr");
|
||||
precache_model("sprites/3dm_palm_01.spr");
|
||||
precache_model("sprites/3dm_palm_02.spr");
|
||||
precache_model("sprites/3dm_palm_03.spr");
|
||||
precache_model("sprites/3dm_sfa_tva.spr");
|
||||
precache_model("sprites/3dm_sfa_tvb.spr");
|
||||
precache_model("sprites/3dm_sfa_tvc.spr");
|
||||
precache_model("sprites/3dm_sfa_tvd.spr");
|
||||
precache_model("sprites/3dm_sfa_tve.spr");
|
||||
precache_model("sprites/3dm_trunk.spr");
|
||||
precache_model("sprites/3dm_trunk1.spr");
|
||||
precache_model("sprites/3dm_trunk2.spr");
|
||||
precache_model("sprites/black_smoke1.spr");
|
||||
precache_model("sprites/blood_smoke.spr");
|
||||
precache_model("sprites/blood_smoke_add.spr");
|
||||
precache_model("sprites/bloodfrag.spr");
|
||||
precache_model("sprites/bloodfrag2.spr");
|
||||
precache_model("sprites/bullet_trail.spr");
|
||||
precache_model("sprites/bullet_trail2.spr");
|
||||
precache_model("sprites/bullet_trail3.spr");
|
||||
precache_model("sprites/bullet_wave.spr");
|
||||
precache_model("sprites/bullet_wave2.spr");
|
||||
precache_model("sprites/compass.spr");
|
||||
precache_model("sprites/conc_decal.spr");
|
||||
precache_model("sprites/confrag.spr");
|
||||
precache_model("sprites/cross_e.spr");
|
||||
precache_model("sprites/cross_t.spr");
|
||||
|
||||
//precache_model("sprites/cross2.spr");
|
||||
precache_pic("textures/cross2.tga");
|
||||
|
||||
|
||||
precache_model("sprites/cursor.spr");
|
||||
precache_model("sprites/debris_concrete1.spr");
|
||||
precache_model("sprites/debris_concrete2.spr");
|
||||
precache_model("sprites/debris_concrete3.spr");
|
||||
precache_model("sprites/downarrow.spr");
|
||||
precache_model("sprites/drop.spr");
|
||||
precache_model("sprites/dust.spr");
|
||||
precache_model("sprites/flash.spr");
|
||||
precache_model("sprites/glassfrag.spr");
|
||||
precache_model("sprites/glow01.spr");
|
||||
precache_model("sprites/gun_muzzle.spr");
|
||||
precache_model("sprites/gun_muzzle2.spr");
|
||||
precache_model("sprites/gun_smoke_add.spr");
|
||||
precache_model("sprites/knife_decal.spr");
|
||||
precache_model("sprites/laserbeam.spr");
|
||||
precache_model("sprites/laserdot.spr");
|
||||
precache_model("sprites/mazyleaves1.spr");
|
||||
precache_model("sprites/mazyleaves2.spr");
|
||||
precache_model("sprites/mazyleaves3.spr");
|
||||
precache_model("sprites/mazyleaves4.spr");
|
||||
precache_model("sprites/mazytrunk1.spr");
|
||||
precache_model("sprites/metal_decal.spr");
|
||||
precache_model("sprites/muzzle1.spr");
|
||||
precache_model("sprites/numbers.spr");
|
||||
precache_model("sprites/ombra.spr");
|
||||
precache_model("sprites/quarterscope.spr");
|
||||
precache_model("sprites/redfl1.spr");
|
||||
precache_model("sprites/scintille.spr");
|
||||
precache_model("sprites/scintille_metal.spr");
|
||||
precache_model("sprites/scintille2.spr");
|
||||
precache_model("sprites/shotgun_pellets.spr");
|
||||
precache_model("sprites/slowmotion.spr");
|
||||
precache_model("sprites/ts_muzzleflash1.spr");
|
||||
precache_model("sprites/ts_muzzleflash1h.spr");
|
||||
precache_model("sprites/ts_muzzleflash1s.spr");
|
||||
precache_model("sprites/ts_muzzleflash2h.spr");
|
||||
precache_model("sprites/ts_muzzleflash6.spr");
|
||||
precache_model("sprites/ts_muzzleflash6b.spr");
|
||||
precache_model("sprites/uparrow.spr");
|
||||
precache_model("sprites/wood_decal.spr");
|
||||
precache_model("sprites/wood_smoke_add.spr");
|
||||
precache_model("sprites/woodfrag.spr");
|
||||
precache_model("sprites/xsmoke1.spr");
|
||||
|
||||
|
||||
// for tga's only. drop the extension... I think.
|
||||
precache_pic("textures/quarterscope.tga");
|
||||
//precache_pic("textures/scope_cross_plus.tga");
|
||||
precache_pic("textures/scope_cross_plus_odd.tga");
|
||||
//precache_pic("textures/quarterscope_small.tga");
|
||||
//precache_pic("textures/cal_9x18mm.tga");
|
||||
|
||||
|
||||
//...do we know what of these we even need?
|
||||
precache_model("sprites/fexplo.spr");
|
||||
|
||||
precache_sound("common/wpn_hudon.wav");
|
||||
precache_sound("common/wpn_hudoff.wav");
|
||||
precache_sound("common/wpn_moveselect.wav");
|
||||
precache_sound("common/wpn_select.wav");
|
||||
|
||||
|
||||
|
||||
}
|
104
src/client/progs.src
Normal file
104
src/client/progs.src
Normal file
|
@ -0,0 +1,104 @@
|
|||
#pragma target fte
|
||||
#pragma progs_dat "../../csprogs.dat"
|
||||
|
||||
#define CSQC
|
||||
#define CLIENT
|
||||
#define TS
|
||||
//TAGGG - do we want this? FreeHL had it I think, FreeCS definitely does
|
||||
#define CLASSIC_VGUI
|
||||
#define GS_RENDERFX
|
||||
|
||||
|
||||
// NEW. When present, the origin is exactly at the center of the player, no further offsets.
|
||||
// Keeps the view identical on the player dying and becoming spectator in place.
|
||||
//#define DEBUG_FORCE_NO_VIEW_OFFSET
|
||||
|
||||
|
||||
#includelist
|
||||
../../../src/shared/fteextensions.qc
|
||||
../../../src/shared/defs.h
|
||||
../../../src/client/defs.h
|
||||
|
||||
//TAGGG - NEW
|
||||
../shared/util.h
|
||||
util.h
|
||||
../shared/defs.h
|
||||
defs.h
|
||||
|
||||
../shared/ammo.h
|
||||
../shared/weapons.h
|
||||
../shared/player.h
|
||||
../shared/powerup.h
|
||||
|
||||
clientinfo.h
|
||||
seatlocal.h
|
||||
input.h
|
||||
|
||||
//TAGGG - NEW
|
||||
precache.h
|
||||
vgui.h
|
||||
ui_eventgrabber.h
|
||||
vgui_buysidemenu.h
|
||||
hud_weaponselect.h
|
||||
inventory_logic_draw.h
|
||||
view.h
|
||||
hud.h
|
||||
// Yes, really, server/entity... We have a clientside rendering component to this.
|
||||
../server/entity/ts_powerup.h
|
||||
|
||||
|
||||
../../../src/vgui/include.src
|
||||
|
||||
../../../src/gs-entbase/client.src
|
||||
../../../src/gs-entbase/shared.src
|
||||
../shared/include.src
|
||||
|
||||
../server/entity/ts_powerup.qc
|
||||
|
||||
../shared/player.qc
|
||||
../shared/inventory_logic.qc
|
||||
|
||||
//TAGGG - NEW
|
||||
input.qc
|
||||
vguiobjects.qc
|
||||
vgui_motd.qc
|
||||
vgui_buysidemenu.qc
|
||||
//vgui_spectator.c ????
|
||||
vgui.qc
|
||||
|
||||
hud_crosshair.qc
|
||||
hud_scope.qc
|
||||
|
||||
draw.qc
|
||||
init.qc
|
||||
seatlocal.qc
|
||||
player.qc
|
||||
clientinfo.qc
|
||||
entities.qc
|
||||
cmds.qc
|
||||
game_event.qc
|
||||
view.qc
|
||||
obituary.qc
|
||||
hud.qc
|
||||
hud_weaponselect.qc
|
||||
inventory_logic_draw.qc
|
||||
scoreboard.qc
|
||||
../../../base/src/client/modelevent.qc
|
||||
|
||||
|
||||
//TAGGG - NEW
|
||||
util.qc
|
||||
precache.qc
|
||||
//../_base/client/voice.c
|
||||
//../_base/client/sound.c
|
||||
//../_base/client/music.c
|
||||
//../_base/client/prints.c
|
||||
//../_base/client/util.c
|
||||
|
||||
|
||||
../../../src/client/include.src
|
||||
../../../src/shared/include.src
|
||||
|
||||
ui_eventgrabber.qc
|
||||
|
||||
#endlist
|
190
src/client/scoreboard.qc
Normal file
190
src/client/scoreboard.qc
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define SCORE_HEADER_C [255/255,156/255,0]
|
||||
#define SCORE_LINE_C [255/255,200/255,0]
|
||||
|
||||
var int autocvar_cl_centerscores = FALSE;
|
||||
var int g_scores_teamplay = 0;
|
||||
|
||||
void
|
||||
Scores_Init(void)
|
||||
{
|
||||
g_scores_teamplay = (int)serverkeyfloat("teamplay");
|
||||
}
|
||||
|
||||
void
|
||||
Scores_DrawTeam(player pl, vector pos)
|
||||
{
|
||||
drawfill(pos, [290, 1], SCORE_LINE_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
drawfont = FONT_20;
|
||||
drawstring(pos + [0,-18], "Teams", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
drawstring(pos + [124,-18], "kills / deaths", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
drawstring(pos + [240,-18], "latency", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
pos[1] += 12;
|
||||
|
||||
for (int t = 1; t <= serverkeyfloat("teams"); t++) {
|
||||
float l;
|
||||
string temp;
|
||||
drawstring(pos, serverkey(sprintf("team_%i", t)), [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
temp = serverkey(sprintf("teamscore_%i", t));
|
||||
l = stringwidth(temp, FALSE, [20,20]);
|
||||
drawstring(pos + [150-l, 0], temp, [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
drawstring(pos + [158, 0], "wins", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
pos[1] += 16;
|
||||
|
||||
for (int i = -1; i > -32; i--) {
|
||||
if (getplayerkeyfloat(i, "*team") != t) {
|
||||
continue;
|
||||
}
|
||||
|
||||
temp = getplayerkeyvalue(i, "name");
|
||||
|
||||
/* Out of players */
|
||||
if (!temp) {
|
||||
break;
|
||||
} else if (temp == getplayerkeyvalue(pl.entnum-1, "name")) {
|
||||
drawfill(pos, [290, 13], [0,0,1], 0.5f, DRAWFLAG_ADDITIVE);
|
||||
}
|
||||
|
||||
drawstring(pos + [24,0], getplayerkeyvalue(i, "name"), [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
drawstring(pos + [154,0], "/", [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
/* Get the kills and align them left to right */
|
||||
temp = getplayerkeyvalue(i, "frags");
|
||||
l = stringwidth(temp, FALSE, [20,20]);
|
||||
drawstring(pos + [150 - l,0], temp, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
/* Deaths are right to left aligned */
|
||||
temp = getplayerkeyvalue(i, "*deaths");
|
||||
drawstring(pos + [165,0], temp, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
/* Get the latency and align it left to right */
|
||||
temp = getplayerkeyvalue(i, "ping");
|
||||
l = stringwidth(temp, FALSE, [20,20]);
|
||||
|
||||
|
||||
//TAGGG - IMPORTANT!
|
||||
// I don't know if TS does this too, but its scoreboard screen looks a lot like Half-Life's
|
||||
// so keeping this for now?
|
||||
if (getplayerkeyfloat(i, "*dead") == 1) {
|
||||
drawsubpic(
|
||||
pos - [8,0],
|
||||
[32,16],
|
||||
g_hud1_spr,
|
||||
[224/256, 240/256],
|
||||
[32/256, 16/256],
|
||||
[1,0,0],
|
||||
1.0f,
|
||||
DRAWFLAG_ADDITIVE
|
||||
);
|
||||
}
|
||||
|
||||
drawstring(pos + [290 - l,0], temp, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
pos[1] += 20;
|
||||
}
|
||||
pos[1] += 12;
|
||||
}
|
||||
|
||||
drawfont = FONT_CON;
|
||||
}
|
||||
|
||||
void
|
||||
Scores_DrawNormal(player pl, vector pos)
|
||||
{
|
||||
drawfill(pos, [290, 1], SCORE_LINE_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
drawfont = FONT_20;
|
||||
drawstring(pos + [0,-18], "Player", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
drawstring(pos + [124,-18], "kills / deaths", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
drawstring(pos + [240,-18], "latency", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
pos[1] += 12;
|
||||
for (int i = -1; i > -32; i--) {
|
||||
float l;
|
||||
string ping;
|
||||
string kills;
|
||||
string deaths;
|
||||
string name;
|
||||
|
||||
if (getplayerkeyfloat(i, "*spec") != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
name = getplayerkeyvalue(i, "name");
|
||||
|
||||
/* Out of players */
|
||||
if (!name) {
|
||||
break;
|
||||
} else if (name == getplayerkeyvalue(pl.entnum-1, "name")) {
|
||||
drawfill(pos, [290, 13], [0,0,1], 0.5f, DRAWFLAG_ADDITIVE);
|
||||
}
|
||||
|
||||
drawstring(pos, getplayerkeyvalue(i, "name"), [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
drawstring(pos + [154,0], "/", [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
/* Get the kills and align them left to right */
|
||||
kills = getplayerkeyvalue(i, "frags");
|
||||
l = stringwidth(kills, FALSE, [20,20]);
|
||||
drawstring(pos + [150 - l,0], kills, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
/* Deaths are right to left aligned */
|
||||
deaths = getplayerkeyvalue(i, "*deaths");
|
||||
drawstring(pos + [165,0], deaths, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
|
||||
/* Get the latency and align it left to right */
|
||||
ping = getplayerkeyvalue(i, "ping");
|
||||
l = stringwidth(ping, FALSE, [20,20]);
|
||||
|
||||
drawstring(pos + [290 - l,0], ping, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE);
|
||||
pos[1] += 20;
|
||||
}
|
||||
|
||||
drawfont = FONT_CON;
|
||||
}
|
||||
|
||||
void
|
||||
Scores_Draw(void)
|
||||
{
|
||||
vector pos;
|
||||
player pl;
|
||||
|
||||
pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if (autocvar_cl_centerscores) {
|
||||
int c = 10;
|
||||
|
||||
/* calculate all valid entries */
|
||||
for (int i = -1; i > -32; i--) {
|
||||
if (getplayerkeyvalue(i, "name") && getplayerkeyfloat(i, "*spec") != 1) {
|
||||
c += 10;
|
||||
}
|
||||
}
|
||||
|
||||
c += (serverkeyfloat("teams") * 10);
|
||||
pos = video_mins + [(video_res[0] / 2) - 145, (video_res[1] / 2) - c];
|
||||
} else {
|
||||
pos = video_mins + [(video_res[0] / 2) - 145, 30];
|
||||
}
|
||||
|
||||
if (serverkeyfloat("teams") > 0) {
|
||||
Scores_DrawTeam(pl, pos);
|
||||
} else {
|
||||
Scores_DrawNormal(pl, pos);
|
||||
}
|
||||
}
|
48
src/client/seatlocal.h
Normal file
48
src/client/seatlocal.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
// Moved here for better control of compile order.
|
||||
// Must be aware of the ClientInfo_t struct, which in turn must be
|
||||
// aware of some weapon-related structs before this point is reached
|
||||
// in compilation.
|
||||
|
||||
|
||||
struct
|
||||
{
|
||||
// PENDING - probably safe to remove these
|
||||
int m_iHealthOld;
|
||||
float m_flHealthAlpha;
|
||||
int m_iArmorOld;
|
||||
float m_flArmorAlpha;
|
||||
int m_iAmmo1Old;
|
||||
float m_flAmmo1Alpha;
|
||||
int m_iAmmo2Old;
|
||||
float m_flAmmo2Alpha;
|
||||
int m_iAmmo3Old;
|
||||
float m_flAmmo3Alpha;
|
||||
int m_iPickupWeapon;
|
||||
float m_flPickupAlpha;
|
||||
int m_iHUDWeaponSelected;
|
||||
float m_flHUDWeaponSelectTime;
|
||||
|
||||
float m_flPrevVGUI;
|
||||
BOOL m_bNeedPrimaryRelease;
|
||||
float m_flReleaseTime;
|
||||
|
||||
|
||||
//TAGGG - assuming this is a fine place to put this
|
||||
// It's the slower movement from holding shift down.
|
||||
int iInputSpeed;
|
||||
// Keeping for now, remove later
|
||||
float fVGUI_Display;
|
||||
|
||||
// like pSeat->ePlayer, but already casted to the gamemod's custom "player" class.
|
||||
// WARNING: Not yet set, find some place around the start of each frame where it
|
||||
// makes sense to set this.
|
||||
// Nevermind, do-able but likely won't be used much. Do m_clientinfo instead
|
||||
//player m_ePlayerRef;
|
||||
ClientInfo_t m_clientinfo;
|
||||
|
||||
} g_seatslocal[4], *pSeatLocal;
|
||||
|
||||
|
||||
|
||||
void pSeatLocal_init(void);
|
12
src/client/seatlocal.qc
Normal file
12
src/client/seatlocal.qc
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
// assumes pSeatLocal is set appropriately in advance.
|
||||
// Would send something of that struct's type, but... looks like that isn't really
|
||||
// an option. Like "structType* arg_this".
|
||||
void
|
||||
pSeatLocal_init(void)
|
||||
{
|
||||
pSeatLocal->m_flPrevVGUI = VGUI_SCREEN::NONE;
|
||||
pSeatLocal->m_bNeedPrimaryRelease = FALSE;
|
||||
pSeatLocal->m_flReleaseTime = 0;
|
||||
}
|
||||
|
8
src/client/ui_eventgrabber.h
Normal file
8
src/client/ui_eventgrabber.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
// Just to be aware of these for earlier compiled things to be able to
|
||||
// have some control.
|
||||
void gFun_UI_EventGrabber_Initialize(void);
|
||||
void gFun_UI_EventGrabber_Show(void);
|
||||
void gFun_UI_EventGrabber_Hide(void);
|
||||
|
||||
|
88
src/client/ui_eventgrabber.qc
Normal file
88
src/client/ui_eventgrabber.qc
Normal file
|
@ -0,0 +1,88 @@
|
|||
|
||||
// This file is named "vgui_late" for being compiled after the nuclide "src" project
|
||||
// includes so that CUIWidget (src/vgui/ui.qc) is available.
|
||||
|
||||
|
||||
|
||||
// example of VGUI usage: see FreeCS, mention of this:
|
||||
// winChooseTeam
|
||||
|
||||
|
||||
// Simple UIWidget to absorb mouse-click and key-press events while in
|
||||
// the MoTD or the buysidemenu screens.
|
||||
// Do not give me child elements! Behavior for handling children removed.
|
||||
|
||||
class CUIEventGrabber:CUIWidget
|
||||
{
|
||||
//void(void) CUIEventGrabber;
|
||||
virtual void(void) Draw;
|
||||
virtual void(float, float, float, float) Input;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
CUIEventGrabber::Draw(void)
|
||||
{
|
||||
// nothing!
|
||||
}
|
||||
|
||||
//TODO: globals from src/client/defs.h over here now (like TS_mouseClickRef, etc.)
|
||||
|
||||
void
|
||||
CUIEventGrabber::Input(float flEVType, float flKey, float flChar, float flDevID)
|
||||
{
|
||||
|
||||
// no need to do this.
|
||||
//g_vguiWidgetCount++;
|
||||
|
||||
switch (flEVType) {
|
||||
|
||||
case IE_KEYDOWN:
|
||||
//printf("CUIEventGrabber::Input %d\n", flKey);
|
||||
if (flKey == K_MOUSE1) {
|
||||
TS_mouseClickRef = 1;
|
||||
} else {
|
||||
TS_keyRefDown = 1;
|
||||
}
|
||||
TS_keyRefUp = flKey;
|
||||
TS_keyRefTapped = flKey; //lasts only this frame
|
||||
TS_keyRefUpASCII = flChar;
|
||||
break;
|
||||
case IE_KEYUP:
|
||||
if (flKey == K_MOUSE1) {
|
||||
TS_mouseClickRef = 0;
|
||||
} else {
|
||||
TS_keyRefDown = 0;
|
||||
}
|
||||
TS_keyRefUp = 0;
|
||||
TS_keyRefUpASCII = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Could include this a lot earlier, but no point as it's useless without being
|
||||
// able to call methods of it until CUIEventGrabber is defined (which requires
|
||||
// UIWidget, which requires all nuclide stuff to be included, which happens late
|
||||
// into the compile)
|
||||
var CUIEventGrabber g_UI_EventGrabber;
|
||||
|
||||
|
||||
void gFun_UI_EventGrabber_Initialize(void){
|
||||
g_UI_EventGrabber = spawn(CUIEventGrabber);
|
||||
g_uiDesktop.Add(g_UI_EventGrabber);
|
||||
|
||||
// make it "visible", or rather, "active" for grabbing input events?
|
||||
// If this flag is missing it gets overlooked in ui.qc's run-through
|
||||
// of child elements for sending input events to is skipped.
|
||||
// Actually not even here, let calls to _Show or _Hide (below) handle this.
|
||||
//g_UI_EventGrabber.m_iFlags |= 1;
|
||||
//g_UI_EventGrabber.FlagAdd(1);
|
||||
}
|
||||
|
||||
void gFun_UI_EventGrabber_Show(void){
|
||||
g_UI_EventGrabber.FlagAdd(1);
|
||||
}
|
||||
void gFun_UI_EventGrabber_Hide(void){
|
||||
g_UI_EventGrabber.FlagRemove(1);
|
||||
}
|
114
src/client/util.h
Normal file
114
src/client/util.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
|
||||
#ifndef TS_CLIENT_UTIL_H
|
||||
#define TS_CLIENT_UTIL_H
|
||||
|
||||
|
||||
#define BITS_DIGITOPT_NONE 0
|
||||
#define BITS_DIGITOPT_FILLER0 1
|
||||
//Give filler 0's in front of the largest used digit if there were leftover digits
|
||||
#define BITS_DIGITOPT_FILLERPAD 2
|
||||
//Give filler padding (just space) in front of the largest used digit if there were leftover digits
|
||||
|
||||
// *** NOTICE - FILLER0 and FILLERPAD are incompatible. Use neither or either.
|
||||
|
||||
#define BITS_DIGITOPT_DEFAULT BITS_DIGITOPT_FILLERPAD
|
||||
|
||||
|
||||
// space between the quote-pairs or not, and it concatenates it.
|
||||
#define CREATE_imageFileRef_t(arg_newVarName, arg_sFilePath, arg_w, arg_h) imageFileRef_t arg_newVarName = { arg_sFilePath"_0.tga" , arg_w, arg_h};
|
||||
|
||||
//New version that does not do the "_0.tga" addition.
|
||||
// We may want to use straight .tga files sometimes, which shouldn't get this change.
|
||||
#define CREATE_imageFileRef_raw_t(arg_newVarName, arg_sFilePath, arg_w, arg_h) imageFileRef_t arg_newVarName = { arg_sFilePath, arg_w, arg_h};
|
||||
|
||||
|
||||
#define CREATE_imageCropBounds_t(arg_newVarName, arg_imageFileRef, arg_srcPos_x, arg_srcPos_y, arg_srcSiz_x, arg_srcSiz_y) imageCropBounds_t arg_newVarName = {arg_imageFileRef, [arg_srcSiz_x, arg_srcSiz_y], [arg_srcPos_x/arg_imageFileRef.w, arg_srcPos_y/arg_imageFileRef.h], [arg_srcSiz_x/arg_imageFileRef.w, arg_srcSiz_y/arg_imageFileRef.h]};
|
||||
|
||||
//NO arg_vDrawScale. would have come after "arg_vDrawPos". A version that supports scaling can be done if needed.
|
||||
//#define DRAW_IMAGE_BOUNDS_ADDITIVE(arg_sImageFileName, arg_imgBoundsVarName, arg_vDrawPos, arg_RGB, arg_alpha)
|
||||
|
||||
#define DRAW_IMAGE_CROPPED_ADDITIVE(arg_cropBoundsVarName, arg_vDrawPos, arg_RGB, arg_alpha) drawsubpic(arg_vDrawPos, arg_cropBoundsVarName.vSize, arg_cropBoundsVarName.myImageFileRef.sFilePath, arg_cropBoundsVarName.vCropStart, arg_cropBoundsVarName.vCropSize, arg_RGB, arg_alpha, DRAWFLAG_ADDITIVE);
|
||||
|
||||
|
||||
//And a simple version
|
||||
//scale not supported yet - can add that if needed.
|
||||
#define DRAW_IMAGE_ADDITIVE(arg_imageFileRefVarName, arg_vDrawPos, arg_clr, arg_alpha) drawsubpic(arg_vDrawPos, [arg_imageFileRefVarName.w, arg_imageFileRefVarName.h], arg_imageFileRefVarName.sFilePath, [0, 0], [1, 1], arg_clr, arg_alpha, DRAWFLAG_ADDITIVE);
|
||||
|
||||
#define DRAW_IMAGE_NORMAL(arg_imageFileRefVarName, arg_vDrawPos, arg_clr, arg_alpha) drawsubpic(arg_vDrawPos, [arg_imageFileRefVarName.w, arg_imageFileRefVarName.h], arg_imageFileRefVarName.sFilePath, [0, 0], [1, 1], arg_clr, arg_alpha, DRAWFLAG_NORMAL);
|
||||
|
||||
|
||||
#define DRAW_IMAGE_EXPER(arg_imageFileRefVarName, arg_vDrawPos, arg_vDrawPivot, flAngle, vScale, vRGB, flOpac) Gfx_RotScalePic(arg_imageFileRefVarName.sFilePath, arg_vDrawPos, arg_vDrawPivot, [arg_imageFileRefVarName.w, arg_imageFileRefVarName.h], flAngle, vScale, vRGB, flOpac)
|
||||
#define DRAW_IMAGE_EXPER2(arg_imageFileRefVarName, arg_vDrawPos, vScale, vInset, vRGB, flOpac) Gfx_ScalePicPreserveBounds(arg_imageFileRefVarName.sFilePath, arg_vDrawPos, [arg_imageFileRefVarName.w, arg_imageFileRefVarName.h], vScale, vInset, vRGB, flOpac)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(CSQC) || defined(MENU)
|
||||
|
||||
//TAGGG - just give those bits names
|
||||
#define DRAWTEXTFIELD_ALIGN_LEFT 1
|
||||
#define DRAWTEXTFIELD_ALIGN_TOP 2
|
||||
#define DRAWTEXTFIELD_ALIGN_RIGHT 4
|
||||
#define DRAWTEXTFIELD_ALIGN_BOTTOM 8
|
||||
|
||||
//Use below to get an intention in one constant.
|
||||
#define DRAWTEXTFIELD_ALIGN_CENTER_CENTER 0
|
||||
#define DRAWTEXTFIELD_ALIGN_LEFT_CENTER 1
|
||||
#define DRAWTEXTFIELD_ALIGN_LEFT_TOP 1|2
|
||||
#define DRAWTEXTFIELD_ALIGN_CENTER_TOP 2
|
||||
#define DRAWTEXTFIELD_ALIGN_RIGHT_TOP 4|2
|
||||
#define DRAWTEXTFIELD_ALIGN_RIGHT_CENTER 4
|
||||
#define DRAWTEXTFIELD_ALIGN_RIGHT_BOTTOM 4|8
|
||||
#define DRAWTEXTFIELD_ALIGN_CENTER_BOTTOM 8
|
||||
#define DRAWTEXTFIELD_ALIGN_LEFT_BOTTOM 1|8
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct{
|
||||
string sFilePath;
|
||||
float w;
|
||||
float h;
|
||||
} imageFileRef_t;
|
||||
|
||||
typedef struct{
|
||||
imageFileRef_t myImageFileRef;
|
||||
vector vSize;
|
||||
|
||||
//NOTICE - vCropStart and vCropSize are fractions (decimals, between 0 and 1) that
|
||||
// show what portion of the picked image to crop from.
|
||||
// uhh... somewhere this decision may make sense?
|
||||
vector vCropStart;
|
||||
vector vCropSize;
|
||||
/*
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
float h;
|
||||
*/
|
||||
} imageCropBounds_t;
|
||||
|
||||
|
||||
|
||||
|
||||
//easy ref.
|
||||
extern const vector vecZero;
|
||||
|
||||
int drawSpriteNumber(imageCropBounds_t* arg_ary_spriteSet, int arg_draw_x, int arg_draw_y, int arg_quantityToDraw, int arg_digits, int arg_bits, vector arg_clr, float arg_opac);
|
||||
void Gfx_Text(vector vPos, string sText, vector vSize, vector vRGB, float flAlpha, float flDrawFlag, float flFont);
|
||||
void Gfx_TextLineWrap( vector vPos, vector vSize, float fAlignFlags, string sText, float flFont );
|
||||
void Gfx_RotScalePic( string sImage, vector arg_vDrawPos, vector arg_vDrawPivot, vector vSize, float flAngle, vector vScale, vector vRGB, float flOpac );
|
||||
void Gfx_ScalePicPreserveBounds(string sImage, vector arg_vDrawPos, vector vSize, vector vScale, vector vInset, vector vRGB, float flOpac);
|
||||
|
||||
|
||||
#endif // TS_CLIENT_UTIL_H
|
186
src/client/util.qc
Normal file
186
src/client/util.qc
Normal file
|
@ -0,0 +1,186 @@
|
|||
|
||||
|
||||
|
||||
//err. what?
|
||||
//Then how do I supply a list of things to draw from?
|
||||
//use another struct that itself has the array? well ok.
|
||||
// OR make it a pointer and assume its length (or send it separately if needed... in this case
|
||||
// we know it has to have 10 images: 1 for each of the digits, 0 - 9)
|
||||
//../ts/client/vgui_buysidemenu.c:1745: error: Array arguments are not supported
|
||||
// TODO - make this a more general utility for any UI, not just the inventory-related stuff.
|
||||
//Now supports returning the number of pixels of width of the string it drew for nearby UI to see too.
|
||||
int
|
||||
drawSpriteNumber(imageCropBounds_t* arg_ary_spriteSet, int arg_draw_x, int arg_draw_y, int arg_quantityToDraw, int arg_digits, int arg_bits, vector arg_clr, float arg_opac){
|
||||
|
||||
//The "arg_bits" can give extra options about drawing. "0" means straight defaults.
|
||||
//NONE SUPPORTED YET. If so, give enum like
|
||||
|
||||
//currently looking at leading 0's (whether they are drawn or not). The first non-zero digit stops this.
|
||||
BOOLEAN leadingZeros = TRUE;
|
||||
|
||||
int spriteWidth = arg_ary_spriteSet[0].vSize.x;
|
||||
int sprigeHeight = arg_ary_spriteSet[0].vSize.y;
|
||||
int quantityYet = arg_quantityToDraw;
|
||||
|
||||
//if(arg_digits != -1){
|
||||
int maxPossible = pow(10, arg_digits) - 1; //...9999, 999, 99, 9
|
||||
|
||||
if(quantityYet < 0){
|
||||
//negatives not allowed, no need seen yet.
|
||||
//To support negatives, we could draw a negative sign left of the highest digit place possible or
|
||||
//of the drawn digit (subjective) and force "quantityYet" positive to draw it as expected.
|
||||
quantityYet = 0;
|
||||
}else if(quantityYet > maxPossible){
|
||||
// force it to the max, that's all we can display
|
||||
//quantityYet = maxPossible;
|
||||
// ACTUALLY, go ahead and show this larger number anyway.
|
||||
// Just take each additional digit necessary and push everything to the right.
|
||||
// That is, if we planned on drawing 3 digits, but have to render the number 3600,
|
||||
// and are told to draw it at draw_x = 300, we may start drawing it at x=280 instead to
|
||||
// reach the same right point
|
||||
string tempString = itos(quantityYet);
|
||||
int digitsNeeded = (int)strlen(tempString);
|
||||
if(digitsNeeded > arg_digits){
|
||||
arg_draw_x -= (digitsNeeded - arg_digits)*spriteWidth;
|
||||
arg_digits = digitsNeeded;
|
||||
}
|
||||
|
||||
}
|
||||
//}else{
|
||||
// //no max constraint, but still no negatives yet.
|
||||
// if(quantityYet < 0){
|
||||
// quantityYet = 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
int leftChange = 0;
|
||||
int digitCount = 0;
|
||||
int totalDrawWidth = 0;
|
||||
|
||||
vector vDrawPosYet;
|
||||
for(int i = arg_digits; i >= 1; i--){
|
||||
vDrawPosYet.x = arg_draw_x + (arg_digits - i) * spriteWidth + leftChange;
|
||||
vDrawPosYet.y = arg_draw_y + 0;
|
||||
|
||||
//pow(10, i) - 1; //...9999, 999, 99, 9
|
||||
//pow(10, i-1); //...1000, 100, 10, 1
|
||||
//pow(10, -(i-1)); //...0.001, 0.01, 0.1, 1
|
||||
|
||||
float multi = pow(10, -(i - 1));
|
||||
float bigMulti = pow(10, (i - 1));
|
||||
int digitAtPlace = (int)(quantityYet * multi);
|
||||
|
||||
if(leadingZeros == TRUE && digitAtPlace == 0 && i > 1){
|
||||
//another leading 0? this is a 0? Not the 1st digit?
|
||||
|
||||
if(arg_bits & BITS_DIGITOPT_FILLER0){
|
||||
//still draw it here.
|
||||
//example, for drawing a value of 872 with 5 digits allowed:
|
||||
// 00872
|
||||
DRAW_IMAGE_CROPPED_ADDITIVE(arg_ary_spriteSet[digitAtPlace], vDrawPosYet, arg_clr, arg_opac)
|
||||
totalDrawWidth += spriteWidth;
|
||||
}else if(arg_bits & BITS_DIGITOPT_FILLERPAD){
|
||||
//this means, still skip over this space as though a filler digit were here.
|
||||
//example from above (5 digits):
|
||||
// 872 (two spaces for the unused 2 digits)
|
||||
//(this behavior is natural; nothing special happens)
|
||||
}else{
|
||||
//no filler0, no fillerPad? Then skip over the space for unused digits.
|
||||
leftChange -= spriteWidth;
|
||||
//example from above:
|
||||
// 872
|
||||
}
|
||||
|
||||
}else{
|
||||
//First non-zero digit makes leadingZeros FALSE for the first time.
|
||||
//Also, the very first digit must be drawn unconditionally. Even if 0.
|
||||
//All digits after this will be drawn.
|
||||
leadingZeros = FALSE;
|
||||
//draw digitAtPlace.
|
||||
DRAW_IMAGE_CROPPED_ADDITIVE(arg_ary_spriteSet[digitAtPlace], vDrawPosYet, arg_clr, arg_opac)
|
||||
totalDrawWidth += spriteWidth;
|
||||
}
|
||||
|
||||
quantityYet = quantityYet - digitAtPlace*bigMulti;
|
||||
}
|
||||
|
||||
//Not functional, but "quantityYet" should end up as "0" all the time.
|
||||
|
||||
return totalDrawWidth;
|
||||
}//END OF drawSpriteNumber
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//TAGGG - BOTH NEW. Convenience methods for drawing without any font-related struct, just some font's load ID.
|
||||
// Otherwise close to the engine-provided drawstring and drawtextfield methods.
|
||||
// "drawfont" is a global provided by the engine (fteextensions.qc)
|
||||
// Compare with vgui/font.cpp's "Font_DrawText" and "Font_DrawField" which take a font struct.
|
||||
void
|
||||
Gfx_Text(vector vPos, string sText, vector vSize, vector vRGB, float flAlpha, float flDrawFlag, float flFont)
|
||||
{
|
||||
drawfont = flFont;
|
||||
drawstring(vPos, sText, vSize, vRGB, flAlpha, flDrawFlag);
|
||||
}
|
||||
|
||||
void
|
||||
Gfx_TextLineWrap( vector vPos, vector vSize, float fAlignFlags, string sText, float flFont )
|
||||
{
|
||||
drawfont = flFont;
|
||||
drawtextfield( vPos, vSize, fAlignFlags, sText );
|
||||
}
|
||||
|
||||
|
||||
//TAGGG - "Gfx_RotScalePic" was made from a blend of methods found in The Wastes.
|
||||
void
|
||||
Gfx_RotScalePic( string sImage, vector arg_vDrawPos, vector arg_vDrawPivot, vector vSize, float flAngle, vector vScale, vector vRGB, float flOpac )
|
||||
{
|
||||
//vector mins = [0, 0];
|
||||
vector mins =
|
||||
[
|
||||
(-arg_vDrawPivot[0] + -vSize[0]/2) * vScale[0],
|
||||
(-arg_vDrawPivot[1] + -vSize[1]/2) * vScale[1]
|
||||
];
|
||||
vector maxs =
|
||||
[
|
||||
(-arg_vDrawPivot[0] + vSize[0]/2) * vScale[0],
|
||||
(-arg_vDrawPivot[1] + vSize[1]/2) * vScale[1]
|
||||
];
|
||||
|
||||
//printfline("Gfx_RotScalePic - %.0f %.0f, %.0f %.0f", mins[0], mins[1], maxs[0], maxs[1]);
|
||||
vector vBaseSize = vSize;
|
||||
|
||||
//Gfx_Pic( vPos + vScalePos, sImage, vScaleSize, vRGB, flAlpha, 0 );
|
||||
drawrotpic(arg_vDrawPos + [arg_vDrawPivot[0]*vScale[0], arg_vDrawPivot[1]*vScale[1]], mins, maxs, sImage, vRGB, flOpac, flAngle);
|
||||
// (time * 40) % 360
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
Gfx_ScalePicPreserveBounds(string sImage, vector arg_vDrawPos, vector vSize, vector vScale, vector vInset, vector vRGB, float flOpac)
|
||||
{
|
||||
vector srcpos = [vInset[0], vInset[1]];
|
||||
vector srcsz;
|
||||
vector sz;
|
||||
|
||||
//srcsz = vSize;
|
||||
srcsz[0] = 1.0 - (vInset[0]*2) ;
|
||||
srcsz[1] = 1.0 - (vInset[1]*2);
|
||||
|
||||
sz[0] = vSize[0] * vScale[0];
|
||||
sz[1] = vSize[1] * vScale[1];
|
||||
|
||||
vector vecOff;// = [0,0];
|
||||
vecOff[0] = -sz[0]/2;
|
||||
vecOff[1] = -sz[1]/2;
|
||||
|
||||
// drawsubpic(pos, sz, pic, srcpos, srcsz, rgb, alpha, 0);
|
||||
drawsubpic(arg_vDrawPos + vecOff, sz, sImage, srcpos, srcsz, vRGB, flOpac); //, 0
|
||||
}
|
||||
|
50
src/client/vgui.h
Normal file
50
src/client/vgui.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
|
||||
|
||||
#define VGUI_WINDOW_BGCOLOR '0.0 0.0 0.0'
|
||||
#define VGUI_WINDOW_FGCOLOR '1.0 0.5 0.0'
|
||||
#define VGUI_WINDOW_BGALPHA 0.76
|
||||
#define VGUI_WINDOW_FGALPHA 1.0
|
||||
|
||||
//var int iVGUIKey;
|
||||
|
||||
vector vVGUIWindowPos;
|
||||
vector vVGUIWindowSiz;
|
||||
//vector vVGUIButtonPos;
|
||||
|
||||
string sMOTDString[25];
|
||||
string sMapString[35];
|
||||
|
||||
var string sMOTD_total;
|
||||
|
||||
class player;
|
||||
|
||||
|
||||
// Keep in synch with the vguiMenus array of vgui.c
|
||||
enum VGUI_SCREEN{
|
||||
NONE = 0,
|
||||
MOTD,
|
||||
BUYSIDEMENU
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
string sTitle;
|
||||
// Whether to do a VGUI_Window call to draw a window.
|
||||
// Not very customizable for now, intended only for the MoTD, but adapts to different screen sizes.
|
||||
// Could have other settings added later, or even be handled per screen's draw call too for completely re-doing
|
||||
BOOLEAN fDrawMainWindowAuto;
|
||||
|
||||
// Custom draw script for a particular screen choice
|
||||
//TAGGG - now accepts how much to change the font size (and adjust other things) by.
|
||||
// Also accepts the player for getting other info from.
|
||||
void(player arg_player, vector vPos, vector vWindowSiz, float fFontSizeMulti ) vDraw;
|
||||
// What to do the moment the screen is changed to this.
|
||||
void(void) vOnInit;
|
||||
} vguiwindow_t;
|
||||
|
||||
|
||||
|
||||
void CSQC_VGUI_Init(void);
|
||||
float CSQC_VGUI_Draw( player arg_player);
|
||||
void VGUI_ChangeScreen(VGUI_SCREEN fNewScreenID);
|
||||
|
||||
|
247
src/client/vgui.qc
Normal file
247
src/client/vgui.qc
Normal file
|
@ -0,0 +1,247 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 2016-2019 Marco 'eukara' Hladik. All rights reserved.
|
||||
*
|
||||
* See the file LICENSE attached with the sources for usage details.
|
||||
*
|
||||
****/
|
||||
|
||||
// Menus with their window titles and draw functions
|
||||
|
||||
//TAGGG
|
||||
// also public now so that the first MOTD element can have its sTitle modified by game
|
||||
// logic. It's a bit more than just one language key now.
|
||||
// And why did the array indeces have a number like this: "[11]"? Looks to adjust fine
|
||||
// to having no number and doing the count itself.
|
||||
// where does "VGUI_TITLE_MODT" or the text it produces ("Message of the day") ever occur
|
||||
// in game files or script?
|
||||
// ANSWERED: it comes from the language file. See one of the files near the compiled
|
||||
// .dat file
|
||||
// (typically in the game's data.pk3dir fodlder) like so:
|
||||
// csprogs.dat.en.po
|
||||
// The identifier and value for that language will show up.
|
||||
// In this case, VGUI_TITLE_MODT won't be used. This new way mimicks the original The
|
||||
// Specialists initial screen a little more.
|
||||
// We can't use logic stuff in a global scope like this. This (vguiMenus[0].sTitle) can
|
||||
// be set in an init method instead.
|
||||
|
||||
|
||||
// Keep in synch with vgui.h's VGUI_SCREEN enum choices, besides the NONE choice.
|
||||
// That isn't represented, not even by a dummy.
|
||||
var vguiwindow_t vguiMenus[] = {
|
||||
//{ _("VGUI_TITLE_MOTD"), VGUI_MessageOfTheDay },
|
||||
{ "", TRUE, VGUI_MessageOfTheDay, NULL },
|
||||
{ "", FALSE, VGUI_BuySideMenu_Update, VGUI_BuySideMenu_OnInit}
|
||||
};
|
||||
|
||||
//var float nextPrintoutTime = -1;
|
||||
|
||||
|
||||
void
|
||||
VGUI_ChangeScreen(VGUI_SCREEN arg_NewScreenID)
|
||||
{
|
||||
pSeatLocal->fVGUI_Display = (float)arg_NewScreenID;
|
||||
|
||||
if(arg_NewScreenID <= VGUI_SCREEN::NONE){
|
||||
// If at NONE or below, also do nothing. This has no "vOnInit" behavior.
|
||||
// Besides obligatory cleanup if we choose (which may as well be done right here)
|
||||
// And turn the cursor lock off.
|
||||
setcursormode(FALSE, "gfx/cursor", [0,0,0], 1.0f);
|
||||
// And don't let Nuclide's src/client/entry.qc try to re-lock the mouse!
|
||||
gFun_UI_EventGrabber_Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// If this screen has a vOnInit, do it!
|
||||
// This is done for the new screen once as soon as it becomes the active one.
|
||||
// Also, turn the cursor lock on, clearly this needs user input.
|
||||
// src/client/entry.qc already has this check too for having any UI widget, but
|
||||
// it takes one mouse-movement for it to kick-in, which causes one slight nudge of the
|
||||
// camera before the cursor appears. Not terrible, but calling for the lock this early
|
||||
// ensures that doesn't get a chance to happen.
|
||||
setcursormode(TRUE, "gfx/cursor", [0,0,0], 1.0f);
|
||||
// And let the event grabber be shown so that entry.qc doesn't try to undo the mouse lock.
|
||||
gFun_UI_EventGrabber_Show();
|
||||
|
||||
if(vguiMenus[arg_NewScreenID - 1].vOnInit != NULL){
|
||||
vguiMenus[arg_NewScreenID - 1].vOnInit();
|
||||
}
|
||||
}//changeScreen
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CSQC_VGUI_Draw
|
||||
|
||||
This is the entry point for FreeTS's (cloned from FreeCS) own "VGUI" implementation
|
||||
Run every frame
|
||||
=================
|
||||
*/
|
||||
float
|
||||
CSQC_VGUI_Draw( player arg_player)
|
||||
{
|
||||
if ( pSeatLocal->fVGUI_Display == VGUI_SCREEN::NONE ) {
|
||||
setcursormode( FALSE );
|
||||
return FALSE;
|
||||
}
|
||||
//int geh = vguiMenus.length;
|
||||
//vTextPos[1] += 14;
|
||||
|
||||
float fontSizeMulti;
|
||||
|
||||
// How much space to add to the left and right of the drawn windows (x) and to the top and
|
||||
// bottom (y).
|
||||
// That does mean each removes twice its value of the drawn window in that dimension.
|
||||
// ex: a window_pad_x of 60 will actually remove 120 from the drawn window. 60 from the
|
||||
// left, 60 from the right.
|
||||
float window_pad_x = video_res[0] * 0.175;
|
||||
float window_pad_y = video_res[1] * 0.166;
|
||||
float window_width_x = video_res[0] - window_pad_x*2;
|
||||
float window_height_y = video_res[1] - window_pad_y*2;
|
||||
|
||||
|
||||
// If the height is over 800, stay at full size. It only gets smaller as the height
|
||||
// decreases.
|
||||
if(video_res[1] >= 800){
|
||||
fontSizeMulti = 1 * 1.0; //REMOVE 1.8 later!!!;
|
||||
}else{
|
||||
//scale it to fit the screen size.
|
||||
//fontSizeMulti = 0.2 + 0.001 * video_res[1];
|
||||
//fontSizeMulti = 0.234 + 0.0007 * video_res[1];
|
||||
fontSizeMulti = 0.1429 + 0.001 * 1.0 * video_res[1];
|
||||
|
||||
|
||||
}//END OF screen height check
|
||||
|
||||
//
|
||||
if(FONT_ARIAL == -1 || Recorded_video_res != video_res){
|
||||
//Give it a font based on this screen size.
|
||||
|
||||
//Recorded_video_res = video_res;
|
||||
Recorded_video_res[0] = video_res[0];
|
||||
Recorded_video_res[1] = video_res[1];
|
||||
|
||||
//FONT_ARIAL = loadfont( "label", "arial", "32", -1 );
|
||||
float font_arial_size = (fontSizeMulti * 24);
|
||||
float font_arial_title_size = (fontSizeMulti * 32);
|
||||
string str_font_arial_size = ftos(font_arial_size);
|
||||
string str_font_arial_title_size = ftos(font_arial_title_size);
|
||||
|
||||
|
||||
FONT_ARIAL = loadfont( "game", "arial", str_font_arial_size, -1 );
|
||||
FONT_ARIAL_TITLE = loadfont( "game", "arial", str_font_arial_title_size, -1 );
|
||||
|
||||
//print( sprintf("CHANGE height:%i fontm:%.2f fontref:%i match:(%i, %i) totalmatch:%i\n", (int)video_res[1], fontSizeMulti, (int)FONT_ARIAL, (int)(Recorded_video_res[0]==video_res[0]), (int)(Recorded_video_res[1]==video_res[1]), (int)(Recorded_video_res==video_res)) );
|
||||
}
|
||||
|
||||
//little demo.
|
||||
/*
|
||||
if(nextPrintoutTime == -1 || (time >= nextPrintoutTime) ){
|
||||
nextPrintoutTime = time + 2;
|
||||
print( sprintf("timed printout. height:%i fontm:%.2f fontref:%i match:(%i, %i) totalmatch:%i\n", (int)video_res[1], fontSizeMulti, (int)FONT_ARIAL, (int)(Recorded_video_res[0]==video_res[0]), (int)(Recorded_video_res[1]==video_res[1]), (int)(Recorded_video_res==video_res)) );
|
||||
}
|
||||
*/
|
||||
|
||||
vVGUIColor = autocvar_vgui_color * ( 1 / 255 );
|
||||
|
||||
// Align the window to the center
|
||||
vVGUIWindowPos = video_mins;
|
||||
//vVGUIWindowPos[0] += ( video_res[0] / 2 ) - 320;
|
||||
//vVGUIWindowPos[1] += ( video_res[1] / 2 ) - 240;
|
||||
vVGUIWindowPos[0] += window_pad_x;
|
||||
vVGUIWindowPos[1] += window_pad_y;
|
||||
|
||||
|
||||
vVGUIWindowSiz[0] = window_width_x;
|
||||
vVGUIWindowSiz[1] = window_height_y;
|
||||
|
||||
// draw the window only if this screen says to.
|
||||
if(vguiMenus[ pSeatLocal->fVGUI_Display - 1 ].fDrawMainWindowAuto){
|
||||
VGUI_Window( vVGUIWindowPos, vVGUIWindowSiz, vguiMenus[ pSeatLocal->fVGUI_Display - 1 ].sTitle, [fontSizeMulti*32,fontSizeMulti*32] );
|
||||
}
|
||||
|
||||
// Display the contents of whatever we have selected
|
||||
vguiMenus[ pSeatLocal->fVGUI_Display - 1 ].vDraw( arg_player, vVGUIWindowPos, vVGUIWindowSiz, fontSizeMulti );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CSQC_VGUI_Init
|
||||
|
||||
Initialize all there is
|
||||
=================
|
||||
*/
|
||||
// NOTE! Not a built-in method, manually called by client init (ClientGame_Init).
|
||||
// ALSO - this means once for the entire client, so handle all pSeat choices
|
||||
// individually as it does.
|
||||
void
|
||||
CSQC_VGUI_Init(void)
|
||||
{
|
||||
string sTemp;
|
||||
int iMOTDLength;
|
||||
int i;
|
||||
int s;
|
||||
|
||||
filestream fmMapDescr;
|
||||
|
||||
// only the first screen choice will use its 'sTitle'
|
||||
vguiMenus[0].sTitle = sprintf("%s - %s", "Free Specialists", serverkey("hostname"));
|
||||
|
||||
// First load the MESSAGE OF THE DAY
|
||||
// TODO: Move this to the server and put strings into infokeys
|
||||
|
||||
//sMOTD_total = serverkey("motd_total");
|
||||
|
||||
sMOTD_total = "";
|
||||
|
||||
iMOTDLength = stof( serverkey( "motdlength" ) );
|
||||
for (i = 0; i < iMOTDLength; i++ ) {
|
||||
sMOTDString[ i ] = serverkey( sprintf( "motdline%i", i ) );
|
||||
|
||||
if ( sMOTDString[ i ] == "/" ) {
|
||||
sMOTD_total = strcat(sMOTD_total, "\n" );
|
||||
}else{
|
||||
sMOTD_total = strcat(sMOTD_total, sprintf("%s\n", sMOTDString[ i ]) );
|
||||
}
|
||||
}
|
||||
|
||||
// color it
|
||||
// NOPE! Let this be handled elsewhere in case of a different VGUI color!
|
||||
//sMOTD_total = strcat("^xFA0", sMOTD_total);
|
||||
|
||||
// Now load the MAP DESCRIPTION
|
||||
fmMapDescr = fopen( sprintf( "maps/%s.txt", mapname ), FILE_READ );
|
||||
if ( fmMapDescr != -1 ) {
|
||||
for (i = 0; i < 35; i++ ) {
|
||||
sTemp = fgets( fmMapDescr );
|
||||
if not ( sTemp ) {
|
||||
break;
|
||||
}
|
||||
sMapString[ i ] = sTemp;
|
||||
}
|
||||
fclose( fmMapDescr );
|
||||
}
|
||||
|
||||
gFun_UI_EventGrabber_Initialize();
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// FOR NOW, a lazy way of init for all seats.
|
||||
// Apply to all seats, not worrying about what numclientseats is because this is tiny.
|
||||
if (serverkeyfloat("slots") != 1) {
|
||||
// We start on the MOTD, always
|
||||
for (s = 0; s < g_seats.length; s++){
|
||||
pSeat = &g_seats[s];
|
||||
pSeatLocal = &g_seatslocal[s];
|
||||
VGUI_ChangeScreen(VGUI_SCREEN::MOTD);
|
||||
}
|
||||
}else{
|
||||
// make all pSeats start at the NONE screen instead
|
||||
for (s = 0; s < g_seats.length; s++){
|
||||
pSeat = &g_seats[s];
|
||||
pSeatLocal = &g_seatslocal[s];
|
||||
VGUI_ChangeScreen(VGUI_SCREEN::NONE);
|
||||
}
|
||||
}
|
||||
}
|
1
src/client/vgui_buysidemenu.h
Normal file
1
src/client/vgui_buysidemenu.h
Normal file
|
@ -0,0 +1 @@
|
|||
|
1598
src/client/vgui_buysidemenu.qc
Normal file
1598
src/client/vgui_buysidemenu.qc
Normal file
File diff suppressed because it is too large
Load diff
100
src/client/vgui_motd.qc
Normal file
100
src/client/vgui_motd.qc
Normal file
|
@ -0,0 +1,100 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 2016-2019 Marco 'eukara' Hladik. All rights reserved.
|
||||
*
|
||||
* See the file LICENSE attached with the sources for usage details.
|
||||
*
|
||||
****/
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_MessageOfTheDay
|
||||
|
||||
The MOTD screen.
|
||||
|
||||
TODO: Networking still needs to be done.
|
||||
You can't store motds in infokey strings because
|
||||
newline chars are not supported. You could hack it to use
|
||||
an array of infokeys, but that'll clutter things up
|
||||
====================
|
||||
*/
|
||||
|
||||
// Cloned from src/menu-fn/w_label.qc, HACKY
|
||||
string
|
||||
Colors_RGB8_to_HEX(vector color)
|
||||
{
|
||||
string out = "^x";
|
||||
//string out = "";
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
string a = "";
|
||||
float b = rint(color[i] * 15);
|
||||
|
||||
switch (b) {
|
||||
case 10:
|
||||
a = "A";
|
||||
break;
|
||||
case 11:
|
||||
a = "B";
|
||||
break;
|
||||
case 12:
|
||||
a = "C";
|
||||
break;
|
||||
case 13:
|
||||
a = "D";
|
||||
break;
|
||||
case 14:
|
||||
a = "E";
|
||||
break;
|
||||
case 15:
|
||||
a = "F";
|
||||
break;
|
||||
default:
|
||||
a = ftos(b);
|
||||
}
|
||||
out = sprintf("%s%s", out, a);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VGUI_MessageOfTheDay(player arg_player, vector vPos, vector vWindowSiz, float fFontSizeMulti ) {
|
||||
|
||||
static void MessageOfTheDay_ButtonOK( void ) {
|
||||
//pSeatLocal->fVGUI_Display = VGUI_SCREEN::BUYSIDEMENU;
|
||||
VGUI_ChangeScreen(VGUI_SCREEN::BUYSIDEMENU);
|
||||
}
|
||||
|
||||
//TAGGG - start from this location instead.
|
||||
//vector vTextPos = vPos + '16 116 0';
|
||||
vector vTextPos = vPos + '16 64 0';
|
||||
|
||||
// apply the VGUI color, since TextLineWrap (route to FTE method drawtextfield)
|
||||
// does not offer color, but in-text markup can offer this.
|
||||
// PENDING: if this is done a lot, offer it as a utility method?
|
||||
// That can take a color vector and apply this hex step for us.
|
||||
// Just beware that this applies to all text after the affected text as well,
|
||||
// markup only stops when overridden by other markup as read from left to right.
|
||||
// example:
|
||||
// ^x0F0 term1 term2
|
||||
// ...colors term1 and term2 green, even if this was not intentional for term2.
|
||||
// ^x0F0 term1 ^xF00 term2
|
||||
// ...colors term1 green, but term2 red.
|
||||
string tempText = sprintf("%s%s", Colors_RGB8_to_HEX(vVGUIColor), sMOTD_total);
|
||||
|
||||
// Oh, and no transparency it seems, so no slight show-through that the title text gets.
|
||||
// So you have to invent your own wordwrap to get that?? Let's just skip that.
|
||||
Gfx_TextLineWrap( vTextPos, vWindowSiz - ('16 64 0' * 2) , DRAWTEXTFIELD_ALIGN_LEFT_TOP, tempText, FONT_ARIAL );
|
||||
|
||||
if(TS_keyRefDown && TS_keyRefTapped == 13){
|
||||
//pressing enter here works too.
|
||||
MessageOfTheDay_ButtonOK();
|
||||
TS_keyRefTapped = 0; //why do we have to do this, no clue
|
||||
}
|
||||
|
||||
VGUI_Button( _("VGUI_OK"), MessageOfTheDay_ButtonOK, [vPos.x + 16, vPos.y + vWindowSiz.y - 30 - 16, 0], '100 30 0' );
|
||||
}
|
||||
|
||||
|
202
src/client/vguiobjects.qc
Normal file
202
src/client/vguiobjects.qc
Normal file
|
@ -0,0 +1,202 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 2016-2019 Marco 'eukara' Hladik. All rights reserved.
|
||||
*
|
||||
* See the file LICENSE attached with the sources for usage details.
|
||||
*
|
||||
****/
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_CheckMouse
|
||||
|
||||
Returns whether or not our mouse cursor hovers over a region
|
||||
====================
|
||||
*/
|
||||
float VGUI_CheckMouse( vector vPos, vector vReg ) {
|
||||
vector vSMins, vSMaxs;
|
||||
|
||||
vSMins = vPos;
|
||||
vSMaxs = vPos;
|
||||
vSMins[0] = vPos[0];
|
||||
vSMaxs[1] = vPos[1] - 1;
|
||||
|
||||
vSMaxs[0] = vPos[0] + vReg[0];
|
||||
vSMaxs[1] = vPos[1] + vReg[1];
|
||||
|
||||
if ( mouse_pos[0] >= vSMins[0] && mouse_pos[0] <= vSMaxs[0] ) {
|
||||
if (mouse_pos[1] >= vSMins[1] && mouse_pos[1] <= vSMaxs[1] ) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_Window
|
||||
|
||||
Draws window with outline, border and title
|
||||
====================
|
||||
*/
|
||||
|
||||
/*
|
||||
//TAGGG - NEW. This is the old parameters. Sends to VGUI_WINDOW to draw the string with a default font size
|
||||
// (the default font size was actually 16, it has been increased to 24)
|
||||
// Order in the new overload further below also puts things in a different order.
|
||||
inline void VGUI_Window( string sTitle, vector vPosition, vector vSize ) {
|
||||
VGUI_Window(vPosition, vSize, sTitle, [24, 24]);
|
||||
}
|
||||
*/
|
||||
|
||||
void VGUI_Window( vector vPosition, vector vSize, string sTitle, vector vFontSize ) {
|
||||
// Draw the background
|
||||
drawfill( vPosition, vSize, VGUI_WINDOW_BGCOLOR, VGUI_WINDOW_BGALPHA );
|
||||
|
||||
// Sides
|
||||
drawfill( vPosition, [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0], vPosition[1] + vSize[1] - 1], [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( vPosition, [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0] + vSize[0] - 1, vPosition[1]], [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
|
||||
// Draw the window title
|
||||
//TAGGG - little bigger.
|
||||
// Also use DRAWFLAG_NORMAL to stop the transparency from not being a solid color choice.
|
||||
// ADDITIVE interprets any darker color than as bright as possible as transparency.
|
||||
// The wordwrapped text does not support this, so let them both be solid.
|
||||
// No benefit to transparent text anyway.
|
||||
//title text font size depends on the height of the window.
|
||||
//Gfx_Text( vPosition + '16 16', sTitle, '12 12', vVGUIColor, VGUI_WINDOW_FGALPHA, DRAWFLAG_ADDITIVE, FONT_CON );
|
||||
Gfx_Text( vPosition + '16 16', sTitle, vFontSize, vVGUIColor, VGUI_WINDOW_FGALPHA, DRAWFLAG_NORMAL, FONT_ARIAL_TITLE );
|
||||
|
||||
//Gfx_TextLineWrap( vPosition + '16 16', vSize - ('16 64 0' * 2) , DRAWTEXTFIELD_ALIGN_LEFT_TOP, sTitle, FONT_ARIAL_STD );
|
||||
|
||||
|
||||
drawfill( vPosition + '0 48', [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_WindowSmall
|
||||
|
||||
Draws smaller window with outline, border and title
|
||||
====================
|
||||
*/
|
||||
void VGUI_WindowSmall( string sTitle, vector vPosition, vector vSize ) {
|
||||
// Draw the background
|
||||
drawfill( vPosition, vSize, VGUI_WINDOW_BGCOLOR, VGUI_WINDOW_BGALPHA );
|
||||
|
||||
// Sides
|
||||
drawfill( vPosition, [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0], vPosition[1] + vSize[1] - 1], [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( vPosition, [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0] + vSize[0] - 1, vPosition[1]], [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
|
||||
// Draw the window title
|
||||
Gfx_Text( vPosition + '8 8', sTitle, '12 12', vVGUIColor, VGUI_WINDOW_FGALPHA, DRAWFLAG_ADDITIVE, FONT_CON );
|
||||
drawfill( vPosition + '0 24', [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_Button
|
||||
|
||||
Draws a button, returns whether or not a mouse is hovering over it (for inheritance' sake)
|
||||
====================
|
||||
*/
|
||||
|
||||
//*** NOTE! Buysidemenu buttons don't use this! Only the MoTD close button, at least so far.
|
||||
float VGUI_Button( string sLabel, void() vFunction, vector vPosition, vector vSize) {
|
||||
vector vLabelPos;
|
||||
|
||||
/*
|
||||
if ( iVGUIKey < 57 ) {
|
||||
iVGUIKey++;
|
||||
}
|
||||
*/
|
||||
|
||||
drawfill( vPosition, [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0], vPosition[1] + vSize[1] - 1], [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( vPosition, [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0] + vSize[0] - 1, vPosition[1]], [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
|
||||
// Draw the button label
|
||||
vLabelPos[0] = vPosition[0] + 16;
|
||||
vLabelPos[1] = vPosition[1] + ( ( vSize[1] / 2 ) - 4 );
|
||||
|
||||
/*
|
||||
//TAGGG REPLACEMENT - "pSeatLocal->fInputKeyCode" for "TS_keyRefUp".
|
||||
if ( ( iVGUIKey == TS_keyRefUp ) ) {
|
||||
vFunction();
|
||||
TS_keyRefUp = 0;
|
||||
return TRUE;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
if ( VGUI_CheckMouse( vPosition, vSize ) ) {
|
||||
//pSeatLocal->fVGUI_Display
|
||||
|
||||
if ( TS_mouseClickRef == TRUE ) {
|
||||
vFunction();
|
||||
TS_mouseClickRef = FALSE;
|
||||
}
|
||||
|
||||
Gfx_Text( vLabelPos, sLabel, vButtonFontSize, vVGUIColor, VGUI_WINDOW_FGALPHA, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
drawfill( vLabelPos + '0 10 0', [ stringwidth( sLabel, TRUE, '12 12' ), 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
return TRUE;
|
||||
} else {
|
||||
Gfx_Text( vLabelPos, sLabel, vButtonFontSize, vVGUIColor * 0.8, VGUI_WINDOW_FGALPHA, DRAWFLAG_ADDITIVE, FONT_ARIAL_STD );
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_FakeButton
|
||||
|
||||
Looks like a button, doesn't function though. Meant for dead buttons
|
||||
====================
|
||||
*/
|
||||
void VGUI_FakeButton( string sLabel, vector vPosition, vector vSize ) {
|
||||
vector vLabelPos;
|
||||
|
||||
drawfill( vPosition, [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0], vPosition[1] + vSize[1] - 1], [vSize[0], 1], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( vPosition, [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
drawfill( [vPosition[0] + vSize[0] - 1, vPosition[1]], [1, vSize[1]], vVGUIColor, VGUI_WINDOW_FGALPHA );
|
||||
|
||||
// Draw the button label
|
||||
vLabelPos[0] = vPosition[0] + 16;
|
||||
vLabelPos[1] = vPosition[1] + ( ( vSize[1] / 2 ) - 4 );
|
||||
|
||||
Gfx_Text( vLabelPos, sLabel, '12 12', vVGUIColor * 0.5, VGUI_WINDOW_FGALPHA, DRAWFLAG_ADDITIVE, FONT_CON );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_Text
|
||||
|
||||
Wrapper for simple GUI text labels
|
||||
====================
|
||||
*/
|
||||
void VGUI_Text( string sText, vector vPos, vector vSize, float fFont ) {
|
||||
Gfx_Text( vPos, sText, vSize, vVGUIColor, VGUI_WINDOW_FGALPHA, DRAWFLAG_ADDITIVE, fFont );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
VGUI_RightText
|
||||
|
||||
Right-aligned version of above
|
||||
====================
|
||||
*/
|
||||
void VGUI_RightText( vector vPos, string sText, vector vSize, vector vColor, float fFont ) {
|
||||
vPos[0] -= stringwidth( sText, FALSE, vSize );
|
||||
Gfx_Text( vPos, sText, vSize, vColor, 1, 0, fFont );
|
||||
}
|
12
src/client/view.h
Normal file
12
src/client/view.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
void TS_SetViewModelFromStats(void);
|
||||
|
||||
void View_RoutineCheck(void);
|
||||
|
||||
void View_UpdateWeapon(entity vm, entity mflash);
|
||||
void resetViewModel(void);
|
||||
void View_HandleZoom(void);
|
||||
vector View_ShakeCalculate(vector vecInputAngles);
|
||||
void View_ShakeCreate(int iLevel);
|
||||
|
||||
void View_ShowMuzzleflash(int index);
|
380
src/client/view.qc
Normal file
380
src/client/view.qc
Normal file
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Also, Nuclide offers Weapons_SetGeomset for calling "setcustomskin" too, but one string
|
||||
// at a time. Don't really know if that way or the current way (cumulative string for
|
||||
// many commands delimited in one setcustomskin call) is any better, so leaving this as
|
||||
// it is for now.
|
||||
|
||||
|
||||
|
||||
// NEW
|
||||
void TS_SetViewModelFromStats(){
|
||||
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
entity vm = pSeat->m_eViewModel;
|
||||
entity mflash = pSeat->m_eMuzzleflash;
|
||||
weapondata_basic_t* basicP;
|
||||
weapondynamic_t dynaRef;
|
||||
|
||||
printfline("TS_SetViewModelFromStats: I happen? activeweap:%d", pl.activeweapon);
|
||||
|
||||
basicP = pl.getEquippedWeaponData();
|
||||
dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
|
||||
//TAGGG NOTE - we're not supporting skins apparently
|
||||
//if (autocvar_skins_dir != "") {
|
||||
// wm = sprintf("skins/%s/%s", autocvar_skins_dir, sViewModels[ aw - 1 ]);
|
||||
//} else {
|
||||
// wm = sprintf("models/%s", sViewModels[ aw - 1 ]);
|
||||
//}
|
||||
//TAGGG NOTE - we're not using that "sViewModels" string either.
|
||||
// Grab the currently equipped weapon, its weapon info, and from there pull the viewmodel to use
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_weaponData[dynaRef.weaponID];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
|
||||
|
||||
Weapons_SetModel((*basicP).sViewModelPath);
|
||||
|
||||
string cumulativeCommandString = "";
|
||||
|
||||
// If this weapon is a Gun (or ironsight-able), check for the presence of attachment-giving
|
||||
// buyopts.
|
||||
if(basicRef.typeID == WEAPONDATA_TYPEID_GUN || basicRef.typeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
weapondata_gun_t gunRef = *((weapondata_gun_t*)basicPointer);
|
||||
|
||||
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_SILENCER){
|
||||
// has the silencer? and an attachment?
|
||||
if(gunRef.silencer_part != -1){
|
||||
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.silencer_part );
|
||||
}
|
||||
}
|
||||
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_LASERSIGHT){
|
||||
// has the silencer? and an attachment?
|
||||
if(gunRef.lasersight_part != -1){
|
||||
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.lasersight_part );
|
||||
}
|
||||
}
|
||||
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_FLASHLIGHT){
|
||||
// has the silencer? and an attachment?
|
||||
if(gunRef.flashlight_part != -1){
|
||||
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.flashlight_part );
|
||||
}
|
||||
}
|
||||
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_SCOPE){
|
||||
// has the silencer? and an attachment?
|
||||
if(gunRef.scope_part != -1){
|
||||
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.scope_part );
|
||||
}
|
||||
}
|
||||
}//END OF gun type check (is a gun of some sort)
|
||||
|
||||
if(dynaRef.forceBodygroup1Submodel > 0){
|
||||
cumulativeCommandString = sprintf("%sgeomset 0 %i\n", cumulativeCommandString, dynaRef.forceBodygroup1Submodel );
|
||||
}
|
||||
// no need to do that geomset with the same value again.
|
||||
pl.prev_forceBodygroup1Submodel = dynaRef.forceBodygroup1Submodel;
|
||||
|
||||
setcustomskin(vm, "", cumulativeCommandString);
|
||||
|
||||
|
||||
// leftovers following a draw call, that likely lead here to begin with.
|
||||
SAVE_STATE(pl.w_attack_next);
|
||||
SAVE_STATE(pl.w_idle_next);
|
||||
SAVE_STATE(pl.viewzoom);
|
||||
SAVE_STATE(pl.weapontime);
|
||||
|
||||
skel_delete( mflash.skeletonindex );
|
||||
mflash.skeletonindex = skel_create( vm.modelindex );
|
||||
pSeat->m_iVMBones = skel_get_numbones( mflash.skeletonindex ) + 1;
|
||||
|
||||
// Don't let View_UpdateWeapon do this all over.
|
||||
pSeat->m_iLastWeapon = pl.activeweapon;
|
||||
|
||||
|
||||
}//TS_SetViewModelFromStats
|
||||
|
||||
|
||||
// On any frame, see if something about the current viewmodel needs to be changed
|
||||
void View_RoutineCheck(void){
|
||||
|
||||
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
entity vm = pSeat->m_eViewModel;
|
||||
weapondynamic_t dynaRef;
|
||||
|
||||
// That can happen, apparently.
|
||||
if(pl.inventoryEquippedIndex == -1){
|
||||
return;
|
||||
}
|
||||
|
||||
dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
|
||||
|
||||
if(pl.prev_forceBodygroup1Submodel != dynaRef.forceBodygroup1Submodel){
|
||||
|
||||
if(dynaRef.forceBodygroup1Submodel > 0){
|
||||
string commandString;
|
||||
commandString = sprintf("geomset 0 %i\n", dynaRef.forceBodygroup1Submodel );
|
||||
setcustomskin(vm, "", commandString);
|
||||
}
|
||||
pl.prev_forceBodygroup1Submodel = dynaRef.forceBodygroup1Submodel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//View_RoutineCheck
|
||||
|
||||
|
||||
|
||||
// vm is pSeat->m_eViewModel passed along from Nuclide's View_DrawViewModel
|
||||
void
|
||||
View_UpdateWeapon(entity vm, entity mflash)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
/* only bother upon change */
|
||||
if (pSeat->m_iLastWeapon == pl.activeweapon) {
|
||||
|
||||
View_RoutineCheck();
|
||||
return;
|
||||
}
|
||||
printfline("View_UpdateWeapon: change detected: %i -> %d", pSeat->m_iLastWeapon, pl.activeweapon);
|
||||
|
||||
pSeat->m_iOldWeapon = pSeat->m_iLastWeapon;
|
||||
pSeat->m_iLastWeapon = pl.activeweapon;
|
||||
|
||||
if (!pl.activeweapon /*|| pl.inventoryEquippedIndex < 0*/) {
|
||||
// can't work with this!
|
||||
resetViewModel();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//printfline("View_UpdateWeapon: change: %d vs %d", pSeat->m_iLastWeapon, pl.activeweapon);
|
||||
|
||||
// Call this to do a few other things for any weapon change
|
||||
TS_Weapon_Draw_extra();
|
||||
|
||||
/* hack, we changed the wep, move this into Game_Input/PMove */
|
||||
Weapons_Draw();
|
||||
|
||||
//No longer a var! Oops
|
||||
//pSeat->m_iVMEjectBone = pSeat->m_iVMBones + 1;
|
||||
|
||||
/* we forced a weapon call outside the prediction,
|
||||
* thus we need to update all the net variables to
|
||||
* make sure these updates are recognized. this is
|
||||
* vile but it'll have to do for now */
|
||||
|
||||
SAVE_STATE(pl.w_attack_next);
|
||||
SAVE_STATE(pl.w_idle_next);
|
||||
SAVE_STATE(pl.viewzoom);
|
||||
SAVE_STATE(pl.weapontime);
|
||||
//TAGGG - NEW VAR.
|
||||
SAVE_STATE(pl.w_attack_akimbo_next);
|
||||
|
||||
skel_delete( mflash.skeletonindex );
|
||||
mflash.skeletonindex = skel_create( vm.modelindex );
|
||||
pSeat->m_iVMBones = skel_get_numbones( mflash.skeletonindex ) + 1;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//TAGGG - NEW METHODS
|
||||
void
|
||||
resetViewModel(void)
|
||||
{
|
||||
|
||||
//TAGGG - CRITICAL. NOT ANYMORE!!!
|
||||
//return;
|
||||
|
||||
|
||||
printfline("resetViewModel called.");
|
||||
|
||||
if(pSeat->m_ePlayer == NULL){
|
||||
printfline("resetViewModel: early end #1, client ent NULL");
|
||||
return;
|
||||
}
|
||||
if(pSeat->m_ePlayer.classname != "player"){
|
||||
printfline("resetViewModel: early end #2, client ent not player");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//was m_eViewModel and m_eMuzzleflash
|
||||
entity vm = pSeat->m_eViewModel;
|
||||
entity mflash = pSeat->m_eMuzzleflash;
|
||||
|
||||
setmodel( vm, "" );
|
||||
skel_delete( mflash.skeletonindex );
|
||||
mflash.skeletonindex = 0; //er wat. would -1 be better or not?
|
||||
pSeat->m_iVMBones = 0 + 1;
|
||||
//pSeat->m_iVMEjectBone = pSeat->m_iVMBones + 1;
|
||||
|
||||
/*
|
||||
player pl = (player) pSeat->m_ePlayer;
|
||||
pl.setInventoryEquippedIndex(-1); //do we have to?
|
||||
*/
|
||||
|
||||
}//END OF resetViewModel
|
||||
|
||||
|
||||
|
||||
//TAGGG - loaned from The Wastes.
|
||||
void View_HandleZoom(void){
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
|
||||
if(pl == NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
if(getplayerkeyvalue(player_localnum, "*spec") != "0"){
|
||||
|
||||
//TAGGG - CRITICAL.
|
||||
// WAIT! This is a spectator, so...? can we even trust pSeat->ePlayer to be a player?
|
||||
// Confusing.
|
||||
// That leads me to believe that either zoom-related vars should be
|
||||
// pSeat-><clienttdata> - specific, or not handled here at all.
|
||||
|
||||
// If our FOV was anything but 1, force it so.
|
||||
// Spectator mode instantly forgets any scope-related FOV mods.
|
||||
// Nuclide might not be calling setsensitivityscaler, so doing it here.
|
||||
// No problem if the setproperty is a little redundant as that might be happening.
|
||||
if(pl.flZoomLevel < 1.0 || pl.viewzoom < 1.0){
|
||||
pl.flZoomLerp = 0;
|
||||
pl.flZoomLevel = 1;
|
||||
pl.viewzoom = 1;
|
||||
setproperty(VF_AFOV, cvar("fov") * pl.flZoomLevel);
|
||||
setsensitivityscaler(pl.flZoomLevel);
|
||||
}
|
||||
// No further zoom logic
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//TAGGG - any references to STAT_VIEWZOOM are now garbage.
|
||||
// Rely on pl.flTargetZoom instead, it's sent to the client every frame
|
||||
// and should be handled serverside anyway.
|
||||
// flCurrentZoom is actually the "target" zoom we want to reach.
|
||||
// flOldZoom is the zoom we started at, at the time of the change.
|
||||
// flZoomLerp is how far along we are from oldZoom to currentZoom.
|
||||
// flZoomLevel is the actual zoom we are at this very moment.
|
||||
|
||||
if (pl.flCurrentZoom != pl.flTargetZoom ) {
|
||||
pl.flOldZoom = pl.flCurrentZoom;
|
||||
pl.flCurrentZoom = pl.flTargetZoom ;
|
||||
pl.flZoomLerp = 0.0f;
|
||||
}
|
||||
|
||||
if (pl.flZoomLerp < 1.0f) {
|
||||
// Slow this down for debugging purposes, this is meant to be fast.
|
||||
// 0.8 can be safe.
|
||||
// The Wastes default was 4.
|
||||
|
||||
pl.flZoomLerp += clframetime * 8;
|
||||
|
||||
if(pl.flZoomLerp >= 1.0){
|
||||
//enforce the cap.
|
||||
pl.flZoomLerp = 1.0;
|
||||
}
|
||||
|
||||
//pl.flZoomLevel = getstatf(STAT_VIEWZOOM);
|
||||
pl.flZoomLevel = Math_Lerp(pl.flOldZoom, pl.flCurrentZoom, pl.flZoomLerp);
|
||||
}
|
||||
|
||||
// Set this, since Nuclide will read it in and apply instantly.
|
||||
// So same effect without having to edit Nuclide, this pipes it over for it
|
||||
// to do the setproperty thing below to apply
|
||||
pl.viewzoom = pl.flZoomLevel;
|
||||
|
||||
////setproperty(VF_AFOV, DEFAULT_FOV * pl.flZoomLevel);
|
||||
////setsensitivityscaler(pl.flZoomLevel);
|
||||
|
||||
// here's the way old FreeCS did it
|
||||
//setproperty(VF_AFOV, cvar("fov") * pl.viewzoom);
|
||||
//setsensitivityscaler(pl.viewzoom);
|
||||
}
|
||||
|
||||
|
||||
vector
|
||||
View_ShakeCalculate(vector vecInputAngles)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
if(pl == NULL)return vecInputAngles; //forget this
|
||||
|
||||
// you might not want to force this every frame unless you like vomiting.
|
||||
//pl.flViewShake = 2;
|
||||
|
||||
if (pl.flViewShake > 0.0f) {
|
||||
vecInputAngles[0] += (random() * pl.flViewShake) * 3;
|
||||
vecInputAngles[1] += (random() * pl.flViewShake) * 3;
|
||||
vecInputAngles[2] += (random() * pl.flViewShake) * 3;
|
||||
pl.flViewShake -= clframetime;
|
||||
}
|
||||
|
||||
return vecInputAngles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
View_ShakeCreate(int iLevel)
|
||||
{
|
||||
player pl = (player)pSeat->m_ePlayer;
|
||||
pl.flViewShake = iLevel / 128;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TAGGG - NEW. Similar to Nuclide's provided "View_SetMuzzleflash", but also acts
|
||||
// as though the first frame were an event by doing the same lines as a 5000-ish
|
||||
// event. This is because TS weapons don't have events for the muzzle flash
|
||||
// unlike HL ones, must be hardcoded to show up.
|
||||
// Figuring out what muzzle flash for what weapon will come another time.
|
||||
// For now using the same HL set, but TS comes with some muzzle-flash looking sprites.
|
||||
// ALSO - for now, always assuming attachment #0.
|
||||
// For the model event in case that changes, see Nuclide's src/client/modelevent.qc,
|
||||
// Event_ProcessModel.
|
||||
void
|
||||
View_ShowMuzzleflash(int index)
|
||||
{
|
||||
// View_SetMuzzleflash
|
||||
pSeat->m_eMuzzleflash.modelindex = (float)index;
|
||||
|
||||
// Event_ProcessModel: force it.
|
||||
pSeat->m_eMuzzleflash.alpha = 1.0f;
|
||||
pSeat->m_eMuzzleflash.scale = 0.25;
|
||||
// attachment #0 is m_iVMBones + 0. Add for #1 to #3, I think.
|
||||
// No idea if any weapons play with attachments, #0 should always
|
||||
// be the end of the weapon for flashes
|
||||
pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
2
src/progs.src
Normal file
2
src/progs.src
Normal file
|
@ -0,0 +1,2 @@
|
|||
#pragma sourcefile client/progs.src
|
||||
#pragma sourcefile server/progs.src
|
4
src/server/Makefile
Normal file
4
src/server/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
CC=fteqcc
|
||||
|
||||
all:
|
||||
$(CC) progs.src
|
85
src/server/client.qc
Normal file
85
src/server/client.qc
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* called every input frame */
|
||||
void
|
||||
Game_RunClientCommand(void)
|
||||
{
|
||||
Footsteps_Update();
|
||||
//TAGGG
|
||||
// FreeHL does it this way instead now (further down)?
|
||||
// I don't think this is even significant.
|
||||
// Then again clientside does its call this way too?
|
||||
// In Predict_PlayerPreFrame, which is Nuclide.
|
||||
// Yet this is within the gamemod? Odd.
|
||||
//PMove_Run();
|
||||
|
||||
player pl = (player)self;
|
||||
pl.Physics_Run();
|
||||
}
|
||||
|
||||
/* custom chat packet */
|
||||
void
|
||||
SV_SendChat(entity sender, string msg, entity eEnt, float fType)
|
||||
{
|
||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||
WriteByte(MSG_MULTICAST, fType == 0 ? EV_CHAT:EV_CHAT_TEAM);
|
||||
WriteByte(MSG_MULTICAST, num_for_edict(sender) - 1);
|
||||
WriteByte(MSG_MULTICAST, sender.team);
|
||||
WriteString(MSG_MULTICAST, msg);
|
||||
|
||||
if (eEnt) {
|
||||
msg_entity = eEnt;
|
||||
multicast([0,0,0], MULTICAST_ONE);
|
||||
} else {
|
||||
multicast([0,0,0], MULTICAST_ALL);
|
||||
}
|
||||
|
||||
localcmd(sprintf("echo [SERVER] %s: %s\n", sender.netname, msg));
|
||||
}
|
||||
|
||||
/* client cmd overrides happen here */
|
||||
void
|
||||
Game_ParseClientCommand(string cmd)
|
||||
{
|
||||
tokenize(cmd);
|
||||
|
||||
if (argv(1) == "timeleft") {
|
||||
string msg;
|
||||
string timestring;
|
||||
float timeleft;
|
||||
timeleft = cvar("mp_timelimit") - (time / 60);
|
||||
timestring = Vox_TimeToString(timeleft);
|
||||
msg = sprintf("we have %s minutes remaining", timestring);
|
||||
Vox_Singlecast(self, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv(0) == "say") {
|
||||
SV_SendChat(self, argv(1), world, 0);
|
||||
return;
|
||||
} else if (argv(0) == "say_team") {
|
||||
entity a;
|
||||
for (a = world; (a = find(a, ::classname, "player"));) {
|
||||
if (a.team == self.team) {
|
||||
SV_SendChat(self, argv(1), a, 1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
clientcommand(self, cmd);
|
||||
}
|
168
src/server/damage.qc
Normal file
168
src/server/damage.qc
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
//TAGGG - NOTE! Identical to base/src/server/damage.qc.
|
||||
// Deviate from it if needed, if not by the end, including that and wiping
|
||||
// this file will do.
|
||||
|
||||
|
||||
/* generic function that applies damage, pain and suffering */
|
||||
void
|
||||
Damage_Apply(entity t, entity c, float dmg, int w, damageType_t type)
|
||||
{
|
||||
base_player tp = (base_player)t;
|
||||
|
||||
CGameRules rules = (CGameRules)g_grMode;
|
||||
|
||||
/* player god mode */
|
||||
if (t.flags & FL_CLIENT && t.flags & FL_GODMODE)
|
||||
return;
|
||||
|
||||
/* already dead, please avoid recursion */
|
||||
if (t.health <= 0)
|
||||
return;
|
||||
|
||||
/* only clients have armor */
|
||||
if (t.flags & FL_CLIENT) {
|
||||
/* skip armor */
|
||||
if not (type & DMG_SKIP_ARMOR)
|
||||
if (tp.armor && dmg > 0) {
|
||||
float flArmor;
|
||||
float flNewDamage;
|
||||
|
||||
flNewDamage = dmg * 0.2;
|
||||
flArmor = (dmg - flNewDamage) * 0.5;
|
||||
|
||||
if (flArmor > tp.armor) {
|
||||
flArmor = tp.armor;
|
||||
flArmor *= (1/0.5);
|
||||
flNewDamage = dmg - flArmor;
|
||||
tp.armor = 0;
|
||||
} else {
|
||||
tp.armor -= flArmor;
|
||||
}
|
||||
dmg = flNewDamage;
|
||||
}
|
||||
}
|
||||
|
||||
dmg = rint(dmg);
|
||||
t.health -= dmg;
|
||||
|
||||
/* the globals... */
|
||||
g_dmg_eAttacker = c;
|
||||
g_dmg_eTarget = t;
|
||||
g_dmg_iDamage = dmg;
|
||||
g_dmg_iHitBody = trace_surface_id;
|
||||
g_dmg_iFlags = type;
|
||||
g_dmg_iWeapon = w;
|
||||
|
||||
if (dmg > 0) {
|
||||
t.dmg_take = dmg;
|
||||
t.dmg_inflictor = c;
|
||||
} else if (t.max_health && t.health > t.max_health) {
|
||||
t.health = t.max_health;
|
||||
}
|
||||
|
||||
CBaseEntity s = (CBaseEntity)t;
|
||||
|
||||
if (s.health <= 0) {
|
||||
if (s.flags & FL_CLIENT) {
|
||||
rules.PlayerDeath((player)s);
|
||||
} else {
|
||||
s.Death();
|
||||
}
|
||||
} else {
|
||||
if (s.flags & FL_CLIENT) {
|
||||
rules.PlayerPain((player)s);
|
||||
} else {
|
||||
s.Pain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* physical check of whether or not we can trace important parts of an ent */
|
||||
float
|
||||
Damage_CheckTrace(entity t, vector vecHitPos)
|
||||
{
|
||||
/* We're lazy. Who cares */
|
||||
if (t.solid == SOLID_BSP) {
|
||||
return (1);
|
||||
}
|
||||
|
||||
traceline(vecHitPos, t.origin, 1, self);
|
||||
if (trace_fraction == 1) {
|
||||
return (1);
|
||||
}
|
||||
traceline(vecHitPos, t.origin + [15,15,0], 1, self);
|
||||
if (trace_fraction == 1) {
|
||||
return (1);
|
||||
}
|
||||
traceline(vecHitPos, t.origin + [-15,-15,0], 1, self);
|
||||
if (trace_fraction == 1) {
|
||||
return (1);
|
||||
}
|
||||
traceline(vecHitPos, t.origin + [-15,15,0], 1, self);
|
||||
if (trace_fraction == 1) {
|
||||
return (1);
|
||||
}
|
||||
traceline(vecHitPos, t.origin + [15,-15,0], 1, self);
|
||||
if (trace_fraction == 1) {
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* even more pain and suffering, mostly used for explosives */
|
||||
void
|
||||
Damage_Radius(vector org, entity attacker, float dmg, float r, int check, int w)
|
||||
{
|
||||
float new_dmg;
|
||||
float dist;
|
||||
float diff;
|
||||
vector pos;
|
||||
|
||||
for (entity e = world; (e = findfloat(e, ::takedamage, DAMAGE_YES));) {
|
||||
pos[0] = e.absmin[0] + (0.5 * (e.absmax[0] - e.absmin[0]));
|
||||
pos[1] = e.absmin[1] + (0.5 * (e.absmax[1] - e.absmin[1]));
|
||||
pos[2] = e.absmin[2] + (0.5 * (e.absmax[2] - e.absmin[2]));
|
||||
|
||||
/* don't bother if it's not anywhere near us */
|
||||
dist = vlen(org - pos);
|
||||
if (dist > r)
|
||||
continue;
|
||||
|
||||
/* can we physically hit this thing? */
|
||||
if (check == TRUE)
|
||||
if (Damage_CheckTrace(e, org) == FALSE)
|
||||
continue;
|
||||
|
||||
/* calculate new damage values */
|
||||
diff = (r - dist) / r;
|
||||
new_dmg = rint(dmg * diff);
|
||||
|
||||
if (diff > 0) {
|
||||
Damage_Apply(e, attacker, new_dmg, w, DMG_EXPLODE);
|
||||
|
||||
/* approximate, feel free to tweak */
|
||||
if (e.movetype == MOVETYPE_WALK) {
|
||||
makevectors(vectoangles(e.origin - org));
|
||||
e.velocity += v_forward * (new_dmg * 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
src/server/defs.h
Normal file
79
src/server/defs.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
#include "player.h"
|
||||
|
||||
|
||||
|
||||
//TAGGG - TODO: some were intended for FreeCS like a c4 timer one, but some
|
||||
// can probably be used by FreeTS anyway
|
||||
|
||||
// Server cvars
|
||||
var int autocvar_mp_winlimit = 0;
|
||||
var int autocvar_mp_halftime = 0;
|
||||
var int autocvar_mp_startmoney = 30000; //was 800
|
||||
//var float autocvar_mp_buytime = 90;
|
||||
var float autocvar_mp_freezetime = 6;
|
||||
var float autocvar_mp_roundtime = 5;
|
||||
var float autocvar_mp_timelimit = 60;
|
||||
var string autocvar_motdfile = "motd.txt";
|
||||
var int autocvar_mp_friendlyfire = FALSE;
|
||||
|
||||
//TAGGG - are we even using all these?
|
||||
var int autocvar_fcs_swapteams = FALSE; /* Swaps spawnpoints */
|
||||
var int autocvar_fcs_maxmoney = 99999; //TAGG - was 16000.
|
||||
|
||||
//TAGGG - not used, don't know if the TS really supported denying weapon drop-ables.
|
||||
// Doesn't mean that can't be implemented anyway (check this CVar, and if on while about
|
||||
// to spawn a drop-able, deny the request and don't drop at all on player death)
|
||||
//var int autocvar_fcs_nopickups = FALSE; /* Disable weapon items */
|
||||
|
||||
|
||||
// Game specific fields
|
||||
|
||||
var int g_ts_gamestate;
|
||||
var float g_ts_gametime;
|
||||
|
||||
// no, just have counts of players in general. Team-based stuff will check for that later.
|
||||
//var int g_cs_alive_t;
|
||||
//var int g_cs_alive_ct;
|
||||
var int g_ts_player_alive_specialists;
|
||||
var int g_ts_player_alive_mercenaries;
|
||||
var int g_ts_player_alive_total;
|
||||
var int g_ts_player_spectator; //do we even need this one?
|
||||
var int g_ts_player_all;
|
||||
|
||||
|
||||
void initSpawnMem(void);
|
||||
|
||||
//TAGGG - restored?
|
||||
void Game_Spawn_ObserverCam(player pl);
|
||||
|
||||
|
||||
// Do these all still exist and need to be prototyped here??
|
||||
/*
|
||||
void Game_SpectatorThink(void);
|
||||
void Game_SpectatorConnect(void);
|
||||
void Game_SpectatorDisconnect(void);
|
||||
void Game_RunClientCommand(void);
|
||||
void Game_ParseClientCommand(string cmd);
|
||||
void Game_InitRules(void);
|
||||
void Game_Worldspawn(void);
|
||||
*/
|
||||
|
||||
|
2
src/server/entity/TSAmmoPack.h
Normal file
2
src/server/entity/TSAmmoPack.h
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
class TSAmmoPack;
|
237
src/server/entity/TSAmmoPack.qc
Normal file
237
src/server/entity/TSAmmoPack.qc
Normal file
|
@ -0,0 +1,237 @@
|
|||
|
||||
class TSAmmoPack{
|
||||
|
||||
// After being recently accessed by a player but not deleted,
|
||||
// I enforce a cooldown time before any other player (including the same one)
|
||||
// can try to pick me up again. This stops spamming inventory scans every
|
||||
// single frame if a player stands over a weapon they can't pick up.
|
||||
float accessibleCooldownTime;
|
||||
// To avoid server clutter over time, a pickup will delete itself
|
||||
float expireTime;
|
||||
|
||||
//myInfo
|
||||
int ary_ammoTotal[AMMO_ID::LAST_ID];
|
||||
|
||||
|
||||
void(void) TSAmmoPack;
|
||||
virtual void() Think;
|
||||
virtual void() Touch;
|
||||
virtual void() PlayerUse;
|
||||
static TSAmmoPack(player arg_player) generate;
|
||||
virtual void(player arg_player) copyFrom;
|
||||
virtual BOOLEAN(player arg_player) copyTo;
|
||||
|
||||
|
||||
//static Container_STR_ptr *Event::commandList;
|
||||
|
||||
|
||||
/*
|
||||
// in-class constructor.
|
||||
void(void) TSAmmoPack = {
|
||||
// can do stuff here too.
|
||||
};
|
||||
*/
|
||||
/*
|
||||
// in-class method.
|
||||
virtual int (int arg1, int arg2) doMath = {
|
||||
return arg1 + arg2;
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static var int TSAmmoPack::testStaticVar = 0;
|
||||
|
||||
|
||||
|
||||
void TSAmmoPack::TSAmmoPack(void){
|
||||
|
||||
|
||||
accessibleCooldownTime = -1;
|
||||
|
||||
for(int i = 0; i < AMMO_ID::LAST_ID; i++){
|
||||
ary_ammoTotal[i] = 0; //safe default
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Static method called from elsewhere to create and initialize a
|
||||
// weapon drop entity, given a player and what weapon to copy information from.
|
||||
TSAmmoPack TSAmmoPack::generate(player arg_player){
|
||||
TSAmmoPack eDrop = spawn(TSAmmoPack);
|
||||
|
||||
|
||||
TSAmmoPack::testStaticVar++;
|
||||
//printfline("AmmoPacks ever dropped: %i", testStaticVar);
|
||||
|
||||
//eDrop.setup(arg_player, arg_weaponID);
|
||||
|
||||
//TSAmmoPack eDrop = spawn(TSAmmoPack);
|
||||
setorigin( eDrop, arg_player.origin + arg_player.view_ofs );
|
||||
setmodel( eDrop, "models/ammopack.mdl");
|
||||
|
||||
eDrop.classname = "remove_me";
|
||||
eDrop.owner = arg_player;
|
||||
eDrop.movetype = MOVETYPE_TOSS;
|
||||
eDrop.solid = SOLID_TRIGGER; //or CORPSE
|
||||
//eDrop.weapon = dynaRefPRE.weaponID;
|
||||
eDrop.think = TSAmmoPack::Think;
|
||||
eDrop.touch = TSAmmoPack::Touch;
|
||||
eDrop.nextthink = time + 0.5f;
|
||||
|
||||
eDrop.copyFrom(arg_player);
|
||||
|
||||
//uhh.. ok, sure
|
||||
eDrop.health = 1;
|
||||
|
||||
setsize( eDrop, '-16 -16 0', '16 16 16' );
|
||||
|
||||
//is this v_forward sometimes bad.. ?
|
||||
makevectors( arg_player.v_angle );
|
||||
|
||||
eDrop.velocity = arg_player.velocity + v_forward * 256;
|
||||
|
||||
//And lastly, assume I need to expire at some point.
|
||||
eDrop.expireTime = time + autocvar_weaponstay; //autocvar_weaponstay
|
||||
|
||||
eDrop.PlayerUse = TSAmmoPack::PlayerUse;
|
||||
|
||||
return eDrop;
|
||||
}//END OF generate
|
||||
|
||||
|
||||
|
||||
// Pack all the player's ammo pools into me.
|
||||
void TSAmmoPack::copyFrom(player arg_player){
|
||||
|
||||
for(int i = 0; i < AMMO_ID::LAST_ID; i++){
|
||||
this.ary_ammoTotal[i] = arg_player.ary_ammoTotal[i]; //take it all.
|
||||
arg_player.ary_ammoTotal[i] = 0;
|
||||
}
|
||||
|
||||
}//END OF copyFrom
|
||||
|
||||
// Give this player my ammo pools, whatever will fit.
|
||||
// Returns whether any ammo transaction happened (pretty likely)
|
||||
BOOLEAN TSAmmoPack::copyTo(player arg_player){
|
||||
|
||||
|
||||
ammodata_t ammoRef;
|
||||
BOOLEAN anyAmmoTaken = FALSE;
|
||||
|
||||
for(int i = 0; i < AMMO_ID::LAST_ID; i++){
|
||||
|
||||
ammoRef = *ary_ammoData[i];
|
||||
//How much can I give to the player?
|
||||
|
||||
|
||||
//this.ary_ammoTotal[i] = arg_player.ary_ammoTotal[i]; //take it all.
|
||||
//arg_player.ary_ammoTotal[i] = 0;
|
||||
|
||||
|
||||
|
||||
int amountToTake;
|
||||
int existingCount = arg_player.ary_ammoTotal[i];
|
||||
int pickupCount = this.ary_ammoTotal[i];
|
||||
|
||||
if(existingCount + pickupCount <= ammoRef.iMax){
|
||||
//absorb it all.
|
||||
amountToTake = pickupCount;
|
||||
}else{
|
||||
//too much in the pickup? this is ok, take as much as we can.
|
||||
amountToTake = ammoRef.iMax - existingCount;
|
||||
}
|
||||
|
||||
if(amountToTake > 0){
|
||||
anyAmmoTaken = TRUE;
|
||||
}else{
|
||||
|
||||
}
|
||||
|
||||
arg_player.ary_ammoTotal[i] += amountToTake;
|
||||
//arg_pickupRef.myInfo.iCount -= amountToTake;
|
||||
|
||||
this.ary_ammoTotal[i]-= amountToTake;
|
||||
|
||||
}//END OF for loop through ammo types
|
||||
|
||||
|
||||
if(anyAmmoTaken){
|
||||
sound(arg_player, CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM, 100, SOUNDFLAG_CUSTOMCLIENT );
|
||||
}else{
|
||||
//sound( arg_player, CHAN_ITEM, "common/wpn_select.wav", 1, ATTN_NORM, 100, SOUNDFLAG_CUSTOMCLIENT );
|
||||
}
|
||||
|
||||
return anyAmmoTaken;
|
||||
}//END OF copyTo
|
||||
|
||||
|
||||
|
||||
void TSAmmoPack::Touch( void ) {
|
||||
if ( other.classname != "player" ) {
|
||||
// assume it's the world.. play this sound?
|
||||
|
||||
//printfline("the thing I touched is %s", other.classname);
|
||||
// TODO - any others we need to keep track of for making the drop?
|
||||
if(other.classname == "worldspawn" || other.classname == "func_breakable"){
|
||||
sound( this, CHAN_ITEM, "items/weapondrop1.wav", 1, ATTN_NORM, 100, SOUNDFLAG_CUSTOMCLIENT );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//entity eOld = self;
|
||||
//TSAmmoPack selfRef = (TSAmmoPack)self;
|
||||
//self = other;
|
||||
|
||||
player otherPlayerRef = (player)other; //safe assumption now.
|
||||
|
||||
BOOLEAN shouldDelete;
|
||||
if(time >= this.accessibleCooldownTime){
|
||||
shouldDelete = copyTo(otherPlayerRef);
|
||||
this.accessibleCooldownTime = time + 0.8;
|
||||
}else{
|
||||
shouldDelete = FALSE;
|
||||
}
|
||||
|
||||
//self = eOld;
|
||||
if(shouldDelete){
|
||||
remove(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}//END OF touch method
|
||||
|
||||
|
||||
|
||||
void TSAmmoPack::PlayerUse( void ) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TSAmmoPack::Think( void ) {
|
||||
//TSAmmoPack selfRef = (TSAmmoPack)self;
|
||||
// oh.. this makes the weapon collidable with the original dropper
|
||||
// again.
|
||||
|
||||
if(time >= this.expireTime){
|
||||
//expire.
|
||||
remove(this);
|
||||
}else{
|
||||
this.owner = world;
|
||||
//set the next think to aim around this time then.
|
||||
this.nextthink = this.expireTime + 0.1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
36
src/server/entity/TSThrownProjectile.h
Normal file
36
src/server/entity/TSThrownProjectile.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
// NOTE - this was not a type in the original TS, just seemed convenient to make.
|
||||
|
||||
//class TSThrownProjectile;
|
||||
|
||||
|
||||
class TSThrownProjectile : TSWorldGun{
|
||||
|
||||
void(void) TSThrownProjectile;
|
||||
virtual void() Think;
|
||||
virtual void() Touch;
|
||||
virtual void() PlayerUse;
|
||||
static TSThrownProjectile (player arg_player, int arg_weaponID) generate;
|
||||
static TSThrownProjectile (player arg_player, weapondynamic_t dynaRefPRE) generate2;
|
||||
|
||||
virtual void (player arg_player, int arg_weaponID) copyFrom;
|
||||
virtual void (player arg_player, weapondynamic_t tempRef) copyFrom2;
|
||||
|
||||
virtual void (player arg_player, int arg_weaponID) copyTo;
|
||||
|
||||
virtual void (BOOL becomeStuck, optional vector dir, optional entity stuckTo, BOOL touchedBreakableGlass)becomePickupable;
|
||||
|
||||
// Like FTE's supplied 'owner' field, but for keeping track of who threw this knife after
|
||||
// the 'owner' field gets reset to still keep track of who should be blamed for damage inflicted.
|
||||
entity m_owner;
|
||||
|
||||
float startAngularVelocityDelay;
|
||||
//vector vPreviousVelocity;
|
||||
|
||||
float forceBecomePickupableTime;
|
||||
|
||||
vector queuedVelocity;
|
||||
float queuedVelocityApplyTime;
|
||||
float switchToNormalPickupBoundsTime;
|
||||
};
|
||||
|
517
src/server/entity/TSThrownProjectile.qc
Normal file
517
src/server/entity/TSThrownProjectile.qc
Normal file
|
@ -0,0 +1,517 @@
|
|||
|
||||
// NOTE - this was not a type in the original TS, just seemed convenient to make.
|
||||
|
||||
// This is meant to be spawned by a player throwing a knife.
|
||||
// It starts with its own behavior (hurt a non-owner I touch, or stick to / bounce-off whatever
|
||||
// isn't a player), and switches over to pickup behavior after it stops being dangerous.
|
||||
|
||||
|
||||
|
||||
|
||||
void TSThrownProjectile::TSThrownProjectile(void){
|
||||
TSWorldGun::TSWorldGun();
|
||||
|
||||
startAngularVelocityDelay = -1;
|
||||
//vPreviousVelocity = vecZero;
|
||||
forceBecomePickupableTime = -1;
|
||||
queuedVelocity = vecZero;
|
||||
queuedVelocityApplyTime = -1;
|
||||
switchToNormalPickupBoundsTime = -1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
TSThrownProjectile TSThrownProjectile::generate(player arg_player, int arg_weaponID){
|
||||
weapondynamic_t dynaRefPRE = arg_player.ary_myWeapons[arg_weaponID];
|
||||
return TSThrownProjectile::generate2(arg_player, dynaRefPRE);
|
||||
}
|
||||
|
||||
// Static method called from elsewhere to create and initialize a
|
||||
// weapon drop entity, given a player and what weapon to copy information from.
|
||||
TSThrownProjectile TSThrownProjectile::generate2(player arg_player, weapondynamic_t dynaRefPRE){
|
||||
TSThrownProjectile eDrop = spawn(TSThrownProjectile);
|
||||
|
||||
//eDrop.setup(arg_player, arg_weaponID);
|
||||
|
||||
//weapondynamic_t dynaRefPRE = arg_player.ary_myWeapons[arg_weaponID];
|
||||
weapondata_basic_t* basicPointerPRE = (weapondata_basic_t*) ary_weaponData[dynaRefPRE.weaponID];
|
||||
weapondata_basic_t basicRefPRE = *(basicPointerPRE);
|
||||
weapondata_throwable_t throwableRef = *((weapondata_throwable_t*)basicPointerPRE);
|
||||
|
||||
|
||||
makevectors( arg_player.v_angle );
|
||||
|
||||
eDrop.movetype = MOVETYPE_BOUNCE;
|
||||
eDrop.solid = SOLID_TRIGGER; //or CORPSE
|
||||
|
||||
setorigin( eDrop, arg_player.origin + arg_player.view_ofs + v_forward * 16 );
|
||||
setmodel( eDrop, basicRefPRE.sWorldModelPath );
|
||||
|
||||
|
||||
//printfline("TSThrownProjectile::generate2 v_up: %.2f %.2f %.2f", v_up.x, v_up.y, v_up.z);
|
||||
vector old_v_up = v_up;
|
||||
|
||||
//eDrop.velocity = arg_player.velocity + v_forward * 256;
|
||||
// relying on the 'makevectors' call from earlier!
|
||||
vector vDir = aim ( arg_player, 100000 );
|
||||
//eDrop.velocity = ( arg_player.velocity + vDir * throwableRef.fThrowSpeed );
|
||||
// we'll let player velocity influence the knives a little..
|
||||
eDrop.velocity = ( arg_player.velocity*0.4 + vDir * throwableRef.fThrowSpeed );
|
||||
|
||||
|
||||
//modify the vector we send a bit...
|
||||
//-70 ?
|
||||
//[-90, -90, 0]
|
||||
rotatevectorsbyangle([-70, 0, 0]);
|
||||
|
||||
// or "v_dir" from later?
|
||||
eDrop.angles = vectoangles( v_forward );
|
||||
eDrop.angles.z = 90;
|
||||
|
||||
eDrop.classname = "remove_me";
|
||||
|
||||
// Also set m_owner
|
||||
eDrop.owner = arg_player;
|
||||
eDrop.m_owner = eDrop.owner;
|
||||
|
||||
|
||||
eDrop.touch = TSThrownProjectile::Touch;
|
||||
eDrop.think = TSThrownProjectile::Think;
|
||||
eDrop.nextthink = time + 0.0; //every frame.
|
||||
|
||||
//eDrop.PlayerUse = TSWorldGun::PlayerUse;
|
||||
// "use" does nothing to me... yet.
|
||||
eDrop.PlayerUse = NULL;
|
||||
|
||||
eDrop.gravity = throwableRef.fThrowGravity;
|
||||
|
||||
|
||||
eDrop.copyFrom2(arg_player, dynaRefPRE);
|
||||
|
||||
//uhh.. ok, sure
|
||||
eDrop.health = 1;
|
||||
|
||||
|
||||
// TAGGG - NOTE.
|
||||
// Somewhere above 1.4? sticks a foot out of the wall (floating in space; none of the knife is in it)
|
||||
// Somewhere at/below 1.4? stuck too far in the wall.
|
||||
// So we'll need a traceline to get right against the thing we collided with?
|
||||
// AND still, throwing at glass doesn't work quite perfect either way.
|
||||
// With undersized bounds, it registers as a hit on the glass but is also stopped
|
||||
// by the glass, like grenades are...
|
||||
// with oversized bounds, the game thinks we hit the map first anyway (reported entity hit:
|
||||
// 'worldpspawn') even though destroying the glass separately and throwing again has no
|
||||
// collision at all in that space, leading me to believe it collided with the glass but called
|
||||
// it worldspawn anyway.
|
||||
// W. H. A. T.
|
||||
//TAGGG - TODO. then. apparently.
|
||||
//setsize( eKnife, '-1.7 -1.7 -1.7', '1.7 1.7 1.7' );
|
||||
//setsize( eKnife, '-1.4 -1.4 -1.4', '1.4 1.4 1.4' );
|
||||
//setsize( eKnife, '-1 -1 -1', '1 1 1' );
|
||||
setsize( eDrop, '-1 -1 0', '1 1 1' );
|
||||
|
||||
//setsize( eDrop, '-16 -16 0', '16 16 16' );
|
||||
|
||||
|
||||
//And lastly, assume I need to expire at some point.
|
||||
// Expires 10 seconds after becoming a pickup. But just in case it never
|
||||
// does for some odd reason, no business existing too long anyway.
|
||||
eDrop.expireTime = time + 30;
|
||||
|
||||
//eDrop.vPreviousVelocity = eDrop.velocity;
|
||||
|
||||
eDrop.startAngularVelocityDelay = time + 0.008; //0.013?
|
||||
|
||||
eDrop.bouncefactor = 0.2;
|
||||
eDrop.friction = 0.78;
|
||||
|
||||
return eDrop;
|
||||
}//END OF generate
|
||||
|
||||
|
||||
void TSThrownProjectile::becomePickupable(BOOL becomeStuck, optional vector dir, optional entity stuckTo, optional BOOL touchedBreakableGlass){
|
||||
|
||||
forceBecomePickupableTime = -1;
|
||||
|
||||
//in a short while, switch to normal bounds.
|
||||
if(switchToNormalPickupBoundsTime == -1){
|
||||
switchToNormalPickupBoundsTime = time + 0.7;
|
||||
}
|
||||
|
||||
|
||||
if(becomeStuck){
|
||||
/*
|
||||
makevectors( normalize(this.velocity) );
|
||||
//rotatevectorsbyangle([-70, 0, 0]);
|
||||
// or "v_dir" from later?
|
||||
this.angles = vectoangles(v_forward);
|
||||
*/
|
||||
if ( touchedBreakableGlass ) {
|
||||
//no! Go through it.
|
||||
// this means, insta-break the breakable glass we hit.
|
||||
|
||||
Damage_Apply( other, this.m_owner, other.health, myInfo.weaponID, DMG_SLASH);
|
||||
queuedVelocity = this.velocity;
|
||||
queuedVelocityApplyTime = time + 0.01;
|
||||
return; //don't do the rest of this method.
|
||||
}else{
|
||||
//stuck as planned.
|
||||
this.angles = vectoangles(normalize(this.velocity));
|
||||
this.angles.z = 90;
|
||||
this.velocity = [0,0,0];
|
||||
startAngularVelocityDelay = -1; //if we haven't reached this, certianly don't try to!
|
||||
|
||||
// assume we can trust trace_endpos.
|
||||
this.origin = trace_endpos - dir * 5.9;
|
||||
//this.origin -= dir * 5.9;
|
||||
this.movetype = MOVETYPE_FLY;
|
||||
}
|
||||
}else{
|
||||
//safe assumption?
|
||||
//this.angles.x = 0;
|
||||
this.angles.z = 0;
|
||||
this.gravity = 0.9;
|
||||
}
|
||||
|
||||
|
||||
// don't make the weapon drop noises. that is all.
|
||||
//this.touch = TSWorldGun::Touch;
|
||||
this.touch = NULL;
|
||||
|
||||
this.PlayerUse = TSWorldGun::PlayerUse;
|
||||
|
||||
// not yet freeman!
|
||||
//this.think = TSWorldGun::Think;
|
||||
//this.nextthink = time + 0.2f;
|
||||
|
||||
//this.expireTime = time + autocvar_weaponstay;
|
||||
// for some reason knives always expire in X seconds.
|
||||
// probably ok in case a bunch of players throw knives in a short timespan,
|
||||
// don't want to spam it up.
|
||||
this.expireTime = time + 30;
|
||||
this.avelocity = [0,0,0];
|
||||
|
||||
}//END OF becomePickupable
|
||||
|
||||
|
||||
void TSThrownProjectile::copyFrom(player arg_player, int arg_weaponID){
|
||||
weapondynamic_t tempRef = arg_player.ary_myWeapons[arg_weaponID];
|
||||
copyFrom2(arg_player, tempRef);
|
||||
}
|
||||
|
||||
// From what index in the player's inventory should I copy stats from?
|
||||
// Called as we're being filled to be thrown from the player.
|
||||
void TSThrownProjectile::copyFrom2(player arg_player, weapondynamic_t tempRef){
|
||||
|
||||
//weapondynamic_t tempRef = arg_player.ary_myWeapons[arg_weaponID];
|
||||
|
||||
COPYVARFROM(weaponID)
|
||||
COPYVARFROM(weaponTypeID)
|
||||
|
||||
//COPYVARFROM(iBitsUpgrade)
|
||||
//actually want to exclude the AKIMBO and FULLLOAD bits. They were only for the shop really.
|
||||
myInfo.iBitsUpgrade = tempRef.iBitsUpgrade & ~(BITS_WEAPONOPT_AKIMBO | BITS_WEAPONOPT_FULLLOAD);
|
||||
|
||||
//COPYVARFROM(iCount)
|
||||
weapondata_basic_t* weaponPointer = ary_weaponData[tempRef.weaponID];
|
||||
weapondata_basic_t basicRef = *((weapondata_basic_t*) weaponPointer);
|
||||
|
||||
// only one throwable is 'thrown' at a time ever.
|
||||
myInfo.iCount = 1;
|
||||
|
||||
|
||||
COPYVARFROM(iPrice) //who cares.
|
||||
//COPYVARFROM(iSlots)
|
||||
|
||||
//re-calculate the slots now.
|
||||
myInfo.iSlots = myInfo.iCount * basicRef.iSlots;
|
||||
|
||||
|
||||
//doesn't use these. We're a throwable.
|
||||
myInfo.iClipLeft = 0;
|
||||
myInfo.iClipAkimboLeft = 0;
|
||||
|
||||
|
||||
COPYVARFROM(iBitsUpgrade_on)
|
||||
COPYVARFROM(iFireMode)
|
||||
COPYVARFROM(iFireModeAkimbo)
|
||||
//COPYVARFROM(iIronSight)
|
||||
|
||||
COPYVARFROM(forceBodygroup1Submodel)
|
||||
|
||||
myInfo.iIronSight = 0; //default: not using it.
|
||||
|
||||
}//END OF copyFrom
|
||||
|
||||
// From what index in the player's inventory should I copy stats from?
|
||||
// Called as we're being picked up by a player to fill one of their weapon slots.
|
||||
void TSThrownProjectile::copyTo(player arg_player, int arg_weaponID){
|
||||
|
||||
weapondynamic_t tempRef = arg_player.ary_myWeapons[arg_weaponID];
|
||||
|
||||
COPYVARTO(weaponID)
|
||||
COPYVARTO(weaponTypeID)
|
||||
//COPYVARTO(iBitsUpgrade)
|
||||
//COPYVARTO(iCount)
|
||||
COPYVARTO(iPrice) //no one cares.
|
||||
//COPYVARTO(iSlots) //re-calculated.
|
||||
//COPYVARTO(iClipLeft)
|
||||
//COPYVARTO(iClipAkimboLeft)
|
||||
//COPYVARTO(iBitsUpgrade_on)
|
||||
//COPYVARTO(iFireMode)
|
||||
//COPYVARTO(iFireModeAkimbo)
|
||||
//COPYVARTO(iIronSight)
|
||||
COPYVARTO(forceBodygroup1Submodel)
|
||||
tempRef.iIronSight = 0; //default: not using it.
|
||||
|
||||
}//END OF copyTo
|
||||
|
||||
|
||||
void TSThrownProjectile::Touch( void ) {
|
||||
|
||||
if ( other.solid == SOLID_TRIGGER ) {
|
||||
return;
|
||||
}
|
||||
if ( other == this.owner ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// func_water has "other.solid == SOLID_BSP" but is still pass-thru-able??
|
||||
// WTF DO I DO.
|
||||
// TAGGG - CRITICAL. - when done, get new findings over to the grenade too, other throwable.
|
||||
if(other.solid == SOLID_NOT || other.solid == SOLID_TRIGGER || other.classname == "func_water"){
|
||||
//ok? don't react.
|
||||
return;
|
||||
}
|
||||
|
||||
vector dir = normalize(this.velocity);
|
||||
// was 15 each??
|
||||
|
||||
//HACKY TEST
|
||||
//printfline("knife vel %.2f %.2f %.2f", velocity.x, velocity.y, velocity.z);
|
||||
|
||||
|
||||
vector src = this.origin - this.velocity*0.06;
|
||||
vector end = this.origin + this.velocity*0.06;
|
||||
traceline(src, end, MOVE_HITMODEL, this);
|
||||
|
||||
//printfline("TRACEFRACTO %.2f", trace_fraction);
|
||||
if(trace_fraction == 1){
|
||||
//WTF??? all we can do.
|
||||
trace_endpos = this.origin;
|
||||
}
|
||||
|
||||
//TAGGG - TODO. Use the trace from further below's point of intersection instead of
|
||||
// "this.origin", mabey this will help..?
|
||||
//string hitTexture = getsurfacetexture(other, getsurfacenearpoint(other, this.origin));
|
||||
string hitTexture = getsurfacetexture(other, getsurfacenearpoint(other, trace_endpos));
|
||||
|
||||
printfline("TSThrownProjectile::Touch I TOUCHED %s solid:%d neartex:%s", other.classname, other.solid, hitTexture);
|
||||
|
||||
if(TRUE){
|
||||
//stop on anything else.
|
||||
// TODO - delete this on hitting a player other than the owner (not that this should even cause
|
||||
// a touch event then) and just stay stuck to any other thing, like map geometry.
|
||||
|
||||
BOOL touchedBreakableGlass = (( other.classname == "func_breakable" ) && ( other.material == GSMATERIAL_GLASS ));
|
||||
|
||||
|
||||
if(other == world || other.classname == "func_wall" || touchedBreakableGlass){
|
||||
//okie, get stuck.
|
||||
//TAGGG - TODO. Spark effect!
|
||||
|
||||
/* FIXME: more universal check? */
|
||||
//...yea there are obvious cases where this fails.
|
||||
//TAGGG - CRITICAL. HOW DO I FIX THIS. knivees can just get stuck on the skybox
|
||||
// still from the nearest texture not being 'sky'.
|
||||
|
||||
if (hitTexture == "sky") {
|
||||
//printfline("Knife: I DONE WENT OUT OF BOUNDS");
|
||||
remove(this);
|
||||
return;
|
||||
}
|
||||
|
||||
//notice, ATTN_NORM. Sound goes further.,
|
||||
sound( this, CHAN_WEAPON, "weapons/knife/knife_hit.wav", 1, ATTN_NORM - 0.15 );
|
||||
|
||||
|
||||
//printfline("trace_fraction %.2f", trace_fraction);
|
||||
|
||||
// Do we get stuck or bounce off?
|
||||
if(trace_fraction != 1.0){
|
||||
float theProd = dotproduct(dir, trace_plane_normal);
|
||||
//printfline("WHAT THE hek IS THIS %.2f", theProd);
|
||||
|
||||
if(theProd <= -0.73){
|
||||
//close to being opposites? be stuck
|
||||
becomePickupable(TRUE, dir, other, touchedBreakableGlass);
|
||||
}else{
|
||||
|
||||
//Did we hit the ground?
|
||||
float dotIsGround = dotproduct(trace_plane_normal, '0 0 1');
|
||||
//printfline("AM I GROUND %.2f", dotIsGround);
|
||||
if(dotIsGround >= 0.91){
|
||||
|
||||
//printfline("IM A proud TREE dandy FROG %.2f", this.velocity.z);
|
||||
if(this.velocity.z <= -260){
|
||||
//Going downards much? Get stuck in the ground.
|
||||
// Hack to look like it's more downwards into the ground.
|
||||
this.velocity.x *= 0.18;
|
||||
this.velocity.y *= 0.18;
|
||||
becomePickupable(TRUE, dir, other, touchedBreakableGlass);
|
||||
}else{
|
||||
//Bounce off the ground instead.
|
||||
becomePickupable(FALSE);
|
||||
//setsize( this, '-16 -16 0', '16 16 16' );
|
||||
|
||||
this.angles.x = 0; //go flat as you fall off.
|
||||
this.velocity = [this.velocity.x * 0.09, this.velocity.y * 0.09, this.velocity.z];
|
||||
this.movetype = MOVETYPE_BOUNCE;
|
||||
}
|
||||
|
||||
//TODO - what is "bouncestop" ??
|
||||
//printfline("GIMMIE DA STATS %.2f %.2f %.2f", mass, bouncestop, bouncefactor);
|
||||
|
||||
}else{
|
||||
this.movetype = MOVETYPE_BOUNCE;
|
||||
|
||||
//no? just keep going, probably.
|
||||
forceBecomePickupableTime = time + 1;
|
||||
|
||||
// thank you w_gauss.c!
|
||||
float n = -dotproduct(trace_plane_normal, dir);
|
||||
vector reflectedVel = vlen(this.velocity) * (2 * trace_plane_normal * n + dir);
|
||||
|
||||
// yes. we have to do this. Thanks, engine.
|
||||
this.origin += trace_plane_normal * 2;
|
||||
setorigin(this, this.origin);
|
||||
queuedVelocity = [reflectedVel.x * 0.8, reflectedVel.y * 0.8, reflectedVel.z];
|
||||
if(queuedVelocity.z > 0){
|
||||
//positive? reduce it a bit.
|
||||
queuedVelocity.z *= 0.85;
|
||||
}
|
||||
queuedVelocityApplyTime = time + 0.01;
|
||||
|
||||
// hold up! Angles?
|
||||
|
||||
vector daAngah = vectoangles(normalize(queuedVelocity));
|
||||
|
||||
int oldPitch = angles.x;
|
||||
|
||||
this.angles = daAngah;
|
||||
|
||||
this.angles.x = oldPitch;
|
||||
this.angles.z = 90;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}else{
|
||||
//??? just assume sticking out this much is ok.
|
||||
// assume stuck.
|
||||
becomePickupable(TRUE, dir, other, touchedBreakableGlass);
|
||||
}
|
||||
|
||||
|
||||
//setsize( this, '-16 -16 0', '16 16 16' );
|
||||
}else{
|
||||
|
||||
printfline("TSThrownProjectile::Touch HIT ENTITY? WHATS IT CALLED %s TAKEDAMAGE? %d", other.classname, other.takedamage);
|
||||
//is it something we can deal damage to?
|
||||
if(other.takedamage == DAMAGE_YES){
|
||||
|
||||
//is it fleshy? eh, assume it is.
|
||||
//there is always a "trace_ent.iBleeds" check though.
|
||||
sound( this, CHAN_WEAPON, "weapons/knife/knife_hitbody.wav", 1, ATTN_IDLE );
|
||||
|
||||
weapondata_basic_t* basicPointerPRE = (weapondata_basic_t*) ary_weaponData[this.myInfo.weaponID];
|
||||
//weapondata_basic_t basicRefPRE = *(basicPointerPRE);
|
||||
weapondata_throwable_t throwableRef = *((weapondata_throwable_t*)basicPointerPRE);
|
||||
|
||||
// tell the thing being damaged that my OWNER was what damaged it.
|
||||
Damage_Apply(other, this.m_owner, throwableRef.fThrowDamage, myInfo.weaponID, DMG_SLASH);
|
||||
|
||||
remove(this);
|
||||
return;
|
||||
}else{
|
||||
//oh. bounce off I guess.
|
||||
//TAGGG - TODO. Spark effect!
|
||||
//TODO - bounce the opposite direction floor-wise?
|
||||
sound( this, CHAN_WEAPON, "weapons/knife/knife_hit.wav", 1, ATTN_NORM - 0.15 );
|
||||
|
||||
becomePickupable(FALSE);
|
||||
//setsize( this, '-16 -16 0', '16 16 16' );
|
||||
|
||||
this.angles.x = 0; //go flat as you fall off.
|
||||
this.velocity = [this.velocity.x * 0.24, this.velocity.y * 0.24, this.velocity.z];
|
||||
this.movetype = MOVETYPE_BOUNCE;
|
||||
|
||||
}
|
||||
}
|
||||
//this.touch = NULL; //no more responses.
|
||||
}
|
||||
|
||||
|
||||
}//END OF touch method
|
||||
|
||||
|
||||
|
||||
void TSThrownProjectile::PlayerUse( void ) {
|
||||
//EMPTY. Use TSWorldGun's PlayerUse once we dropped,
|
||||
// OR have logic that redirects to that in here when we want to make this pickup-able.
|
||||
// Whichever works.
|
||||
}
|
||||
|
||||
|
||||
void TSThrownProjectile::Think( void ) {
|
||||
|
||||
if(this.startAngularVelocityDelay != -1 && time >= this.startAngularVelocityDelay){
|
||||
this.startAngularVelocityDelay = -1;
|
||||
this.avelocity = [360 * 2.7, 0, 0];
|
||||
}
|
||||
|
||||
if(forceBecomePickupableTime != -1 && time >= forceBecomePickupableTime){
|
||||
//check. are we moving too slow? keep rechecking after.
|
||||
forceBecomePickupableTime = time + 1;
|
||||
if(vlen(velocity) < 8){
|
||||
//ok.
|
||||
forceBecomePickupableTime = -1;
|
||||
becomePickupable(FALSE);
|
||||
//setsize( this, '-16 -16 0', '16 16 16' );
|
||||
|
||||
this.angles.x = 0; //go flat as you fall off.
|
||||
this.velocity = [this.velocity.x * 0.07, this.velocity.y * 0.07, this.velocity.z];
|
||||
this.movetype = MOVETYPE_BOUNCE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(queuedVelocityApplyTime != -1 && time >= queuedVelocityApplyTime){
|
||||
queuedVelocityApplyTime = -1;
|
||||
this.velocity = queuedVelocity;
|
||||
}
|
||||
|
||||
|
||||
if(time >= expireTime){
|
||||
// that's the end.
|
||||
remove(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(switchToNormalPickupBoundsTime >= 0 && time >= switchToNormalPickupBoundsTime){
|
||||
switchToNormalPickupBoundsTime = -2; //to not try this again.
|
||||
// and resume the normal think method.
|
||||
setsize( this, '-16 -16 0', '16 16 16' );
|
||||
this.think = TSWorldGun::Think;
|
||||
this.nextthink = time + 0.01f;
|
||||
//printfline("OH YEAHHHHHHHHhhhhh");
|
||||
return;
|
||||
}
|
||||
|
||||
//this.vPreviousVelocity = this.velocity;
|
||||
this.nextthink = time + 0.0;
|
||||
}
|
||||
|
||||
|
36
src/server/entity/TSWorldGun.h
Normal file
36
src/server/entity/TSWorldGun.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
//class TSWorldGun;
|
||||
|
||||
// not inheriting from CBaseEntity... this wasn't made with that in mind,
|
||||
// and it doesn't look like it needs anything from there.
|
||||
// If there were a version of this to be respawnable, it may make sense to make it a child of
|
||||
// CBaseEntiy and then remove the GF_CANRESPAWN flag gflags added to all CBaseEntities by default
|
||||
// (I think)... since changed to the IDENTITY_CANRESPAWN flag of ".identity" for CBaseEntity's
|
||||
class TSWorldGun{
|
||||
|
||||
// After being recently accessed by a player but not deleted,
|
||||
// I enforce a cooldown time before any other player (including the same one)
|
||||
// can try to pick me up again. This stops spamming inventory scans every
|
||||
// single frame if a player stands over a weapon they can't pick up.
|
||||
float accessibleCooldownTime;
|
||||
// To avoid server clutter over time, a pickup will delete itself
|
||||
float expireTime;
|
||||
|
||||
weapondynamic_t myInfo;
|
||||
|
||||
|
||||
void(void) TSWorldGun;
|
||||
virtual void() Think;
|
||||
virtual void() Touch;
|
||||
virtual void() PlayerUse;
|
||||
static TSWorldGun (player arg_player, int arg_weaponID) generate;
|
||||
static TSWorldGun (player arg_player, weapondynamic_t dynaRefPRE) generate2;
|
||||
|
||||
virtual void (player arg_player, int arg_weaponID) copyFrom;
|
||||
virtual void (player arg_player, weapondynamic_t tempRef) copyFrom2;
|
||||
|
||||
virtual void (player arg_player, int arg_weaponID) copyTo;
|
||||
|
||||
};
|
||||
|
||||
|
259
src/server/entity/TSWorldGun.qc
Normal file
259
src/server/entity/TSWorldGun.qc
Normal file
|
@ -0,0 +1,259 @@
|
|||
|
||||
|
||||
|
||||
void TSWorldGun::TSWorldGun(void){
|
||||
|
||||
accessibleCooldownTime = -1;
|
||||
myInfo = spawn(weapondynamic_t);
|
||||
|
||||
}
|
||||
|
||||
|
||||
TSWorldGun TSWorldGun::generate(player arg_player, int arg_weaponID){
|
||||
weapondynamic_t dynaRefPRE = arg_player.ary_myWeapons[arg_weaponID];
|
||||
return TSWorldGun::generate2(arg_player, dynaRefPRE);
|
||||
}
|
||||
|
||||
// Static method called from elsewhere to create and initialize a
|
||||
// weapon drop entity, given a player and what weapon to copy information from.
|
||||
TSWorldGun TSWorldGun::generate2(player arg_player, weapondynamic_t dynaRefPRE){
|
||||
TSWorldGun eDrop = spawn(TSWorldGun);
|
||||
|
||||
//eDrop.setup(arg_player, arg_weaponID);
|
||||
|
||||
//weapondynamic_t dynaRefPRE = arg_player.ary_myWeapons[arg_weaponID];
|
||||
weapondata_basic_t* basicPointerPRE = (weapondata_basic_t*) ary_weaponData[dynaRefPRE.weaponID];
|
||||
weapondata_basic_t basicRefPRE = *(basicPointerPRE);
|
||||
|
||||
|
||||
eDrop.movetype = MOVETYPE_TOSS;
|
||||
eDrop.solid = SOLID_TRIGGER; //or CORPSE
|
||||
|
||||
//TSWorldGun eDrop = spawn(TSWorldGun);
|
||||
setorigin( eDrop, arg_player.origin + arg_player.view_ofs );
|
||||
setmodel( eDrop, basicRefPRE.sWorldModelPath );
|
||||
|
||||
eDrop.angles = [0, randomInRange_f(0, 360), 0];
|
||||
eDrop.avelocity = [0, randomInRange_f(100, 500), 0];
|
||||
|
||||
eDrop.classname = "remove_me";
|
||||
eDrop.owner = arg_player;
|
||||
//eDrop.weapon = dynaRefPRE.weaponID;
|
||||
eDrop.think = TSWorldGun::Think;
|
||||
eDrop.touch = TSWorldGun::Touch;
|
||||
eDrop.nextthink = time + 0.5f;
|
||||
|
||||
eDrop.copyFrom2(arg_player, dynaRefPRE);
|
||||
|
||||
//uhh.. ok, sure
|
||||
eDrop.health = 1;
|
||||
|
||||
|
||||
setsize( eDrop, '-16 -16 0', '16 16 16' );
|
||||
|
||||
//is this v_forward sometimes bad.. ?
|
||||
makevectors( arg_player.v_angle );
|
||||
|
||||
eDrop.velocity = arg_player.velocity + v_forward * 320; //256?
|
||||
eDrop.gravity = 0.9;
|
||||
|
||||
//And lastly, assume I need to expire at some point.
|
||||
eDrop.expireTime = time + autocvar_weaponstay; //autocvar_weaponstay
|
||||
|
||||
eDrop.PlayerUse = TSWorldGun::PlayerUse;
|
||||
|
||||
|
||||
return eDrop;
|
||||
}//END OF generate
|
||||
|
||||
|
||||
|
||||
void TSWorldGun::copyFrom(player arg_player, int arg_weaponID){
|
||||
weapondynamic_t tempRef = arg_player.ary_myWeapons[arg_weaponID];
|
||||
copyFrom2(arg_player, tempRef);
|
||||
}
|
||||
|
||||
// From what index in the player's inventory should I copy stats from?
|
||||
// Called as we're being filled to be thrown from the player.
|
||||
void TSWorldGun::copyFrom2(player arg_player, weapondynamic_t tempRef){
|
||||
|
||||
//weapondynamic_t tempRef = arg_player.ary_myWeapons[arg_weaponID];
|
||||
|
||||
|
||||
COPYVARFROM(weaponID)
|
||||
COPYVARFROM(weaponTypeID)
|
||||
|
||||
|
||||
//COPYVARFROM(iBitsUpgrade)
|
||||
//actually want to exclude the AKIMBO and FULLLOAD bits. They were only for the shop really.
|
||||
myInfo.iBitsUpgrade = tempRef.iBitsUpgrade & ~(BITS_WEAPONOPT_AKIMBO | BITS_WEAPONOPT_FULLLOAD);
|
||||
|
||||
//COPYVARFROM(iCount)
|
||||
weapondata_basic_t* weaponPointer = ary_weaponData[tempRef.weaponID];
|
||||
weapondata_basic_t basicRef = *((weapondata_basic_t*) weaponPointer);
|
||||
if(basicRef.typeID == WEAPONDATA_TYPEID_THROWABLE){
|
||||
//we're a throwable? Send the count over.
|
||||
COPYVARFROM(iCount)
|
||||
}else{
|
||||
// Always a count of 1. Any dropped weapon from akimbo is really just "one" of the two pickups needed.
|
||||
// Even weapons that only come in akimbo, like goldencolts, don't hurt by being called a count of "1".
|
||||
// They keep the same shop stats, which is really the point here.
|
||||
myInfo.iCount = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
COPYVARFROM(iPrice) //who cares.
|
||||
//COPYVARFROM(iSlots)
|
||||
|
||||
//re-calculate the slots now.
|
||||
myInfo.iSlots = myInfo.iCount * basicRef.iSlots;
|
||||
|
||||
|
||||
//Hold on... this isn't so simple.
|
||||
|
||||
if(basicRef.typeID == WEAPONDATA_TYPEID_GUN || basicRef.typeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
if(basicRef.iBitsUpgradeAuto & BITS_WEAPONOPT_AKIMBO){
|
||||
//If the weapon we're spawned from is forced akimbo, the intuitive way works.
|
||||
//iClipLeft & iClipAkimboLeft can be filled, this is a singular drop to represent akimbo.
|
||||
COPYVARFROM(iClipLeft)
|
||||
COPYVARFROM(iClipAkimboLeft)
|
||||
}else if(basicRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO && tempRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO){
|
||||
//If the weapon supports akimbo and we have it, assume the thing dropped is the 2nd weapon.
|
||||
//That is, the one tied to "iClipAkimboLeft".
|
||||
|
||||
if(arg_player.weaponEquippedAkimbo){
|
||||
myInfo.iClipLeft = tempRef.iClipAkimboLeft;
|
||||
myInfo.iClipAkimboLeft = 0;
|
||||
}else{
|
||||
//oh.. drop the singular varient's ammo count then.
|
||||
myInfo.iClipLeft = tempRef.iClipLeft;
|
||||
myInfo.iClipAkimboLeft = 0;
|
||||
}
|
||||
}else{
|
||||
//not akimbo or doesn't support it? Only this one.
|
||||
myInfo.iClipLeft = tempRef.iClipLeft;
|
||||
myInfo.iClipAkimboLeft = 0;
|
||||
}
|
||||
|
||||
}else{
|
||||
//doesn't use these.
|
||||
myInfo.iClipLeft = 0;
|
||||
myInfo.iClipAkimboLeft = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
COPYVARFROM(iBitsUpgrade_on)
|
||||
COPYVARFROM(iFireMode) //we can always copy both firemodes to any individual weapon, can't really hurt.
|
||||
COPYVARFROM(iFireModeAkimbo)
|
||||
//COPYVARFROM(iIronSight)
|
||||
|
||||
COPYVARFROM(forceBodygroup1Submodel)
|
||||
|
||||
myInfo.iIronSight = 0; //default: not using it.
|
||||
|
||||
}//END OF copyFrom
|
||||
|
||||
// From what index in the player's inventory should I copy stats from?
|
||||
// Called as we're being picked up by a player to fill one of their weapon slots.
|
||||
void TSWorldGun::copyTo(player arg_player, int arg_weaponID){
|
||||
|
||||
|
||||
weapondynamic_t tempRef = arg_player.ary_myWeapons[arg_weaponID];
|
||||
|
||||
COPYVARTO(weaponID)
|
||||
COPYVARTO(weaponTypeID)
|
||||
//COPYVARTO(iBitsUpgrade)
|
||||
//COPYVARTO(iCount)
|
||||
COPYVARTO(iPrice) //no one cares.
|
||||
//COPYVARTO(iSlots) //re-calculated.
|
||||
//COPYVARTO(iClipLeft)
|
||||
//COPYVARTO(iClipAkimboLeft)
|
||||
//COPYVARTO(iBitsUpgrade_on)
|
||||
//COPYVARTO(iFireMode)
|
||||
//COPYVARTO(iFireModeAkimbo)
|
||||
//COPYVARTO(iIronSight)
|
||||
COPYVARTO(forceBodygroup1Submodel)
|
||||
tempRef.iIronSight = 0; //default: not using it.
|
||||
|
||||
}//END OF copyTo
|
||||
|
||||
|
||||
|
||||
void TSWorldGun::Touch( void ) {
|
||||
if ( other.classname != "player" ) {
|
||||
// assume it's the world.. play this sound?
|
||||
|
||||
//printfline("the thing I touched is %s", other.classname);
|
||||
// TODO - any others we need to keep track of for making the drop?
|
||||
if(other.classname == "worldspawn" || other.classname == "func_breakable"){
|
||||
sound( this, CHAN_ITEM, "items/weapondrop1.wav", 1, ATTN_NORM, 100, SOUNDFLAG_CUSTOMCLIENT );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}//END OF touch method
|
||||
|
||||
|
||||
|
||||
void TSWorldGun::PlayerUse( void ) {
|
||||
|
||||
//laziness.
|
||||
//entity other = eActivator;
|
||||
//...no, hides a global, not wise.
|
||||
other = eActivator; //this however should be fine?
|
||||
|
||||
// !!! IMPORTANT !!!
|
||||
// Want to be able to pick me up rapidly? You have to remove this check too,
|
||||
// or setting the owner to "player" on generation.
|
||||
// This means the player that spawend me can't pick me up for a little while.
|
||||
// This is completely independent of accessibleCooldownTime, which prevents
|
||||
// all players from picking me up too soon instead.
|
||||
if ( other.classname != "player" ) {
|
||||
return;
|
||||
} else if ( other == this.owner ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//entity eOld = self;
|
||||
//TSWorldGun selfRef = (TSWorldGun)self;
|
||||
//self = other;
|
||||
|
||||
player otherPlayerRef = (player)other; //safe assumption now.
|
||||
|
||||
|
||||
BOOLEAN shouldDelete;
|
||||
if(time >= this.accessibleCooldownTime){
|
||||
shouldDelete = otherPlayerRef.attemptAddWeaponFromPickup(this);
|
||||
this.accessibleCooldownTime = time + 0.8;
|
||||
}else{
|
||||
shouldDelete = FALSE;
|
||||
}
|
||||
|
||||
//self = eOld;
|
||||
if(shouldDelete){
|
||||
remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TSWorldGun::Think( void ) {
|
||||
//TSWorldGun selfRef = (TSWorldGun)self;
|
||||
// oh.. this makes the weapon collidable with the original dropper
|
||||
// again.
|
||||
|
||||
if(time >= this.expireTime){
|
||||
//expire.
|
||||
remove(this);
|
||||
}else{
|
||||
this.owner = world;
|
||||
//set the next think to aim around this time then.
|
||||
this.nextthink = this.expireTime + 0.1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
0
src/server/entity/ts_bomb.qc
Normal file
0
src/server/entity/ts_bomb.qc
Normal file
0
src/server/entity/ts_dmhill.qc
Normal file
0
src/server/entity/ts_dmhill.qc
Normal file
0
src/server/entity/ts_groundweapon.qc
Normal file
0
src/server/entity/ts_groundweapon.qc
Normal file
0
src/server/entity/ts_hack.qc
Normal file
0
src/server/entity/ts_hack.qc
Normal file
0
src/server/entity/ts_mapglobals.qc
Normal file
0
src/server/entity/ts_mapglobals.qc
Normal file
0
src/server/entity/ts_model.qc
Normal file
0
src/server/entity/ts_model.qc
Normal file
0
src/server/entity/ts_objective_manager.qc
Normal file
0
src/server/entity/ts_objective_manager.qc
Normal file
0
src/server/entity/ts_objective_ptr.qc
Normal file
0
src/server/entity/ts_objective_ptr.qc
Normal file
11
src/server/entity/ts_powerup.h
Normal file
11
src/server/entity/ts_powerup.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
|
||||
class ts_powerup;
|
||||
|
||||
|
||||
#ifdef CSQC
|
||||
// static method... essentially at least.
|
||||
void Powerup_Parse(void);
|
||||
#endif
|
||||
|
725
src/server/entity/ts_powerup.qc
Normal file
725
src/server/entity/ts_powerup.qc
Normal file
|
@ -0,0 +1,725 @@
|
|||
|
||||
|
||||
#define POWERUP_MODEL_PATH "models/powerup.mdl"
|
||||
|
||||
|
||||
enum pwuptype_choice{
|
||||
Random = 0,
|
||||
Slow_Motion = 1,
|
||||
Infinite_Clip = 2,
|
||||
Kung_Fu = 4,
|
||||
Slow_Pause = 8,
|
||||
Double_Firerate = 16,
|
||||
Grenade = 32,
|
||||
Health = 64,
|
||||
Armor = 128,
|
||||
Low_Gravity = 256 //better known as super/power jump? ehh see the manual, it has the boots icon'd one.
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SSQC
|
||||
|
||||
class ts_powerup:CBaseEntity{
|
||||
|
||||
|
||||
//key values?
|
||||
pwuptype_choice m_ePwuptype;
|
||||
int m_iPwupduration;
|
||||
string m_strMessage;
|
||||
float expireTime;
|
||||
float respawnTime;
|
||||
|
||||
// actual picked submodel. Can be looked at for logic on touch.
|
||||
// nahhh, just take it from the powerup_data_... structs.
|
||||
//int submodelIndex;
|
||||
|
||||
// What member of ary_powerupData do I get my behavior/model/etc. from?
|
||||
POWERUP_ID dataID;
|
||||
BOOL alreadyPickedUp;
|
||||
BOOL isRespawnCall;
|
||||
|
||||
vector mem_origin;
|
||||
|
||||
void(void) ts_powerup;
|
||||
virtual void(void) CustomInit;
|
||||
//virtual void(void) restartAnim;
|
||||
virtual float(entity pvsent, float cflags) send;
|
||||
virtual void(void) Respawn;
|
||||
virtual void(void) Think;
|
||||
virtual void(void) Touch;
|
||||
virtual void(void) determineDataID;
|
||||
virtual void(void) declareDisposable;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void ts_powerup::ts_powerup(void){
|
||||
|
||||
// And why don't we just receive these as name/value pairs like clientside does?
|
||||
// I HAVE NO CLUE.
|
||||
// And is it a problem we never call CBaseEntity's constructor?
|
||||
// If we did it would read every single field that way, in addition to us
|
||||
// reading every single field here, just to get info that the CBaseEntity expects
|
||||
// (origin) and what ts_powerup expects. Not efficient!
|
||||
|
||||
|
||||
int nfields = tokenize( __fullspawndata );
|
||||
for (int i = 1; i < (nfields - 1 ); i += 2) {
|
||||
switch ( argv( i ) ) {
|
||||
case "pwuptype":
|
||||
m_ePwuptype = stoi(argv(i + 1));
|
||||
break;
|
||||
// this is actually the time that passes before respawning.
|
||||
// So at one time this was meant to determine how long a powerup lasts once used? really?
|
||||
case "pwupduration":
|
||||
m_iPwupduration = stoi(argv(i + 1));
|
||||
break;
|
||||
// Pretty sure this goes completely unused. Wouldn't only some tutorial have any reason
|
||||
// to put a message on screen upon touching a powerup (so I assume is the point)?
|
||||
case "message":
|
||||
m_strMessage = argv(i + 1);
|
||||
break;
|
||||
//VECTOR PARAMETER EXAMPLE (not in this one though):
|
||||
/*
|
||||
case "color":
|
||||
colormod = stov( argv( i + 1 ) );
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//gflags |= GF_CANRESPAWN;
|
||||
|
||||
}//END OF constructor
|
||||
|
||||
|
||||
void ts_powerup::CustomInit(void){
|
||||
|
||||
|
||||
expireTime = -1; //assumption.
|
||||
respawnTime = -1;
|
||||
|
||||
//anything spechul?
|
||||
|
||||
//setmodel( this, "models/powerup.mdl" );
|
||||
|
||||
this.isRespawnCall = TRUE;
|
||||
|
||||
this.alreadyPickedUp = FALSE;
|
||||
|
||||
|
||||
this.classname = "ts_powerup";
|
||||
this.owner = world;
|
||||
this.movetype = MOVETYPE_TOSS;
|
||||
this.solid = SOLID_TRIGGER; //or CORPSE
|
||||
|
||||
this.pvsflags = PVSF_NOREMOVE | PVSF_IGNOREPVS;
|
||||
|
||||
|
||||
//customphysics?
|
||||
//eDrop.think = ts_powerup::Think;
|
||||
//eDrop.touch = ts_powerup::Touch;
|
||||
//this.nextthink = time + 0.5f;
|
||||
|
||||
//uhh.. ok, sure
|
||||
//this.health = 1;
|
||||
|
||||
setsize( this, '-24 -24 0', '24 24 48' );
|
||||
|
||||
//this.modelflags = ?
|
||||
this.frame = 0;
|
||||
this.frame1time = 0;
|
||||
|
||||
//frames: 31
|
||||
//FPS: 20
|
||||
|
||||
//this.think = restartAnim;
|
||||
//this.nextthink = time + (31-2)/20;
|
||||
|
||||
this.model = POWERUP_MODEL_PATH;
|
||||
|
||||
//takedamage = DAMAGE_NO;
|
||||
|
||||
// start at where I spawned, already recorded by CBaseEntity for us.
|
||||
origin = m_oldOrigin;
|
||||
|
||||
// Tone the gravity down to see them fall better / prove their positions are reset on spawn.
|
||||
//this.gravity = 0.006;
|
||||
|
||||
// And this lets the powerup start against the ground, compared to where it was placed on the map.
|
||||
// Original TS might not even do this, but hey. Why not, it would end up here anyway in a second of
|
||||
// gametime.
|
||||
if(!droptofloor()){
|
||||
vector vSource = this.origin;
|
||||
//we must take matters into our own hands.
|
||||
traceline ( vSource, vSource + ( '0 0 -1' * 300 ), TRUE, this );
|
||||
//entity ef = findradius(trace_endpos, 40);
|
||||
|
||||
this.origin[2] = trace_endpos[2];
|
||||
}
|
||||
|
||||
// forget if we were on the ground.
|
||||
// Yes, we have to do this or we forget to fall since a origin force-change (teleport)
|
||||
// I LOVE YOU, ENGINE.
|
||||
flags &= ~FL_ONGROUND;
|
||||
groundentity = NULL;
|
||||
|
||||
|
||||
// Do this AFTER your position-shifting shenanigans you hooligan!
|
||||
setorigin(this, origin);
|
||||
|
||||
determineDataID();
|
||||
|
||||
// not necessarily the origin at spawntime,
|
||||
// this keeps track of the origin since the last time we let the client know what it is.
|
||||
// Yes. Really.
|
||||
mem_origin = this.origin;
|
||||
|
||||
this.SendEntity = send;
|
||||
this.SendFlags = 1;
|
||||
|
||||
this.touch = ts_powerup::Touch;
|
||||
think = ts_powerup::Think;
|
||||
this.nextthink = time + 0;
|
||||
|
||||
// ??? SERVERTEST
|
||||
//setmodel( this, "models/powerup.mdl" );
|
||||
|
||||
//m_oldModel = POWERUP_MODEL_PATH;
|
||||
//RendermodeUpdate();
|
||||
|
||||
}//END OF CustomInit
|
||||
|
||||
|
||||
|
||||
float ts_powerup::send(entity pvsent, float cflags)
|
||||
{
|
||||
WriteByte(MSG_ENTITY, ENT_POWERUP);
|
||||
WriteCoord(MSG_ENTITY, this.origin[0]);
|
||||
WriteCoord(MSG_ENTITY, this.origin[1]);
|
||||
WriteCoord(MSG_ENTITY, this.origin[2]);
|
||||
WriteCoord(MSG_ENTITY, this.angles[0]);
|
||||
WriteCoord(MSG_ENTITY, this.angles[1]);
|
||||
WriteCoord(MSG_ENTITY, this.angles[2]);
|
||||
|
||||
WriteByte(MSG_ENTITY, this.dataID);
|
||||
WriteByte(MSG_ENTITY, this.alreadyPickedUp);
|
||||
WriteByte(MSG_ENTITY, this.isRespawnCall);
|
||||
|
||||
this.isRespawnCall = FALSE; //only send once this time.
|
||||
|
||||
//WriteByte(MSG_ENTITY, dataID);
|
||||
|
||||
//WriteString(MSG_ENTITY, this.texture);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ts_powerup::Respawn(void){
|
||||
|
||||
CustomInit();
|
||||
|
||||
}//END OF Respawn
|
||||
|
||||
|
||||
|
||||
|
||||
void ts_powerup::Think(void){
|
||||
|
||||
//TAGGG - CRITICAL. DISCUSS.
|
||||
// Is it ok to resend the entity to the client(s) just because our position changed?
|
||||
// There is no way around this? Any part of physics handled clientside for an ounce of prediction? etc.?
|
||||
// Would falling look odd on low internet connections?
|
||||
// Yes pre-placed map powerups wouldn't spend much time falling. Or any if we're using the 'detect & snap to ground'
|
||||
// feature, but this is more of a concern for other things that seem to lose all default functionality for server-client
|
||||
// interactions (setting SendEntity ever I think is what causes this?)
|
||||
|
||||
if(respawnTime != -1 && time >= respawnTime){
|
||||
// re-appear then!
|
||||
respawnTime = -1;
|
||||
Respawn();
|
||||
return;
|
||||
}
|
||||
if(this.origin != mem_origin){
|
||||
mem_origin = this.origin;
|
||||
//uh-oh. we gotta tell the client.
|
||||
this.SendEntity = send;
|
||||
this.SendFlags = 1;
|
||||
}
|
||||
|
||||
|
||||
this.nextthink = time + 0.01;
|
||||
|
||||
}//END OF think
|
||||
|
||||
|
||||
void ts_powerup::Touch(void){
|
||||
|
||||
|
||||
if(other.classname != "player"){
|
||||
return;
|
||||
}
|
||||
|
||||
//TAGGG - TODO. Actually query to see whether our type of powerup is usable, and, if so, forbid
|
||||
// adding to the player if they already have a usable powerup equipped (stored for use).
|
||||
// Any other time, this powerup always gets deleted & makes the gunpickup noise in original TS behavior.
|
||||
// Deviate however, or not.
|
||||
sound( other, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM, 100, SOUNDFLAG_CUSTOMCLIENT );
|
||||
|
||||
|
||||
//remove(self);
|
||||
|
||||
if(this.expireTime == -1){
|
||||
//pre-placed by the map? Create the illusion we disappeared.
|
||||
CBaseEntity::Hide();
|
||||
this.alreadyPickedUp = TRUE;
|
||||
|
||||
//respawn 'm_iPwupduration seconds from now, often 60 (at least it is in ts_bikini)
|
||||
this.respawnTime = time + m_iPwupduration;
|
||||
|
||||
this.SendEntity = send;
|
||||
this.SendFlags = 1;
|
||||
}else{
|
||||
//We were going to expire at some point? Do so now then, no need to hide to never be respawned and deleted later.
|
||||
remove(self);
|
||||
}
|
||||
|
||||
}//END OF Touch
|
||||
|
||||
|
||||
|
||||
|
||||
void ts_powerup::determineDataID(void){
|
||||
int bitsToChoose[9];
|
||||
|
||||
// really.. this is just setting bitsToChoose[0] to 0. Stops a 'uninitialized variable'
|
||||
// warning when done this way instead.
|
||||
// God I love FTE.
|
||||
(*bitsToChoose) = 0;
|
||||
|
||||
|
||||
//TEST to see how this works out.
|
||||
// Should only ever spawn karate and double-fire.
|
||||
//m_ePwuptype = 4 | 16;
|
||||
// And infinite clip/double clip, and slow pause/old slow pause.
|
||||
//m_ePwuptype = 2 | 8;
|
||||
|
||||
|
||||
int _1Count = 0;
|
||||
int currentBit = 1;
|
||||
int bitmask = m_ePwuptype;
|
||||
int bitEnd = 256;
|
||||
while(TRUE){
|
||||
if(bitmask & currentBit){
|
||||
bitsToChoose[_1Count] = currentBit;
|
||||
_1Count += 1;
|
||||
}
|
||||
if(currentBit < bitEnd){
|
||||
currentBit = currentBit << 1; //next.
|
||||
}else{
|
||||
//stop!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int bitIndexChosen = 0;
|
||||
int bitChosen = 0;
|
||||
|
||||
if(_1Count == 0){
|
||||
//choose from them all now.
|
||||
bitIndexChosen = randomInRange_i(0, 8);
|
||||
bitChosen = (1 << bitIndexChosen);
|
||||
}else if(_1Count == 1){
|
||||
bitIndexChosen = 0;
|
||||
bitChosen = bitsToChoose[bitIndexChosen];
|
||||
}else{
|
||||
bitIndexChosen = randomInRange_i(0, _1Count-1);
|
||||
bitChosen = bitsToChoose[bitIndexChosen];
|
||||
}
|
||||
|
||||
//submodel 4: a pause sign. ???
|
||||
//submodel 11: double-magazine (possible replacement for infinite ammo in some versions)?
|
||||
if(bitChosen == 1){
|
||||
//slow motion
|
||||
//submodelIndex = 5;
|
||||
dataID = POWERUP_ID::SlowMotion;
|
||||
}else if(bitChosen == 2){
|
||||
//"Infinite Clip"
|
||||
//submodelIndex = 3;
|
||||
|
||||
if(_1Count == 1){
|
||||
//force the original only.
|
||||
dataID = POWERUP_ID::InfiniteClip;
|
||||
}else{
|
||||
//I was picked of at least 1 other choice? Allow me to be the alternate too.
|
||||
if(random() < 0.5){
|
||||
dataID = POWERUP_ID::InfiniteClip;
|
||||
}else{
|
||||
dataID = POWERUP_ID::DoubleClip;
|
||||
}
|
||||
}
|
||||
}else if(bitChosen == 4){
|
||||
//"Kung Fu"
|
||||
//submodelIndex = 1;
|
||||
dataID = POWERUP_ID::KungFu;
|
||||
}else if(bitChosen == 8){
|
||||
//Slow Pause
|
||||
//submodelIndex = 10;
|
||||
|
||||
if(_1Count == 1){
|
||||
//force the original only.
|
||||
dataID = POWERUP_ID::SlowPause;
|
||||
}else{
|
||||
//I was picked of at least 1 other choice? Allow me to be the alternate too.
|
||||
if(random() < 0.5){
|
||||
dataID = POWERUP_ID::SlowPause;
|
||||
}else{
|
||||
dataID = POWERUP_ID::SlowPauseOld;
|
||||
}
|
||||
}
|
||||
}else if(bitChosen == 16){
|
||||
// Double Firerate
|
||||
//submodelIndex = 2;
|
||||
dataID = POWERUP_ID::DoubleFirerate;
|
||||
}else if(bitChosen == 32){
|
||||
//Grenade
|
||||
//submodelIndex = 6;
|
||||
dataID = POWERUP_ID::Grenade;
|
||||
}else if(bitChosen == 64){
|
||||
//Health
|
||||
//submodelIndex = 7;
|
||||
dataID = POWERUP_ID::Health;
|
||||
}else if(bitChosen == 128){
|
||||
//Armor
|
||||
//submodelIndex = 8;
|
||||
dataID = POWERUP_ID::Armor;
|
||||
}else if(bitChosen == 256){
|
||||
//Low gravity (or rather super jump)
|
||||
//submodelIndex = 9;
|
||||
dataID = POWERUP_ID::SuperJump;
|
||||
}
|
||||
|
||||
|
||||
}//END OF determineDataID
|
||||
|
||||
|
||||
void ts_powerup::declareDisposable(void){
|
||||
|
||||
// Since we clearly weren't spawned by the map for this method to be called,
|
||||
// we need to do what "Respawn" would. The would-be constructor stuff.
|
||||
CustomInit();
|
||||
|
||||
this.classname = "remove_me";
|
||||
|
||||
// TODO - decrease alpha slowly until the epiretime is up?
|
||||
// It's really bizarre original TS did that, but hey. We don't have to replicate every little detail,
|
||||
// or if we greatly prefer whatever working a different way.
|
||||
// ALSO - the # seconds is an assumtpion, original powerup expire time not known yet.
|
||||
this.expireTime = time + 30;
|
||||
|
||||
|
||||
// actually not needed. Things with the "remove_me" classname are deleted,
|
||||
// so there is never a chance to see this has the flag and respawn it.
|
||||
// Then again deletions can be delayed. Maybe it's not a problem if entities
|
||||
// with 'remove' called on them are exempt from searches in the same frame?
|
||||
// ehhh... let's just play it safe and do this then.
|
||||
|
||||
|
||||
// IAWEJGFAWEIFJAWEIA
|
||||
entityRemoveRespawnFlag(this);
|
||||
|
||||
|
||||
}//END OF declareDisposable
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CSQC
|
||||
|
||||
class ts_powerup:CBaseEntity{
|
||||
|
||||
//key values?
|
||||
pwuptype_choice m_ePwuptype;
|
||||
int m_iPwupduration;
|
||||
string m_strMessage;
|
||||
|
||||
// What member of ary_powerupData do I get my behavior/model/etc. from?
|
||||
POWERUP_ID dataID;
|
||||
BOOL alreadyPickedUp;
|
||||
|
||||
void(void) ts_powerup;
|
||||
virtual void(void) Initialized;
|
||||
virtual void(void) updateAnim;
|
||||
virtual void(string strField, string strKey) SpawnKey;
|
||||
|
||||
virtual float(void) predraw;
|
||||
//virtual void(void)Physics;
|
||||
//virtual void(void)Think;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void ts_powerup::ts_powerup(void){
|
||||
|
||||
}//END OF constructor
|
||||
|
||||
|
||||
// Use me instead for startup stuff expected after reading spawnflags.
|
||||
void ts_powerup::Initialized(void){
|
||||
//base method is empty, this is pointless
|
||||
//CBaseEntity::Initialized();
|
||||
|
||||
// until the server tells us!
|
||||
dataID = POWERUP_ID::NONE;
|
||||
|
||||
this.frame = 0;
|
||||
//this.baseframe = 0;
|
||||
|
||||
this.alreadyPickedUp = FALSE;
|
||||
|
||||
|
||||
this.classname = "ts_powerup";
|
||||
this.owner = world;
|
||||
|
||||
|
||||
//this.customphysics = Empty;
|
||||
// no use for customphysics, right?
|
||||
// so the client and server can do this? interesting.
|
||||
// looks like serverside is much more common though.
|
||||
// if we ever do customphysics, "frametime" may be good to use.
|
||||
// also, "runstandardplayerphysics(self)". the snark's a good reference.
|
||||
|
||||
// not helpful here unfortunately.
|
||||
//this.think = updateAnim;
|
||||
//this.nextthink = time + (31-1)/50;
|
||||
|
||||
//ts_powerup selfRef = this;
|
||||
|
||||
}//END OF Initialized
|
||||
|
||||
|
||||
|
||||
|
||||
void ts_powerup::updateAnim(void){
|
||||
|
||||
//this.lerpfrac = 1;
|
||||
//this.baseframe1time = 0;
|
||||
//this.frame1time = 0;
|
||||
//printfline("ts_powerup::updateAnim %0.2f", this.frame1time);
|
||||
//this.frame1time += clframetime;
|
||||
|
||||
//resets the anim frame.
|
||||
this.frame1time = 0;
|
||||
|
||||
|
||||
// Resetting the anim slightly before the marked frame end stops it from appearing to
|
||||
// stall near the end.
|
||||
// Unsure if this is a goldsource/FTE engine difference, but this was a nice exercise
|
||||
// in a server/client interaction with an entity (or it's representative clone?)
|
||||
// besides the player.
|
||||
// Decals are a good source for that too though.
|
||||
this.think = updateAnim;
|
||||
this.nextthink = time + (31-1)/20 - 0.005;
|
||||
//this.nextthink = time + 0.017;
|
||||
//this.nextthink = time + 0.0;
|
||||
|
||||
}//END OF restartAnim
|
||||
|
||||
|
||||
|
||||
|
||||
void Powerup_Parse(void){
|
||||
|
||||
ts_powerup selfRef;
|
||||
|
||||
/* convert us to an object of type decal */
|
||||
|
||||
// Keep in mind, this is just clientside. The server could really tell us to remove
|
||||
// ourselves if needed.
|
||||
if(self.classname != "ts_powerup"){
|
||||
//is this really persistent?
|
||||
//printfline("ts_powerup: Powerup_Parse. MY CLASSNAME IS INCORRECT: %s", self.classname);
|
||||
spawnfunc_ts_powerup();
|
||||
self.classname = "ts_powerup";
|
||||
}
|
||||
|
||||
selfRef = (ts_powerup)self;
|
||||
|
||||
BOOL mem_alreadyPickedUp = selfRef.alreadyPickedUp;
|
||||
|
||||
|
||||
|
||||
|
||||
// TEST. we probably don't need this.
|
||||
// oh, not possible for the client... go figure.
|
||||
//selfRef.pvsflags = PVSF_NOREMOVE;
|
||||
|
||||
selfRef.origin[0] = readcoord();
|
||||
selfRef.origin[1] = readcoord();
|
||||
selfRef.origin[2] = readcoord();
|
||||
|
||||
selfRef.angles[0] = readcoord();
|
||||
selfRef.angles[1] = readcoord();
|
||||
selfRef.angles[2] = readcoord();
|
||||
|
||||
selfRef.dataID = readbyte();
|
||||
selfRef.alreadyPickedUp = readbyte();
|
||||
BOOL isRespawnCall = readbyte();
|
||||
|
||||
//selfRef.dataID = readbyte(
|
||||
//setmodel( entTarget, NULL );
|
||||
//entTarget.modelindex = 0; //This may be redundant with above.
|
||||
|
||||
|
||||
//selfRef.modelIndex = 0;
|
||||
|
||||
//selfRef.lerpfrac = 1;
|
||||
|
||||
|
||||
|
||||
|
||||
selfRef.frame = 0;
|
||||
//selfRef.frame1time += frametime; ???
|
||||
//selfRef.frame1time = 0; resets the anim to frame 0.
|
||||
|
||||
|
||||
//selfRef.baseframe = 0;
|
||||
|
||||
//selfRef.baseframe1time += 0.1;
|
||||
|
||||
|
||||
if(isRespawnCall || (!selfRef.alreadyPickedUp && (mem_alreadyPickedUp != selfRef.alreadyPickedUp))){
|
||||
// forcively respawned, or appearing since being hidden? Force animation time to 0 (start at beginning)
|
||||
selfRef.frame1time = 0;
|
||||
selfRef.think = ts_powerup::updateAnim;
|
||||
selfRef.nextthink = time + (31-1)/20 - 0.005;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(!selfRef.alreadyPickedUp){
|
||||
|
||||
if(mem_alreadyPickedUp != selfRef.alreadyPickedUp){
|
||||
//... (script moved to above)
|
||||
}
|
||||
|
||||
// display as usual.
|
||||
setmodel( selfRef, POWERUP_MODEL_PATH);
|
||||
|
||||
powerupdata_basic_t powerupRef = *(ary_powerupData[selfRef.dataID]);
|
||||
int mySubmodel = powerupRef.iSubmodelChoice;
|
||||
|
||||
setcustomskin(selfRef, "", sprintf("geomset 0 %i", mySubmodel));
|
||||
}else{
|
||||
// Player touched me? Blank me!
|
||||
setmodel( selfRef, "" );
|
||||
selfRef.modelindex = 0;
|
||||
selfRef.think = NULL;
|
||||
selfRef.nextthink = 0;
|
||||
}
|
||||
|
||||
|
||||
//selfRef.customphysics = ts_powerup::Physics;
|
||||
//selfRef.think = ts_powerup::Think;
|
||||
//selfRef.nextthink = time + 0;
|
||||
|
||||
// unnecessary.
|
||||
//selfRef.predraw = ts_powerup::predraw;
|
||||
|
||||
// Why do we have to do this in Powerup_Parse and not just once in "Initialized?"
|
||||
selfRef.drawmask = MASK_ENGINE;
|
||||
|
||||
|
||||
}//END OF Powerup_Parse
|
||||
|
||||
|
||||
|
||||
float ts_powerup::predraw(void)
|
||||
{
|
||||
ts_powerup selfRef = (ts_powerup)self;
|
||||
|
||||
selfRef.frame1time += clframetime;
|
||||
|
||||
|
||||
//adddecal(selfRef.m_strShader, selfRef.origin, selfRef.mins, selfRef.maxs, selfRef.color, 1.0f);
|
||||
addentity(selfRef);
|
||||
return PREDRAW_NEXT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void ts_powerup::Physics(void){
|
||||
|
||||
this.frame1time += clframetime;
|
||||
|
||||
}//END OF Physics
|
||||
|
||||
void ts_powerup::Think(void){
|
||||
|
||||
this.frame1time += clframetime;
|
||||
this.nextthink = time + 0;
|
||||
|
||||
}//END OF Physics
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void ts_powerup::SpawnKey(string strField, string strKey)
|
||||
{
|
||||
//printfline("SpawnKey:: ts_powerup: (%s: %s)", strField, strKey);
|
||||
|
||||
switch (strField) {
|
||||
case "pwuptype":
|
||||
m_ePwuptype = stoi(strKey);
|
||||
break;
|
||||
case "pwupduration":
|
||||
m_iPwupduration = stoi(strKey);
|
||||
break;
|
||||
case "message":m_strMessage = strKey;
|
||||
break;
|
||||
|
||||
|
||||
// Just leave this disabled, only hinders us.
|
||||
// Way easier to force these things, constants work fine above.
|
||||
/*
|
||||
case "shader":
|
||||
//m_strSprite = strKey;
|
||||
//precache_pic(m_strSprite);
|
||||
//m_vecSize = drawgetimagesize(m_strSprite) / 2;
|
||||
break;
|
||||
case "model":
|
||||
//m_strSprite = sprintf("%s_0.tga", strKey);
|
||||
//m_vecSize = drawgetimagesize(m_strSprite) / 2;
|
||||
break;
|
||||
case "scale":
|
||||
//m_flScale = stof(strKey);
|
||||
break;
|
||||
case "rendercolor":
|
||||
case "rendercolour":
|
||||
//m_vecColor = stov(strKey) / 255;
|
||||
break;
|
||||
case "renderamt":
|
||||
//m_flMaxAlpha = stof(strKey) / 255;
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
CBaseEntity::SpawnKey(strField, strKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
0
src/server/entity/ts_slowmotion.qc
Normal file
0
src/server/entity/ts_slowmotion.qc
Normal file
0
src/server/entity/ts_slowmotionpoint.qc
Normal file
0
src/server/entity/ts_slowmotionpoint.qc
Normal file
0
src/server/entity/ts_teamescape.qc
Normal file
0
src/server/entity/ts_teamescape.qc
Normal file
0
src/server/entity/ts_trigger.qc
Normal file
0
src/server/entity/ts_trigger.qc
Normal file
0
src/server/entity/ts_wingiver.qc
Normal file
0
src/server/entity/ts_wingiver.qc
Normal file
7
src/server/entity/weapon_tsgun.qc
Normal file
7
src/server/entity/weapon_tsgun.qc
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
// ***NOTICE! This entity will probably go completely unused.
|
||||
// Likely never used by the map as it's not mentioned in ts_fgd.ffd,
|
||||
// and our own logic for weapons does not use an entity anyway.
|
||||
// This was likely used to represent a weapon in the player's
|
||||
// inventory in the original game, since TSWorldGun is clearly the
|
||||
// in-world pickup.
|
130
src/server/gamerules.h
Normal file
130
src/server/gamerules.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
// Blended from the old FreeTS Gamerules and FreeCS, some remnants of FreeHL may be
|
||||
// floating around. Most of the codebase is FreeHL inspired but this is an exception.
|
||||
|
||||
|
||||
|
||||
|
||||
extern TS_GameMode currentGameMode;
|
||||
|
||||
extern BOOL bRule_MoneyAllowed;
|
||||
extern int iRule_MaxWeightSlots;
|
||||
|
||||
extern BOOL bRule_GameMod_TheOne;
|
||||
extern BOOL bRule_GameMod_LastManStanding;
|
||||
|
||||
extern int iRule_teamCount;
|
||||
extern string sRule_TeamNames[4];
|
||||
|
||||
// Not keeping track of when to start the game,
|
||||
// for recalling when the game started after it has.
|
||||
extern float global_gameStartTime;
|
||||
extern float global_nextBreakableRespawn;
|
||||
|
||||
//not meant to be customizable... yet?
|
||||
#define BREAKABLE_RESPAWN_INTERVAL 60
|
||||
|
||||
|
||||
|
||||
//TAGGG - move someplace better perhaps?
|
||||
#define MAX_SPAWN_POINTS 32
|
||||
var entity ary_spawnStart_start[MAX_SPAWN_POINTS];
|
||||
var int ary_spawnStart_start_softMax = 0;
|
||||
var entity ary_spawnStart_deathmatch[MAX_SPAWN_POINTS];
|
||||
var int ary_spawnStart_deathmatch_softMax = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class player;
|
||||
|
||||
|
||||
class TSGameRules:CGameRules
|
||||
{
|
||||
virtual void(base_player) PlayerConnect;
|
||||
virtual void(base_player) PlayerDisconnect;
|
||||
virtual void(base_player) PlayerKill;
|
||||
virtual void(base_player) PlayerPreFrame;
|
||||
virtual void(base_player) PlayerPostFrame;
|
||||
virtual void(base_player) PlayerDeath;
|
||||
virtual void(base_player) PlayerPain;
|
||||
|
||||
virtual void(base_player) LevelDecodeParms;
|
||||
virtual void(base_player) LevelChangeParms;
|
||||
virtual void(void) LevelNewParms;
|
||||
};
|
||||
|
||||
class TSSingleplayerRules:TSGameRules
|
||||
{
|
||||
/* client */
|
||||
virtual void(base_player) PlayerSpawn;
|
||||
virtual void(base_player) PlayerDeath;
|
||||
};
|
||||
|
||||
class TSMultiplayerRules:TSGameRules
|
||||
{
|
||||
int m_iIntermission;
|
||||
int m_iIntermissionTime;
|
||||
|
||||
void() TSMultiplayerRules;
|
||||
|
||||
virtual void(void) RespawnMain;
|
||||
virtual void(void) RespawnRoutine;
|
||||
|
||||
virtual void(void) InitPostEnts;
|
||||
virtual void(void) FrameStart;
|
||||
|
||||
/* client */
|
||||
virtual void(base_player) PlayerDisconnect;
|
||||
virtual void(base_player) PlayerSpawn;
|
||||
virtual void(base_player) PlayerPreFrame;
|
||||
virtual void(base_player) PlayerPostFrame;
|
||||
virtual void(base_player) PlayerDeath;
|
||||
virtual float(base_player, string) ConsoleCommand;
|
||||
|
||||
virtual void(float, int) TimerBegin;
|
||||
virtual void(void) TimerUpdate;
|
||||
|
||||
|
||||
virtual void(int, int, int) RoundOver;
|
||||
virtual void(int) RestartRound;
|
||||
virtual void(base_player) DeathCheck;
|
||||
virtual void(void) CountPlayers;
|
||||
//virtual void(void) SwitchTeams;
|
||||
virtual void(void) TimeOut;
|
||||
|
||||
|
||||
virtual void(player pl) MakePlayerInvisible;
|
||||
virtual void(base_player) PlayerMakePlayable;
|
||||
virtual void(base_player) PlayerMakeSpectator;
|
||||
virtual void(base_player pp) PlayerMakeSpectatorDelayed;
|
||||
virtual void(base_player, int) PlayerRespawn;
|
||||
virtual entity(float) PlayerFindSpawn;
|
||||
|
||||
virtual void(player pl) setPlayerMoneyDefault;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
void Money_AddMoney(player ePlayer, int iMoneyValue);
|
||||
|
270
src/server/gamerules.qc
Normal file
270
src/server/gamerules.qc
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO! Should all these be instance vars of the gamerules object instead of
|
||||
// things floating around like this?
|
||||
|
||||
|
||||
//default if unspecified.
|
||||
//TODO - hook a bunch of this stuff up to console.
|
||||
|
||||
//Including a "changeteam" command if we implement teams.
|
||||
var TS_GameMode currentGameMode = TS_GameMode::DEATHMATCH;
|
||||
|
||||
|
||||
//TAGGG - INCLUSION - YOU BETTER NOT FORGET ABOUT THESE VARS.
|
||||
|
||||
//TAGGG TODO - there isn't one var / CVar to tell what gamemode this is? uhhh ok.
|
||||
//until something deeper is made like a gamemode struct or CVar, this will have to do.
|
||||
|
||||
//defaults. The "81" max weight slots rule is from The Specialists as-is.
|
||||
var BOOL bRule_MoneyAllowed = TRUE;
|
||||
var int iRule_MaxWeightSlots = 81;
|
||||
|
||||
var BOOL bRule_GameMod_TheOne = FALSE;
|
||||
var BOOL bRule_GameMod_LastManStanding = FALSE;
|
||||
|
||||
// Fill these from reading the player's team list. See notes in shared/rules.h about the
|
||||
// "team list" string in Create Server options (original game)
|
||||
var int iRule_teamCount = 0;
|
||||
var string sRule_TeamNames[4];
|
||||
|
||||
var float global_gameStartTime = 0;
|
||||
var float global_nextBreakableRespawn = 0;
|
||||
|
||||
|
||||
void
|
||||
TSGameRules::PlayerDeath(base_player pp)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TSGameRules::PlayerPain(base_player pp)
|
||||
{
|
||||
//TAGGG - why was this using "self" instead of the "pp"? haha.. the pp.
|
||||
// Also this is completely new to FreeCS and probably FreeHL. ?
|
||||
sound(pp, CHAN_VOICE, sprintf("player/pain%i.wav", randomInRange_i(1, 3)), 1, ATTN_IDLE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TAGGG - CRITICAL. Do any new player vars need to be copied too,
|
||||
// same for LevelChangeParams further down?
|
||||
void
|
||||
TSGameRules::LevelDecodeParms(base_player pp)
|
||||
{
|
||||
player pl = (player)pp;
|
||||
g_landmarkpos[0] = parm1;
|
||||
g_landmarkpos[1] = parm2;
|
||||
g_landmarkpos[2] = parm3;
|
||||
pl.angles[0] = parm4;
|
||||
pl.angles[1] = parm5;
|
||||
pl.angles[2] = parm6;
|
||||
pl.velocity[0] = parm7;
|
||||
pl.velocity[1] = parm8;
|
||||
pl.velocity[2] = parm9;
|
||||
pl.g_items = parm10;
|
||||
pl.activeweapon = parm11;
|
||||
pl.flags = parm64;
|
||||
|
||||
//TAGGG - why was this one missing from FreeHL? Present in FreeCS.
|
||||
pl.gflags = parm63;
|
||||
|
||||
/*
|
||||
pl.ammo_9mm = parm12;
|
||||
pl.ammo_357 = parm13;
|
||||
pl.ammo_buckshot = parm14;
|
||||
pl.ammo_m203_grenade = parm15;
|
||||
pl.ammo_bolt = parm16;
|
||||
pl.ammo_rocket = parm17;
|
||||
pl.ammo_uranium = parm18;
|
||||
pl.ammo_handgrenade = parm19;
|
||||
pl.ammo_satchel = parm20;
|
||||
pl.ammo_tripmine = parm21;
|
||||
pl.ammo_snark = parm22;
|
||||
pl.ammo_hornet = parm23;
|
||||
|
||||
pl.glock_mag = parm24;
|
||||
pl.mp5_mag = parm25;
|
||||
pl.python_mag = parm26;
|
||||
pl.shotgun_mag = parm27;
|
||||
pl.crossbow_mag = parm28;
|
||||
pl.rpg_mag = parm29;
|
||||
pl.satchel_chg = parm30;
|
||||
*/
|
||||
|
||||
|
||||
if (pl.flags & FL_CROUCHING) {
|
||||
setsize(pl, VEC_CHULL_MIN, VEC_CHULL_MAX);
|
||||
} else {
|
||||
setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TSGameRules::LevelChangeParms(base_player pp)
|
||||
{
|
||||
player pl = (player)pp;
|
||||
parm1 = g_landmarkpos[0];
|
||||
parm2 = g_landmarkpos[1];
|
||||
parm3 = g_landmarkpos[2];
|
||||
parm4 = pl.angles[0];
|
||||
parm5 = pl.angles[1];
|
||||
parm6 = pl.angles[2];
|
||||
parm7 = pl.velocity[0];
|
||||
parm8 = pl.velocity[1];
|
||||
parm9 = pl.velocity[2];
|
||||
|
||||
//TAGGG - why was this one missing from FreeHL? Present in FreeCS.
|
||||
parm63 = pl.gflags;
|
||||
|
||||
parm64 = pl.flags;
|
||||
parm10 = pl.g_items;
|
||||
parm11 = pl.activeweapon;
|
||||
|
||||
/*
|
||||
parm12 = pl.ammo_9mm;
|
||||
parm13 = pl.ammo_357;
|
||||
parm14 = pl.ammo_buckshot;
|
||||
parm15 = pl.ammo_m203_grenade;
|
||||
parm16 = pl.ammo_bolt;
|
||||
parm17 = pl.ammo_rocket;
|
||||
parm18 = pl.ammo_uranium;
|
||||
parm19 = pl.ammo_handgrenade;
|
||||
parm20 = pl.ammo_satchel;
|
||||
parm21 = pl.ammo_tripmine;
|
||||
parm22 = pl.ammo_snark;
|
||||
parm23 = pl.ammo_hornet;
|
||||
parm24 = pl.glock_mag;
|
||||
parm25 = pl.mp5_mag;
|
||||
parm26 = pl.python_mag;
|
||||
parm27 = pl.shotgun_mag;
|
||||
parm28 = pl.crossbow_mag;
|
||||
parm29 = pl.rpg_mag;
|
||||
parm30 = pl.satchel_chg;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TSGameRules::LevelNewParms(void)
|
||||
{
|
||||
//TAGGG - parm63 added, absent in FreeHL.
|
||||
parm1 = parm2 = parm3 = parm4 = parm5 = parm6 = parm7 =
|
||||
parm8 = parm9 = parm10 = parm11 = parm12 = parm13 = parm14 =
|
||||
parm15 = parm16 = parm17 = parm18 = parm19 = parm20 = parm21 =
|
||||
parm22 = parm23 = parm24 = parm25 = parm26 = parm27 = parm28 =
|
||||
parm29 = parm30 = parm63 = 0;
|
||||
parm64 = FL_CLIENT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//TAGGG - NEW. Need PlayerPreFrame as well.
|
||||
void
|
||||
TSGameRules::PlayerPreFrame(base_player pp)
|
||||
{
|
||||
//TAGGG - NEW
|
||||
// Is this null check redundant?
|
||||
if(pp != NULL){
|
||||
player pl = (player)pp;
|
||||
pl.preThink();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* we check what fields have changed over the course of the frame and network
|
||||
* only the ones that have actually changed */
|
||||
void
|
||||
TSGameRules::PlayerPostFrame(base_player pp)
|
||||
{
|
||||
//TAGGG - NEW
|
||||
// Is this null check redundant?
|
||||
if(pp != NULL){
|
||||
player pl = (player)pp;
|
||||
pl.postThink();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TSGameRules::PlayerConnect(base_player pl)
|
||||
{
|
||||
if (Plugin_PlayerConnect(pl) == FALSE)
|
||||
bprint(PRINT_HIGH, sprintf("%s connected\n", pl.netname));
|
||||
}
|
||||
|
||||
void
|
||||
TSGameRules::PlayerDisconnect(base_player pl)
|
||||
{
|
||||
if (Plugin_PlayerDisconnect(pl) == FALSE)
|
||||
bprint(PRINT_HIGH, sprintf("%s disconnected\n", pl.netname));
|
||||
|
||||
/* Make this unusable */
|
||||
pl.solid = SOLID_NOT;
|
||||
pl.movetype = MOVETYPE_NONE;
|
||||
pl.modelindex = 0;
|
||||
pl.health = 0;
|
||||
pl.takedamage = 0;
|
||||
|
||||
//TAGGG
|
||||
// FreeHL way.
|
||||
pl.SendFlags = -1;
|
||||
// why does FreeCS do this instead? Check for mentions of PLAYER_MODELINDEX
|
||||
// in FreeCS that aren't in FreeHL maybe.
|
||||
//pl.SendFlags = PLAYER_MODELINDEX;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TSGameRules::PlayerKill(base_player pl)
|
||||
{
|
||||
//because we are TS.
|
||||
//Damage_Apply(pl, pl, pl.health, WEAPON_NONE, DMG_SKIP_ARMOR);
|
||||
Damage_Apply(pl, pl, pl.health, WEAPON_ID::NONE, DMG_SKIP_ARMOR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
Money_AddMoney(player pl, int iMoneyValue)
|
||||
{
|
||||
//dprint(sprintf("^2Money_AddMoney^7: giving %s $%i\n", pl.netname, iMoneyValue));
|
||||
pl.money += iMoneyValue;
|
||||
|
||||
if (pl.money > autocvar_fcs_maxmoney) {
|
||||
pl.money = autocvar_fcs_maxmoney;
|
||||
}
|
||||
|
||||
if (pl.money < 0) {
|
||||
pl.money = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
1113
src/server/gamerules_multiplayer.qc
Normal file
1113
src/server/gamerules_multiplayer.qc
Normal file
File diff suppressed because it is too large
Load diff
116
src/server/gamerules_singleplayer.qc
Normal file
116
src/server/gamerules_singleplayer.qc
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
//TAGGG - Although TS is not very singleplayer-friendly, could this still use
|
||||
// some things like the corpse fade think call instead? Being a copy of FreeHL is just sloppy.
|
||||
// Heck just have stuff not dependent on being single/multiplayer moved to TSGameRules::PlayerDeath
|
||||
// instead, 99% of the stuff does not care about being multiplayer.
|
||||
|
||||
void
|
||||
TSSingleplayerRules::PlayerDeath(base_player pp)
|
||||
{
|
||||
TSGameRules::PlayerDeath(pp);
|
||||
|
||||
player pl = (player)pp;
|
||||
|
||||
pp.movetype = MOVETYPE_NONE;
|
||||
pp.solid = SOLID_NOT;
|
||||
pp.takedamage = DAMAGE_NO;
|
||||
|
||||
//pp.gflags &= ~GF_FLASHLIGHT;
|
||||
|
||||
//TAGGG - good idea too?
|
||||
pl.setInventoryEquippedIndex(-1);
|
||||
|
||||
pp.armor = pp.activeweapon = pp.g_items = pp.weapon = 0;
|
||||
pp.health = 0;
|
||||
Sound_Play(pp, CHAN_AUTO, "player.die");
|
||||
|
||||
if (cvar("coop") == 1) {
|
||||
pp.think = PutClientInServer;
|
||||
pp.nextthink = time + 4.0f;
|
||||
}
|
||||
|
||||
if (pp.health < -50) {
|
||||
FX_GibHuman(pp.origin);
|
||||
}
|
||||
|
||||
/* Let's handle corpses on the clientside */
|
||||
entity corpse = spawn();
|
||||
setorigin(corpse, pp.origin + [0,0,32]);
|
||||
setmodel(corpse, pp.model);
|
||||
setsize(corpse, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
corpse.movetype = MOVETYPE_TOSS;
|
||||
corpse.solid = SOLID_TRIGGER;
|
||||
corpse.modelindex = pp.modelindex;
|
||||
corpse.frame = ANIM_DIESIMPLE;
|
||||
corpse.angles = pp.angles;
|
||||
corpse.velocity = pp.velocity;
|
||||
}
|
||||
|
||||
void
|
||||
TSSingleplayerRules::PlayerSpawn(base_player pp)
|
||||
{
|
||||
pp.classname = "player";
|
||||
pp.health = pp.max_health = 100;
|
||||
pp.takedamage = DAMAGE_YES;
|
||||
pp.solid = SOLID_SLIDEBOX;
|
||||
pp.movetype = MOVETYPE_WALK;
|
||||
pp.flags = FL_CLIENT;
|
||||
pp.viewzoom = 1.0;
|
||||
pp.model = "models/player.mdl";
|
||||
|
||||
|
||||
if (cvar("coop") == 1) {
|
||||
string mymodel = infokey(pp, "model");
|
||||
if (mymodel) {
|
||||
mymodel = sprintf("models/player/%s/%s.mdl", mymodel, mymodel);
|
||||
if (whichpack(mymodel)) {
|
||||
pp.model = mymodel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setmodel(pp, pp.model);
|
||||
|
||||
setsize(pp, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
pp.velocity = [0,0,0];
|
||||
pp.gravity = __NULL__;
|
||||
pp.frame = 1;
|
||||
//pp.SendEntity = Player_SendEntity;
|
||||
pp.SendFlags = UPDATE_ALL;
|
||||
pp.customphysics = Empty;
|
||||
pp.iBleeds = TRUE;
|
||||
forceinfokey(pp, "*spec", "0");
|
||||
forceinfokey(pp, "*deaths", ftos(pp.deaths));
|
||||
|
||||
/* this is where the mods want to deviate */
|
||||
entity spot;
|
||||
|
||||
if (startspot != "") {
|
||||
dprint(sprintf("^3Gamerules_Spawn^7: Startspot is %s\n", startspot));
|
||||
LevelDecodeParms(pp);
|
||||
setorigin(pp, Landmark_GetSpot());
|
||||
} else {
|
||||
LevelNewParms();
|
||||
spot = find(world, ::classname, "info_player_start");
|
||||
setorigin(pp, spot.origin);
|
||||
pp.angles = spot.angles;
|
||||
}
|
||||
|
||||
Weapons_RefreshAmmo(pp);
|
||||
Client_FixAngle(pp, pp.angles);
|
||||
}
|
315
src/server/init.qc
Normal file
315
src/server/init.qc
Normal file
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
//TAGGG - file renamed from server.qc to init.qc.
|
||||
// Has only init methods like client/init.qc, so naming after that.
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
// DEMO! So 'enumflags' does mean they count up in powers of 2,
|
||||
// so starting at 1, then 2, 4, 8, 16, 32, etc.
|
||||
// (starting at 1 since 2^0 is 1; each spot is 2^i)
|
||||
// typedef + name after not required, but do-able.
|
||||
// And floats because FTE. Or Quake C, one of those.
|
||||
typedef enumflags
|
||||
{
|
||||
TEST_ENUM_1,
|
||||
TEST_ENUM_2,
|
||||
TEST_ENUM_3,
|
||||
TEST_ENUM_4,
|
||||
TEST_ENUM_5
|
||||
|
||||
} myFlags_t;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void Game_Spawn_ObserverCam(player pl){
|
||||
|
||||
//printfline("Game_Spawn_ObserverCam: ary_spawnStart_start_softMax: %i", ary_spawnStart_start_softMax);
|
||||
if(ary_spawnStart_start_softMax == 0){
|
||||
printfline("Game_Spawn_ObserverCam: no recorded spawn locations");
|
||||
|
||||
//printfline("MY VALS! %d %d %d %d %d", TEST_ENUM_1, TEST_ENUM_2, TEST_ENUM_3, TEST_ENUM_4, TEST_ENUM_5);
|
||||
|
||||
initSpawnMem();
|
||||
}
|
||||
|
||||
// force randomSpawnChoice to a constant to spawn in the same place always if needed
|
||||
int randomSpawnChoice = randomInRange_i(0, ary_spawnStart_deathmatch_softMax-1);
|
||||
entity eCamera = ary_spawnStart_deathmatch[randomSpawnChoice];
|
||||
|
||||
entity eTarget;
|
||||
|
||||
if (eCamera) {
|
||||
pl.origin = eCamera.origin;
|
||||
|
||||
if (eCamera.target) {
|
||||
eTarget = find(world, ::targetname, eCamera.target);
|
||||
if (eTarget) {
|
||||
pl.angles = vectoangles(eTarget.origin - eCamera.origin);
|
||||
pl.angles[0] *= -1;
|
||||
}
|
||||
}else{
|
||||
// no target? What else do we have?
|
||||
//entity eTarget;
|
||||
//self.angles = eCamera.angles;
|
||||
pl.angles = eCamera.angles;
|
||||
// Why do we have to do this now? No clue.
|
||||
// something about animation.h maybe? I Forget.
|
||||
pl.v_angle = pl.angles;
|
||||
Client_FixAngle(pl, pl.angles);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Can't find a camera? Just do this lazy thing, CS seems to do the same
|
||||
eCamera = find (world, ::classname, "info_player_start");
|
||||
|
||||
if (eCamera) {
|
||||
pl.origin = eCamera.origin;
|
||||
|
||||
if (eCamera.target) {
|
||||
eTarget = find(world, ::targetname, eCamera.target);
|
||||
if (eTarget) {
|
||||
pl.angles = vectoangles(eTarget.origin - eCamera.origin);
|
||||
pl.angles[0] *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TAGGG - NEW LINE. do we need to do this?
|
||||
setorigin(pl, pl.origin);
|
||||
Client_FixAngle(pl, pl.angles);
|
||||
|
||||
}//END OF Game_Spawn_ObserverCam
|
||||
|
||||
|
||||
|
||||
//TAGGG - NOTE. Should this get a 0.1 delay like nuclide's src/server/entry.qc does here:
|
||||
// entity respawntimer = spawn();
|
||||
// respawntimer.think = init_respawn;
|
||||
// respawntimer.nextthink = time + 0.1f;
|
||||
// Might not be necessary. Or compare better with old FreeTS, just in case the new way of calling
|
||||
// (straight from Game_Worldspawn) doesn't work.
|
||||
// This needs the other info_<whatever> entities to exist for keeping track of places to spawn,
|
||||
// being incomplete at the time of this call is unacceptable.
|
||||
|
||||
void initSpawnMem(void){
|
||||
//printfline("initSpawnMem");
|
||||
// two spawn-related entities present in the map:
|
||||
// info_player_start
|
||||
// info_player_deathmatch
|
||||
|
||||
ary_spawnStart_start_softMax = 0;
|
||||
ary_spawnStart_deathmatch_softMax = 0;
|
||||
|
||||
|
||||
for(entity eFind = world; (eFind = find(eFind, classname, "info_player_start"));){
|
||||
if(ary_spawnStart_start_softMax == MAX_SPAWN_POINTS){
|
||||
printfline("ERROR!! Map exceeds maximum of %d info_player_start points. Further ones ignored.", MAX_SPAWN_POINTS);
|
||||
break;
|
||||
}
|
||||
//printfline("info_player_start: found one: %s", eFind.classname);
|
||||
|
||||
ary_spawnStart_start[ary_spawnStart_start_softMax] = eFind;
|
||||
ary_spawnStart_start_softMax++;
|
||||
}
|
||||
|
||||
|
||||
for(entity eFind = world; (eFind = find(eFind, classname, "info_player_deathmatch"));){
|
||||
if(ary_spawnStart_deathmatch_softMax == MAX_SPAWN_POINTS){
|
||||
printfline("ERROR!! Map exceeds maximum of %d info_player_deathmatch points. Further ones ignored.", MAX_SPAWN_POINTS);
|
||||
break;
|
||||
}
|
||||
|
||||
//printfline("info_player_deathmatch: found %s", eFind.classname);
|
||||
|
||||
ary_spawnStart_deathmatch[ary_spawnStart_deathmatch_softMax] = eFind;
|
||||
ary_spawnStart_deathmatch_softMax++;
|
||||
}
|
||||
|
||||
printfline("initSpawnMem: how many spawns, info_p_start:%i info_p_deathmatch:%i", ary_spawnStart_start_softMax, ary_spawnStart_deathmatch_softMax);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
Game_InitRules(void)
|
||||
{
|
||||
if (cvar("sv_playerslots") == 1 || cvar("coop") == 1) {
|
||||
g_grMode = spawn(TSSingleplayerRules);
|
||||
} else {
|
||||
g_grMode = spawn(TSMultiplayerRules);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Effectvely, ServerGame_Init
|
||||
void
|
||||
Game_Worldspawn(void)
|
||||
{
|
||||
printfline("***ServerGame_Init (Game_Worldspawn) called***");
|
||||
//TAGGG - NEW
|
||||
SharedGame_Init();
|
||||
ServerGame_Precache();
|
||||
|
||||
// calls each weapon's .precache method.
|
||||
Weapons_Init();
|
||||
Player_Precache();
|
||||
|
||||
|
||||
//TAGGG - new stuff
|
||||
|
||||
string sTemp;
|
||||
int iMOTDLines = 0;
|
||||
|
||||
|
||||
printfline("Game_Worldspawn");
|
||||
|
||||
|
||||
// The message of the day.
|
||||
// This saves the contents of the MoTD text file (path given by CVar motdfile) to a series of
|
||||
// serverinfo entries named motdline0, 1, 2, ... for reading back in client/vgui.c from the server
|
||||
// at client-connect time (CSQC_VGUI_Init) to go in the MoTD window whenever needed.
|
||||
localcmd(sprintf("echo [MOTD] Loading %s.\n", autocvar_motdfile));
|
||||
filestream fmMOTD = fopen(autocvar_motdfile, FILE_READ);
|
||||
if (fmMOTD >= 0) {
|
||||
for (int i = 0; i < 25; i++) {
|
||||
sTemp = fgets(fmMOTD);
|
||||
|
||||
if not (sTemp) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sTemp == NULL) {
|
||||
localcmd(sprintf("serverinfo motdline%i /\n", iMOTDLines));
|
||||
} else {
|
||||
localcmd(sprintf("serverinfo motdline%i %s\n", iMOTDLines, sTemp));
|
||||
}
|
||||
iMOTDLines++;
|
||||
}
|
||||
localcmd(sprintf("serverinfo motdlength %i\n", iMOTDLines));
|
||||
fclose(fmMOTD);
|
||||
} else {
|
||||
error("[MOTD] Loading failed.\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TAGGG - REMOVED! Nuclide now handles mapcycle
|
||||
/*
|
||||
// The mapcycle information.
|
||||
localcmd(sprintf("echo [MAPCYCLE] Loading %s.\n", autocvar_mapcyclefile));
|
||||
filestream fmMapcycle = fopen(autocvar_mapcyclefile, FILE_READ);
|
||||
|
||||
if (fmMapcycle >= 0) {
|
||||
for (int i = 0;; i++) {
|
||||
sTemp = fgets(fmMapcycle);
|
||||
if not (sTemp) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sTemp != NULL) {
|
||||
iMapCycleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
fseek(fmMapcycle, 0);
|
||||
localcmd(sprintf("echo [MAPCYCLE] List has %i maps.\n", iMapCycleCount));
|
||||
sMapCycle = memalloc(sizeof(string) * iMapCycleCount);
|
||||
for (int i = 0; i < iMapCycleCount; i++) {
|
||||
sMapCycle[i] = fgets(fmMapcycle);
|
||||
}
|
||||
fclose(fmMapcycle);
|
||||
|
||||
for (int i = 0; i < iMapCycleCount; i++) {
|
||||
if (sMapCycle[i] == mapname) {
|
||||
if ((i + 1) < iMapCycleCount) {
|
||||
localcmd(sprintf("echo [MAPCYCLE] Next map: %s\n", sMapCycle[i + 1]));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iMapCycleCount = 0;
|
||||
error("[MAPCYCLE] Loading failed.\n");
|
||||
}
|
||||
*/
|
||||
|
||||
// Let's make our version information clear
|
||||
localcmd(sprintf("serverinfo fcs_ver %s\n", __DATE__));
|
||||
|
||||
|
||||
/*Bot_Init();*/
|
||||
|
||||
|
||||
//How many weight slots does this player have?
|
||||
//...oh wait, we already receive whatever the actual playerSlots is, even if the inventorylogic
|
||||
// clientside never had to refer to it.
|
||||
// That is, pl.iTotalSlots is set by serverside when we spawn with a config (filled by whatever
|
||||
// weapons are applied).
|
||||
// Then, serverside pl.iTotalSlots gets written to clientside pl.iTotalSlots too each frame.
|
||||
//pointerstat(STAT_WEIGHTSLOTS, EV_FLOAT, iTotalSlots);
|
||||
|
||||
|
||||
//TAGGG - INCLUSION. NEW.
|
||||
pointerstat(STAT_RULE_MONEYALLOWED, EV_INTEGER, &bRule_MoneyAllowed);
|
||||
pointerstat(STAT_RULE_MAXWEIGHTSLOTS, EV_INTEGER, &iRule_MaxWeightSlots);
|
||||
|
||||
//iBombRadius = 1024;
|
||||
localcmd(sprintf("serverinfo slots %d\n", cvar("sv_playerslots")));
|
||||
localcmd("teamplay 1\n");
|
||||
|
||||
//TAGGG - shouldn't these get explicit defaults?
|
||||
g_ts_gamestate = GAME_INACTIVE;
|
||||
g_ts_gametime = 0;
|
||||
|
||||
|
||||
// Go ahead, setup some info about all possible locations for players to spawn.
|
||||
// All entities have loaded at this point, right? Sure they have for this to be Game_Worldspawn,
|
||||
// see what calls that if uncertain from nuclide.
|
||||
initSpawnMem();
|
||||
|
||||
|
||||
//TAGGG - 2020update?
|
||||
/*
|
||||
// and what do these mean anyway? Search game files, maybe something tells what "buy.kevlar" means?
|
||||
// how to derive that to some sound file I assume?
|
||||
Sound_Precache("buy.kevlar");
|
||||
Sound_Precache("buy.weapon");
|
||||
Sound_Precache("buy.ammo");
|
||||
|
||||
Weapons_Init();
|
||||
|
||||
clientstat(STAT_MONEY, EV_INTEGER, player::money);
|
||||
*/
|
||||
|
||||
clientstat(STAT_MONEY, EV_INTEGER, player::money);
|
||||
pointerstat(STAT_GAMETIME, EV_FLOAT, &g_ts_gametime);
|
||||
pointerstat(STAT_GAMESTATE, EV_INTEGER, &g_ts_gamestate);
|
||||
|
||||
|
||||
}
|
30
src/server/items.h
Normal file
30
src/server/items.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* PICKUP ITEMS */
|
||||
class item_pickup:CBaseTrigger
|
||||
{
|
||||
int m_bFloating;
|
||||
int m_iClip;
|
||||
int m_iWasDropped;
|
||||
int id;
|
||||
void(void) item_pickup;
|
||||
|
||||
virtual void(void) touch;
|
||||
virtual void(int i) SetItem;
|
||||
virtual void(void) Respawn;
|
||||
virtual void(int) SetFloating;
|
||||
};
|
88
src/server/items.qc
Normal file
88
src/server/items.qc
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void item_pickup::touch(void)
|
||||
{
|
||||
if (other.classname != "player") {
|
||||
return;
|
||||
}
|
||||
|
||||
/* don't remove if AddItem fails */
|
||||
if (Weapons_AddItem((player)other, id, m_iClip) == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging_Pickup(other, this, __NULL__);
|
||||
Sound_Play(other, CHAN_ITEM, "weapon.pickup");
|
||||
|
||||
UseTargets(other, TRIG_TOGGLE, m_flDelay);
|
||||
|
||||
if (real_owner || m_iWasDropped == 1 || cvar("sv_playerslots") == 1) {
|
||||
remove(self);
|
||||
} else {
|
||||
Hide();
|
||||
think = Respawn;
|
||||
nextthink = time + 30.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void item_pickup::SetItem(int i)
|
||||
{
|
||||
id = i;
|
||||
m_oldModel = Weapons_GetWorldmodel(id);
|
||||
SetModel(m_oldModel);
|
||||
SetSize([-16,-16,0], [16,16,16]);
|
||||
}
|
||||
|
||||
void item_pickup::SetFloating(int i)
|
||||
{
|
||||
m_bFloating = rint(bound(0, m_bFloating, 1));
|
||||
}
|
||||
|
||||
void item_pickup::Respawn(void)
|
||||
{
|
||||
SetSolid(SOLID_TRIGGER);
|
||||
SetOrigin(m_oldOrigin);
|
||||
|
||||
/* At some points, the item id might not yet be set */
|
||||
if (m_oldModel) {
|
||||
SetModel(m_oldModel);
|
||||
}
|
||||
|
||||
SetSize([-16,-16,0], [16,16,16]);
|
||||
|
||||
think = __NULL__;
|
||||
nextthink = -1;
|
||||
|
||||
if (!m_iWasDropped && cvar("sv_playerslots") > 1) {
|
||||
if (!real_owner && time > 30.0f)
|
||||
Sound_Play(this, CHAN_ITEM, "item.respawn");
|
||||
|
||||
m_iClip = -1;
|
||||
}
|
||||
|
||||
if (!m_bFloating) {
|
||||
droptofloor();
|
||||
SetMovetype(MOVETYPE_TOSS);
|
||||
}
|
||||
}
|
||||
|
||||
void item_pickup::item_pickup(void)
|
||||
{
|
||||
Sound_Precache("item.respawn");
|
||||
Sound_Precache("weapon.pickup");
|
||||
CBaseTrigger::CBaseTrigger();
|
||||
}
|
18
src/server/player.h
Normal file
18
src/server/player.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void Player_UseDown(void);
|
||||
void Player_UseUp(void);
|
185
src/server/player.qc
Normal file
185
src/server/player.qc
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
====================
|
||||
UseWorkaround
|
||||
====================
|
||||
*/
|
||||
void UseWorkaround(entity eTarget)
|
||||
{
|
||||
eActivator = self;
|
||||
entity eOldSelf = self;
|
||||
self = eTarget;
|
||||
self.PlayerUse();
|
||||
self = eOldSelf;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
Player_UseDown
|
||||
====================
|
||||
*/
|
||||
void Player_UseDown(void)
|
||||
{
|
||||
vector vecSrc;
|
||||
|
||||
//TAGGG - CHECK. Does the "self.flags & FL_USE_RELEASED" check need to be commented out?
|
||||
// Old way has it that way
|
||||
if (self.health <= 0) {
|
||||
return;
|
||||
} else if (!(self.flags & FL_USE_RELEASED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
makevectors(self.v_angle);
|
||||
vecSrc = self.origin + self.view_ofs;
|
||||
|
||||
|
||||
player pl = (player)self;
|
||||
|
||||
//TAGGG - modern FreeHL way. Replacing it with my own completely for now
|
||||
/*
|
||||
self.hitcontentsmaski = CONTENTBITS_POINTSOLID;
|
||||
traceline(vecSrc, vecSrc + (v_forward * 64), MOVE_HITMODEL, self);
|
||||
|
||||
|
||||
if (trace_ent.PlayerUse) {
|
||||
self.flags &= ~FL_USE_RELEASED;
|
||||
|
||||
UseWorkaround(trace_ent);
|
||||
|
||||
// Some entities want to support Use spamming
|
||||
if (!(self.flags & FL_USE_RELEASED)) {
|
||||
sound(self, CHAN_ITEM, "common/wpn_select.wav", 0.25, ATTN_IDLE);
|
||||
}
|
||||
} else {
|
||||
sound(self, CHAN_ITEM, "common/wpn_denyselect.wav", 0.25, ATTN_IDLE);
|
||||
self.flags &= ~FL_USE_RELEASED;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
BOOLEAN isPickup;
|
||||
|
||||
//TAGGG - point of interest!
|
||||
// Old way for the 2nd to last param here was MOVE_NOMONSTERS (1).
|
||||
// FreeHL's way is now MOVE_HITMODEL. Trying that out, revert if needed
|
||||
traceline ( vecSrc, vecSrc + ( v_forward * 36 ), MOVE_HITMODEL, self );
|
||||
|
||||
entity ef = findradius(trace_endpos, 40);
|
||||
while (ef) {
|
||||
//printfline("An entity found: %s has use? %d", ef.classname, (ef.PlayerUse!=NULL) );
|
||||
|
||||
if (ef.PlayerUse != NULL) {
|
||||
|
||||
|
||||
vector dirTowards = normalize(ef.origin - self.origin);
|
||||
|
||||
//TODO - how do we obtain this. v_forward from make angles??
|
||||
vector dirFacing = v_forward;
|
||||
|
||||
// cheap check for now, this is the only combo that has both
|
||||
// a class of "remove_me" and a 'PlayerUse' method set.
|
||||
isPickup = (ef.classname == "remove_me");
|
||||
|
||||
//isPickup = FALSE;
|
||||
|
||||
if(isPickup){
|
||||
//woohoo.
|
||||
//just keep trying use on anything we can.
|
||||
UseWorkaround(ef);
|
||||
//return;
|
||||
}else{
|
||||
float prod = dotproduct(dirTowards, dirFacing);
|
||||
float dist = vlen(ef.origin - self.origin);
|
||||
//printfline("use dotprod:%.2f dist:%.2f", prod, dist);
|
||||
if(prod >= 0.97 && dist < 52){
|
||||
|
||||
UseWorkaround(ef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
ef = ef.chain;
|
||||
}
|
||||
|
||||
|
||||
//printfline("self.mins.z? %.2f", self.mins.z);
|
||||
//search around where the player is standing too.
|
||||
ef = findradius(self.origin + [0,0,self.mins.z], 30);
|
||||
while (ef) {
|
||||
if (ef.PlayerUse != NULL) {
|
||||
|
||||
|
||||
isPickup = (ef.classname == "remove_me");
|
||||
|
||||
//isPickup = FALSE;
|
||||
|
||||
if(isPickup){
|
||||
|
||||
UseWorkaround(ef);
|
||||
}
|
||||
//return;
|
||||
}
|
||||
ef = ef.chain;
|
||||
}
|
||||
|
||||
|
||||
self.flags &= ~FL_USE_RELEASED;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
Player_UseUp
|
||||
====================
|
||||
*/
|
||||
void Player_UseUp(void) {
|
||||
if (!(self.flags & FL_USE_RELEASED)) {
|
||||
self.flags |= FL_USE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
// check out TS_playerEquippedWeapon, it already does this
|
||||
/*
|
||||
//TAGGG - CRITICAL! Make this work better with the inventory system.
|
||||
// Changing activeweapon directly is meaningless to us, changing
|
||||
/// inventoryEquippedIndex makes more sense, then activeweapon from that
|
||||
// ...unless we trust that is handled anyway, but I doubt that.
|
||||
void CSEv_PlayerSwitchWeapon_i(int w)
|
||||
{
|
||||
player pl = (player)self;
|
||||
|
||||
//pl.activeweapon = w;
|
||||
pl.setInventoryEquippedIndex(w);
|
||||
Weapons_Draw();
|
||||
}
|
||||
*/
|
||||
|
||||
void
|
||||
Player_Precache(void)
|
||||
{
|
||||
searchhandle pm;
|
||||
pm = search_begin("models/player/*/*.mdl", TRUE, TRUE);
|
||||
for (int i = 0; i < search_getsize(pm); i++) {
|
||||
precache_model(search_getfilename(pm, i));
|
||||
}
|
||||
search_end(pm);
|
||||
}
|
2
src/server/precache.h
Normal file
2
src/server/precache.h
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
void ServerGame_Precache(void);
|
142
src/server/precache.qc
Normal file
142
src/server/precache.qc
Normal file
|
@ -0,0 +1,142 @@
|
|||
|
||||
|
||||
|
||||
void ServerGame_Precache(void){
|
||||
printfline("***ServerGame_Precache called***");
|
||||
SharedGame_Precache();
|
||||
|
||||
|
||||
//TAGGG - From FreeHL. Anything that ends up unused by FreeTS can be removed,
|
||||
// including sound config files that are then unused.
|
||||
////////////////////////////////////////////////////////////
|
||||
Sound_Precache("player.die");
|
||||
Sound_Precache("player.die_headshot");
|
||||
Sound_Precache("player.fall");
|
||||
Sound_Precache("player.lightfall");
|
||||
|
||||
precache_model("models/player.mdl");
|
||||
// no.
|
||||
//precache_model("models/w_weaponbox.mdl");
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
//precache_sound("weapons/pistol-empty.wav");
|
||||
precache_sound("weapons/fnh/fnh-fire.wav");
|
||||
|
||||
//counterstikre leftovers... remove ASAP.
|
||||
precache_sound("weapons/knife_slash1.wav");
|
||||
precache_sound("weapons/knife_slash2.wav");
|
||||
precache_sound("weapons/ak47-1.wav");
|
||||
precache_sound("weapons/ak47-2.wav");
|
||||
|
||||
precache_model("models/powerup.mdl");
|
||||
|
||||
|
||||
|
||||
// player models in cstrike are precached
|
||||
// serverside only, so we can too I suppose
|
||||
precache_model("models/player/agent/agent.mdl");
|
||||
precache_model("models/player/castor/castor.mdl");
|
||||
precache_model("models/player/gordon/gordon.mdl");
|
||||
precache_model("models/player/hitman/hitman.mdl");
|
||||
precache_model("models/player/laurence/laurence.mdl");
|
||||
precache_model("models/player/merc/merc.mdl");
|
||||
precache_model("models/player/seal/seal.mdl");
|
||||
|
||||
|
||||
|
||||
|
||||
for(int i = 1; i < WEAPON_ID::LAST_ID; i++){
|
||||
if(ary_weaponData[i] != NULL){
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_weaponData[i];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
if(basicRef.sViewModelPath != NULL){
|
||||
precache_model(basicRef.sViewModelPath);
|
||||
}
|
||||
if(basicRef.sPlayerModelPath != NULL){
|
||||
precache_model(basicRef.sPlayerModelPath);
|
||||
}
|
||||
if(basicRef.sPlayerSilencerModelPath != NULL){
|
||||
precache_model(basicRef.sPlayerSilencerModelPath);
|
||||
}
|
||||
if(basicRef.sWorldModelPath != NULL){
|
||||
//printfline("I PRECACHED THIS %s", basicRef.sWorldModelPath);
|
||||
precache_model(basicRef.sWorldModelPath);
|
||||
}
|
||||
//precache_model(basicRef.sIconFilePath);
|
||||
}
|
||||
}
|
||||
for(int i = 1; i < WEAPON_AKIMBO_UPGRADE_ID::LAST_ID; i++){
|
||||
if(ary_akimboUpgradeData[i] != NULL){
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_akimboUpgradeData[i];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
if(basicRef.sViewModelPath != NULL){
|
||||
precache_model(basicRef.sViewModelPath);
|
||||
}
|
||||
if(basicRef.sPlayerModelPath != NULL){
|
||||
precache_model(basicRef.sPlayerModelPath);
|
||||
}
|
||||
if(basicRef.sPlayerSilencerModelPath != NULL){
|
||||
precache_model(basicRef.sPlayerSilencerModelPath);
|
||||
}
|
||||
if(basicRef.sWorldModelPath != NULL){
|
||||
//printfline("I PRECACHED THIS %s", basicRef.sWorldModelPath);
|
||||
precache_model(basicRef.sWorldModelPath);
|
||||
}
|
||||
//precache_model(basicRef.sIconFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// do 3D models too.
|
||||
precache_model("models/ammopack.mdl");
|
||||
|
||||
|
||||
precache_model("sprites/glow01.spr");
|
||||
|
||||
|
||||
|
||||
|
||||
precache_sound("player/die1.wav");
|
||||
precache_sound("player/die2.wav");
|
||||
precache_sound("player/die3.wav");
|
||||
precache_sound("player/headshot.wav");
|
||||
precache_sound("player/headshot2.wav");
|
||||
precache_sound("player/bodyhit1.wav");
|
||||
precache_sound("player/bodyhit2.wav");
|
||||
precache_sound("player/bodyhit3.wav");
|
||||
precache_sound("player/bodyhit4.wav");
|
||||
precache_sound("player/pain1.wav");
|
||||
precache_sound("player/pain2.wav");
|
||||
precache_sound("player/pain3.wav");
|
||||
precache_sound("player/pain4.wav");
|
||||
|
||||
|
||||
|
||||
precache_sound("weapons/grenbounce1.wav");
|
||||
precache_sound("weapons/grenbounce2.wav");
|
||||
precache_sound("weapons/grenbounce3.wav");
|
||||
precache_sound("weapons/grenbounce4.wav");
|
||||
|
||||
precache_sound("explo/explode.wav");
|
||||
precache_sound("explo/explode1.wav");
|
||||
precache_sound("explo/explode2.wav");
|
||||
precache_sound("explo/explode3.wav");
|
||||
precache_sound("explo/explode4.wav");
|
||||
|
||||
precache_sound("goslow.wav");
|
||||
|
||||
|
||||
//Other weapons related... should this be in "shared" instead?
|
||||
precache_sound("weapons/ak47/fire.wav");
|
||||
precache_sound("weapons/aug/aug-fire.wav");
|
||||
precache_sound("weapons/aug/aug-fire-sil.wav");
|
||||
precache_sound("weapons/barrett/fire.wav");
|
||||
//...ETC
|
||||
|
||||
|
||||
}
|
132
src/server/progs.src
Normal file
132
src/server/progs.src
Normal file
|
@ -0,0 +1,132 @@
|
|||
#pragma target fte
|
||||
#pragma progs_dat "../../progs.dat"
|
||||
|
||||
#define QWSSQC
|
||||
#define SERVER
|
||||
#define TS
|
||||
|
||||
//TAGGG - NEW. TS definitely has penetration, and so should this then.
|
||||
#define BULLETPENETRATION
|
||||
//TAGGG - NEW. FreeCS also has this. Do we want/need it? Keeping for now.
|
||||
// NEVERMIND, requires a new var "cs_shotmultiplier" and any other prediction-related tie-ins as seen
|
||||
// in freeCS's shared/player.qc.
|
||||
// Would like to know what BULLETPATTERNS even does to see if FreeTS had anything like that.
|
||||
//#define BULLETPATTERNS
|
||||
|
||||
#define GS_RENDERFX
|
||||
|
||||
|
||||
|
||||
// FORCE A SPAWN LOC. Sometimes easier for testing to have a constant set of spawn coords/angle.
|
||||
// Use with the right start params to jump into the map at debug.
|
||||
|
||||
//#define TS_CUSTOM_SPAWN_ORIGIN '21 -1291 -380'
|
||||
//#define TS_CUSTOM_SPAWN_ORIGIN '-754 569 -380'
|
||||
|
||||
// THE POOL
|
||||
#define TS_CUSTOM_SPAWN_ORIGIN '-458.5 957.7 -380.0'
|
||||
#define TS_CUSTOM_SPAWN_VANGLE [0.0, 45.0, 0.0]
|
||||
|
||||
// in the house
|
||||
//#define TS_CUSTOM_SPAWN_ORIGIN [93.1, -80.0, -380.0]
|
||||
//#define TS_CUSTOM_SPAWN_VANGLE [0.0, 90.0, 0.0]
|
||||
|
||||
|
||||
// ts_awaken: the ledge thingy.
|
||||
//#define TS_CUSTOM_SPAWN_ORIGIN [12.4, -1350.6, -292.0]
|
||||
//#define TS_CUSTOM_SPAWN_VANGLE [-12.5, 0.5, 0.0]
|
||||
|
||||
|
||||
|
||||
|
||||
#includelist
|
||||
../../../src/shared/fteextensions.qc
|
||||
../../../src/gs-entbase/server/defs.h
|
||||
../../../src/shared/defs.h
|
||||
../../../src/server/defs.h
|
||||
|
||||
|
||||
../../../src/gs-entbase/server.src
|
||||
../../../src/gs-entbase/shared.src
|
||||
|
||||
//TAGGG - NEW
|
||||
../shared/util.h
|
||||
../shared/defs.h
|
||||
defs.h
|
||||
|
||||
../shared/ammo.h
|
||||
../shared/weapons.h
|
||||
../shared/player.h
|
||||
../shared/powerup.h
|
||||
|
||||
|
||||
precache.h
|
||||
entity/ts_powerup.h
|
||||
//player.c
|
||||
|
||||
entity/TSWorldGun.h
|
||||
entity/TSThrownProjectile.h
|
||||
|
||||
|
||||
../shared/include.src
|
||||
|
||||
// <HL monster includes were here>
|
||||
|
||||
//TAGGG - NEW
|
||||
entity/ts_bomb.qc
|
||||
entity/ts_dmhill.qc
|
||||
entity/ts_groundweapon.qc
|
||||
entity/ts_hack.qc
|
||||
entity/ts_mapglobals.qc
|
||||
entity/ts_model.qc
|
||||
entity/ts_objective_manager.qc
|
||||
entity/ts_objective_ptr.qc
|
||||
entity/ts_powerup.qc
|
||||
entity/ts_slowmotion.qc
|
||||
entity/ts_slowmotionpoint.qc
|
||||
entity/ts_teamescape.qc
|
||||
entity/ts_trigger.qc
|
||||
entity/ts_wingiver.qc
|
||||
entity/TSWorldGun.qc
|
||||
entity/TSThrownProjectile.qc
|
||||
entity/TSAmmoPack.qc
|
||||
|
||||
../shared/player.qc
|
||||
../shared/inventory_logic.qc
|
||||
|
||||
player.qc
|
||||
spectator.qc
|
||||
items.qc
|
||||
world_items.qc
|
||||
|
||||
../../../src/botlib/include.src
|
||||
|
||||
gamerules.qc
|
||||
gamerules_singleplayer.qc
|
||||
gamerules_multiplayer.qc
|
||||
client.qc
|
||||
init.qc
|
||||
|
||||
// NOPE! Using our own copy now
|
||||
//../../../base/src/server/damage.qc
|
||||
damage.qc
|
||||
|
||||
rules.qc
|
||||
../../../base/src/server/modelevent.qc
|
||||
|
||||
spawn.qc
|
||||
|
||||
|
||||
|
||||
//TAGGG - NEW
|
||||
// no server util.h/.qc, yet?
|
||||
//util.qc
|
||||
precache.qc
|
||||
//money.qc
|
||||
//spectator.qc
|
||||
|
||||
|
||||
|
||||
../../../src/server/include.src
|
||||
../../../src/shared/include.src
|
||||
#endlist
|
20
src/server/rules.qc
Normal file
20
src/server/rules.qc
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
int Rules_IsTeamPlay(void)
|
||||
{
|
||||
return cvar("teamplay");
|
||||
}
|
41
src/server/spawn.qc
Normal file
41
src/server/spawn.qc
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void info_player_start(void)
|
||||
{
|
||||
self.solid = SOLID_TRIGGER;
|
||||
setsize(self, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
self.botinfo = BOTINFO_SPAWNPOINT;
|
||||
}
|
||||
|
||||
void info_player_deathmatch(void)
|
||||
{
|
||||
self.solid = SOLID_TRIGGER;
|
||||
setsize(self, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
self.botinfo = BOTINFO_SPAWNPOINT;
|
||||
}
|
||||
|
||||
void info_player_team1(void)
|
||||
{
|
||||
self.classname = "info_player_deathmatch";
|
||||
self.botinfo = BOTINFO_SPAWNPOINT;
|
||||
}
|
||||
|
||||
void info_player_team2(void)
|
||||
{
|
||||
self.classname = "info_player_deathmatch";
|
||||
self.botinfo = BOTINFO_SPAWNPOINT;
|
||||
}
|
28
src/server/spectator.qc
Normal file
28
src/server/spectator.qc
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void Game_SpectatorThink(void)
|
||||
{
|
||||
|
||||
}
|
||||
void Game_SpectatorConnect(void)
|
||||
{
|
||||
|
||||
}
|
||||
void Game_SpectatorDisconnect(void)
|
||||
{
|
||||
|
||||
}
|
49
src/server/world_items.qc
Normal file
49
src/server/world_items.qc
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
//TAGGG - PENDING DELETE! I think this is useless to FreeTS?
|
||||
|
||||
|
||||
/* This is one of those leftovers from trying to get a game out in time */
|
||||
class world_items:CBaseTrigger
|
||||
{
|
||||
void(void) world_items;
|
||||
};
|
||||
|
||||
void world_items::world_items(void)
|
||||
{
|
||||
/*
|
||||
int nfields = tokenize(__fullspawndata);
|
||||
for (int i = 1; i < (nfields - 1); i += 2) {
|
||||
switch (argv(i)) {
|
||||
case "type":
|
||||
float type = stof(argv(i+1));
|
||||
switch (type) {
|
||||
case 44:
|
||||
spawnfunc_item_battery();
|
||||
break;
|
||||
case 45:
|
||||
spawnfunc_item_suit();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
39
src/shared/ammo.h
Normal file
39
src/shared/ammo.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
|
||||
enum AMMO_ID{
|
||||
NONE = 0,
|
||||
_9x19mm = 1,
|
||||
_45Acp = 2,
|
||||
_p50AE = 3,
|
||||
|
||||
_5p7x28 = 4,
|
||||
|
||||
_10mmAUTO = 5,
|
||||
_p22LR = 6,
|
||||
_p454Casull = 7,
|
||||
_5p56Nato = 8,
|
||||
_7p62x39mm = 9,
|
||||
_50BMG = 10,
|
||||
_SHELLS = 11,
|
||||
|
||||
//needed for the cut weapons from TS 2.0
|
||||
_32ACP = 12,
|
||||
_7p62x51mm = 13,
|
||||
|
||||
LAST_ID = 14
|
||||
};
|
||||
|
||||
|
||||
#define ASSIGN_AMMODATA(arg_constName) ary_ammoData[AMMO_ID::##arg_constName] = (ammodata_t*) &ammo_##arg_constName;
|
||||
#define DECLARE_AMMODATA(arg_varName, arg_sDisplayName, arg_fPricePerBullet, arg_iMax) ammodata_t ammo_##arg_varName = {arg_sDisplayName, arg_fPricePerBullet, arg_iMax};
|
||||
|
||||
|
||||
|
||||
typedef struct{
|
||||
string sDisplayName;
|
||||
float fPricePerBullet;
|
||||
int iMax;
|
||||
// fPricePerBullet * iMax = total cost to fill (added for the first weapon of a given ammo type purchased)
|
||||
} ammodata_t;
|
||||
|
||||
|
35
src/shared/ammo.qc
Normal file
35
src/shared/ammo.qc
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
|
||||
|
||||
DECLARE_AMMODATA(NONE, "_NONE_", 0, 0)
|
||||
DECLARE_AMMODATA(_9x19mm, "9 x 19mm", 1.6666666666, 210)
|
||||
DECLARE_AMMODATA(_45Acp, ".45Acp", 5.1, 200)
|
||||
|
||||
DECLARE_AMMODATA(_p50AE, ".50AE", 7, 70)
|
||||
|
||||
|
||||
DECLARE_AMMODATA(_5p7x28, "5.7 x 28", 6, 200)
|
||||
|
||||
|
||||
DECLARE_AMMODATA(_10mmAUTO, "10mm AUTO", 4.9, 100)
|
||||
|
||||
DECLARE_AMMODATA(_p22LR, ".22 LR", 6, 150)
|
||||
|
||||
DECLARE_AMMODATA(_p454Casull, ".454 Casull", 30, 50)
|
||||
|
||||
DECLARE_AMMODATA(_5p56Nato, "5.56 Nato", 2.6666666666, 90)
|
||||
|
||||
DECLARE_AMMODATA(_7p62x39mm, "7.62 x 39mm", 3.3333333333, 90)
|
||||
|
||||
DECLARE_AMMODATA(_50BMG, "50BMG", 100, 20)
|
||||
|
||||
DECLARE_AMMODATA(_SHELLS, "SHELLS", 16, 60)
|
||||
|
||||
DECLARE_AMMODATA(_32ACP, ".32 ACP", 0.5, 300)
|
||||
DECLARE_AMMODATA(_7p62x51mm, "7.62 x 51mm", 30, 35)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
95
src/shared/animations.h
Normal file
95
src/shared/animations.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
ANIM_LOOKIDLE,
|
||||
ANIM_IDLE,
|
||||
ANIM_DEEPIDLE,
|
||||
ANIM_RUN2,
|
||||
ANIM_WALK2HANDED,
|
||||
ANIM_2HANDSHOT,
|
||||
ANIM_CRAWL,
|
||||
ANIM_CROUCHIDLE,
|
||||
ANIM_JUMP,
|
||||
ANIM_LONGJUMP,
|
||||
ANIM_SWIM,
|
||||
ANIM_TREADWATER,
|
||||
ANIM_RUN,
|
||||
ANIM_WALK,
|
||||
ANIM_AIM2,
|
||||
ANIM_SHOOT2,
|
||||
ANIM_AIM1,
|
||||
ANIM_SHOOT1,
|
||||
ANIM_DIESIMPLE,
|
||||
ANIM_DIEBACKWARDS1,
|
||||
ANIM_DIEBACKWARDS2,
|
||||
ANIM_DIEFORWARD,
|
||||
ANIM_DIEHEADSHOT,
|
||||
ANIM_DIESPIN,
|
||||
ANIM_DIEGUTSHOT,
|
||||
ANIM_AIMCROWBAR,
|
||||
ANIM_SHOOTCROWBAR,
|
||||
ANIM_CR_AIMCROWBAR,
|
||||
ANIM_CR_SHOOTCROWBAR,
|
||||
ANIM_AIMTRIPMINE,
|
||||
ANIM_SHOOTTRIPMINE,
|
||||
ANIM_CR_AIMTRIPMINE,
|
||||
ANIM_CR_SHOOTTRIPMINE,
|
||||
ANIM_AIM1HAND,
|
||||
ANIM_SHOOT1HAND,
|
||||
ANIM_CR_AIM1HAND,
|
||||
ANIM_CR_SHOOT1HAND,
|
||||
ANIM_AIMPYTHON,
|
||||
ANIM_SHOOTPYTHON,
|
||||
ANIM_CR_AIMPYTHON,
|
||||
ANIM_CR_SHOOTPYTHON,
|
||||
ANIM_AIMSHOTGUN,
|
||||
ANIM_SHOOTSHOTGUN,
|
||||
ANIM_CR_AIMSHOTGUN,
|
||||
ANIM_CR_SHOOTSHOTGUN,
|
||||
ANIM_AIMGAUSS,
|
||||
ANIM_SHOOTGAUSS,
|
||||
ANIM_CR_AIMGAUSS,
|
||||
ANIM_CR_SHOOTGAUSS,
|
||||
ANIM_AIMMP5,
|
||||
ANIM_SHOOTMP5,
|
||||
ANIM_CR_AIMMP5,
|
||||
ANIM_CR_SHOOTMP5,
|
||||
ANIM_AIMRPG,
|
||||
ANIM_SHOOTRPG,
|
||||
ANIM_CR_AIMRPG,
|
||||
ANIM_CR_SHOOTRPG,
|
||||
ANIM_AIMEGON,
|
||||
ANIM_SHOOTEGON,
|
||||
ANIM_CR_AIMEGON,
|
||||
ANIM_CR_SHOOTEGON,
|
||||
ANIM_AIMSQUEAK,
|
||||
ANIM_SHOOTSQUEAK,
|
||||
ANIM_CR_AIMSQUEAK,
|
||||
ANIM_CR_SHOOTSQUEAK,
|
||||
ANIM_AIMHIVE,
|
||||
ANIM_SHOOTHIVE,
|
||||
ANIM_CR_AIMHIVE,
|
||||
ANIM_CR_SHOOTHIVE,
|
||||
ANIM_AIMBOW,
|
||||
ANIM_SHOOTBOW,
|
||||
ANIM_CR_AIMBOW,
|
||||
ANIM_CR_SHOOTBOW
|
||||
};
|
||||
|
||||
void Animation_PlayerTop(player, float, float);
|
||||
void Animation_PlayerBottom(player, float, float);
|
130
src/shared/animations.qc
Normal file
130
src/shared/animations.qc
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
.float subblend2frac;
|
||||
.float subblendfrac;
|
||||
.float baseframe1time;
|
||||
.float bonecontrol1;
|
||||
.float bonecontrol2;
|
||||
.float bonecontrol3;
|
||||
.float bonecontrol4;
|
||||
|
||||
void Animation_Print(string sWow) {
|
||||
#ifdef CLIENT
|
||||
print(sprintf("[DEBUG] %s", sWow));
|
||||
#else
|
||||
bprint(PRINT_HIGH, sprintf("SSQC: %s", sWow) );
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Animation_TimerUpdate(player pl, float ftime)
|
||||
{
|
||||
makevectors([0, pl.angles[1], 0]);
|
||||
|
||||
/* top animation is always just being incremented */
|
||||
pl.anim_top_time += ftime;
|
||||
pl.anim_top_delay -= ftime;
|
||||
|
||||
/* we may be walking backwards, thus decrement bottom */
|
||||
if (dotproduct(pl.velocity, v_forward) < 0) {
|
||||
pl.anim_bottom_time -= ftime;
|
||||
} else {
|
||||
pl.anim_bottom_time += ftime;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Animation_PlayerUpdate
|
||||
|
||||
Called every frame to update the animation sequences
|
||||
depending on what the player is doing
|
||||
=================
|
||||
*/
|
||||
void
|
||||
Animation_PlayerUpdate(player pl)
|
||||
{
|
||||
pl.basebone = gettagindex(pl, "Bip01 Spine1");
|
||||
|
||||
if (pl.anim_top_delay <= 0.0f) {
|
||||
pl.anim_top = Weapons_GetAim(pl.activeweapon);
|
||||
}
|
||||
|
||||
if (vlen(pl.velocity) == 0) {
|
||||
if (pl.flags & FL_CROUCHING) {
|
||||
pl.anim_bottom = ANIM_CROUCHIDLE;
|
||||
} else {
|
||||
pl.anim_bottom = ANIM_IDLE;
|
||||
}
|
||||
} else if (vlen(pl.velocity) < 150) {
|
||||
if (pl.flags & FL_CROUCHING) {
|
||||
pl.anim_bottom = ANIM_CRAWL;
|
||||
} else {
|
||||
pl.anim_bottom = ANIM_WALK;
|
||||
}
|
||||
} else if (vlen(pl.velocity) > 150) {
|
||||
if (pl.flags & FL_CROUCHING) {
|
||||
pl.anim_bottom = ANIM_CRAWL;
|
||||
} else {
|
||||
pl.anim_bottom = ANIM_RUN;
|
||||
}
|
||||
}
|
||||
|
||||
pl.frame = pl.anim_top;
|
||||
pl.frame1time = pl.anim_top_time;
|
||||
pl.baseframe = pl.anim_bottom;
|
||||
pl.baseframe1time = pl.anim_bottom_time;
|
||||
|
||||
/* hack, we can't play the animations in reverse the normal way */
|
||||
if (pl.frame1time < 0.0f) {
|
||||
pl.frame1time = 10.0f;
|
||||
}
|
||||
|
||||
makevectors([0, pl.angles[1], 0]);
|
||||
float fCorrect = dotproduct(pl.velocity, v_right) * 0.25f;
|
||||
|
||||
/* Turn torso */
|
||||
pl.bonecontrol1 = fCorrect;
|
||||
pl.bonecontrol2 = pl.bonecontrol1 * 0.5;
|
||||
pl.bonecontrol3 = pl.bonecontrol2 * 0.5;
|
||||
pl.bonecontrol4 = pl.bonecontrol3 * 0.5;
|
||||
|
||||
/* Correct the legs */
|
||||
pl.angles[1] -= fCorrect;
|
||||
|
||||
#ifdef SERVER
|
||||
pl.subblendfrac =
|
||||
pl.subblend2frac = pl.v_angle[0] / 90;
|
||||
#else
|
||||
pl.subblendfrac =
|
||||
pl.subblend2frac = pl.pitch / 90;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Animation_PlayerTop(player pl, float topanim, float timer)
|
||||
{
|
||||
pl.anim_top = topanim;
|
||||
pl.anim_top_time = 0.0f;
|
||||
pl.anim_top_delay = timer;
|
||||
}
|
||||
|
||||
void
|
||||
Animation_PlayerBottom(player pl, float botanim, float timer)
|
||||
{
|
||||
pl.anim_bottom = botanim;
|
||||
}
|
135
src/shared/defs.h
Normal file
135
src/shared/defs.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
|
||||
|
||||
enum TS_GameMode{
|
||||
DEATHMATCH,
|
||||
TEAM_DEATHMATCH,
|
||||
TEAM_PLAY
|
||||
};
|
||||
|
||||
|
||||
enum TS_Team{
|
||||
TEAM_NONE = -1,
|
||||
TEAM_1 = 0,
|
||||
TEAM_2 = 1,
|
||||
TEAM_3 = 2,
|
||||
TEAM_4 = 3
|
||||
};
|
||||
|
||||
#ifdef SSQC
|
||||
//server CVar
|
||||
var float autocvar_weaponstay = 60; //default originally 15
|
||||
#endif
|
||||
|
||||
|
||||
// for convenience and clarity, see fteextensions.qc for the rest of the SOUNDFLAG choices
|
||||
#define SOUNDFLAG_NONE 0
|
||||
|
||||
// Can use SOUNDFLAG_PLAYER_SHARED in most places meant to play sounds client & serverside.
|
||||
// UNICAST stops the sound from playing for the client who called it since that client
|
||||
// played it themselves already, after all they sent the message to the server to begin with.
|
||||
// Some other flags too. To avoid UNICAST, use SOUNDFLAG_COMMON instead.
|
||||
#ifdef CLIENT
|
||||
#define SOUNDFLAG_PLAYER_SHARED (SOUNDFLAG_NOSPACIALISE | SOUNDFLAG_FOLLOW)
|
||||
#define SOUNDFLAG_PLAYER_COMMON (SOUNDFLAG_NOSPACIALISE | SOUNDFLAG_FOLLOW)
|
||||
#else //SERVER
|
||||
#define SOUNDFLAG_PLAYER_SHARED (SOUNDFLAG_FOLLOW | SOUNDFLAG_UNICAST)
|
||||
#define SOUNDFLAG_PLAYER_COMMON (SOUNDFLAG_FOLLOW)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// OLD WAY! Blah, forgot about this, just use the new names above.
|
||||
// Pretty simple way to handle the server not having the "NOSPACIALISE" flag.
|
||||
// Sounds that depend on being played very neatly clientside really just need to...
|
||||
// be played clientside though (send an event from the server to play it there).
|
||||
// Really SOUNDFLAG_CUSTOMCLIENT -> SOUNDFLAG_PLAYER_COMMON .
|
||||
#ifdef CSQC
|
||||
#define SOUNDFLAG_CUSTOMCLIENT SOUNDFLAG_FOLLOW | SOUNDFLAG_NOSPACIALISE
|
||||
#else
|
||||
#define SOUNDFLAG_CUSTOMCLIENT SOUNDFLAG_FOLLOW
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// ???
|
||||
//#pragma target FTE
|
||||
//#pragma flag enable lo //enable short-circuiting
|
||||
|
||||
|
||||
|
||||
//TAGGG
|
||||
//FLAG. Rename of the same value used by FL_SEMI_TOGGLED, since that name isn't used in The Specialists
|
||||
//now. It will instead tell whether this is the first time firing since a release or not.
|
||||
// One for secondary added too. hope that's a good idea
|
||||
// ...unfortunately using these for flag values might've been a bad idea.
|
||||
// Even in the old version, "1<<21" was already used for something else.
|
||||
// They've been offset in the new version (base FreeCS by the way).
|
||||
// So these will just be player bool's instead.
|
||||
//#define FL_PRIMARY_FIRE_FIRST_FRAME (1<<20)
|
||||
//#define FL_SECONDARY_FIRE_FIRST_FRAME (1<<21)
|
||||
|
||||
|
||||
|
||||
// MODERN FREECS STATS (in case they're needed for the sake of being
|
||||
// an extensive gamemod, but seeing as FreeHL has none of these, I doubt
|
||||
// that)
|
||||
/*
|
||||
STAT_MONEY = 34,
|
||||
STAT_PROGRESS,
|
||||
STAT_GAMETIME,
|
||||
STAT_GAMESTATE
|
||||
*/
|
||||
|
||||
|
||||
// Why 34? See the fteextensions.qc (built-in / engine-provided things),
|
||||
// looks like #33 is reserved. FreeCS still complies with this so trusting that.
|
||||
#define STAT_BUILTIN_SEPARATOR 34
|
||||
|
||||
|
||||
//TAGGG - Still need to remove some irrelevant ones like BUYZONE,, ESCAPEZONE, WON_T, etc.
|
||||
// Also starting at stat 34? Feel some constant would be more comfortable for that, might not be an option though.
|
||||
enum {
|
||||
STAT_BUYZONE = STAT_BUILTIN_SEPARATOR,
|
||||
|
||||
STAT_MONEY,
|
||||
STAT_FLAGS,
|
||||
|
||||
STAT_PROGRESS,
|
||||
STAT_TEAM,
|
||||
STAT_GAMETIME,
|
||||
STAT_GAMESTATE,
|
||||
/*
|
||||
STAT_WON_T,
|
||||
STAT_WON_CT,
|
||||
*/
|
||||
|
||||
//TAGGG - NEW
|
||||
//TAGGG - INCLUSION. just to show up in searches, new stuff though
|
||||
STAT_RULE_MONEYALLOWED,
|
||||
STAT_RULE_MAXWEIGHTSLOTS
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
GAME_INACTIVE,
|
||||
GAME_COMMENCING,
|
||||
GAME_FREEZE,
|
||||
GAME_ACTIVE,
|
||||
GAME_END,
|
||||
GAME_OVER
|
||||
};
|
||||
|
||||
|
||||
var float autocvar_movemodmulti = 1;
|
||||
|
||||
|
||||
//float Game_GetFriction(entity eTarget);
|
||||
//float Game_GetMaxSpeed( entity eTarget );
|
||||
|
||||
|
||||
// Prototype for weapons_common.qc method(s)!
|
||||
// For things here to be aware of these earlier
|
||||
void Weapons_Draw(void);
|
||||
void Weapons_Holster(void);
|
||||
|
||||
|
20
src/shared/effects.h
Normal file
20
src/shared/effects.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
#ifdef SSQC
|
||||
void Effect_Explosion(vector vecOrigin );
|
||||
#else
|
||||
void EV_Effect_Explosion(vector vecOrigin );
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef SSQC
|
||||
//TAGGG - Thank you The Wastes!
|
||||
void Effect_ScreenShake(vector vecOrigin, float fRadius, float fStrength );
|
||||
#else
|
||||
void EV_Effect_ScreenShake(int iType);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void Effect_CreateExplosion(vector vPos);
|
121
src/shared/effects.qc
Normal file
121
src/shared/effects.qc
Normal file
|
@ -0,0 +1,121 @@
|
|||
|
||||
//TAGGG - INCLUSION. and new file.
|
||||
|
||||
//Try copying from _base/shared/effects.c. From the up to date repository or the old one, dunno.
|
||||
|
||||
|
||||
#ifdef SSQC
|
||||
void Effect_Explosion(vector vecOrigin )
|
||||
{
|
||||
//the specialists explosion does not do this, strangely enough.
|
||||
//Decals_PlaceScorch(vecOrigin);
|
||||
|
||||
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
|
||||
WriteByte( MSG_MULTICAST, EVENT_TS::EFFECT_EXPLOSION );
|
||||
WriteCoord( MSG_MULTICAST, vecOrigin[0] );
|
||||
WriteCoord( MSG_MULTICAST, vecOrigin[1] );
|
||||
WriteCoord( MSG_MULTICAST, vecOrigin[2] );
|
||||
|
||||
msg_entity = world;
|
||||
multicast( vecOrigin, MULTICAST_PVS );
|
||||
|
||||
}
|
||||
#else
|
||||
void EV_Effect_Explosion(vector vecOrigin ){
|
||||
pointparticles( PART_EXPLOSION, vecOrigin, [0,0,0], 1 );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef SSQC
|
||||
//TAGGG - Thank you The Wastes!
|
||||
void Effect_ScreenShake(vector vecOrigin, float fRadius, float fStrength )
|
||||
{
|
||||
entity eDChain = findradius( vecOrigin, fRadius );
|
||||
while( eDChain ) {
|
||||
if ( eDChain.classname == "player" ) {
|
||||
float fDiff = vlen( vecOrigin - eDChain.origin );
|
||||
|
||||
fDiff = ( fRadius - fDiff ) / fRadius;
|
||||
fStrength = fStrength * fDiff;
|
||||
|
||||
if ( fDiff > 0 ) {
|
||||
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
|
||||
WriteByte( MSG_MULTICAST, EVENT_TS::EFFECT_SHAKE );
|
||||
WriteByte( MSG_MULTICAST, (int)fStrength );
|
||||
|
||||
msg_entity = eDChain;
|
||||
multicast( [0,0,0], MULTICAST_ONE );
|
||||
}
|
||||
}
|
||||
eDChain = eDChain.chain;
|
||||
}
|
||||
}
|
||||
#else
|
||||
//void EV_Effect_ScreenShake(vector vecOrigin, float fRadius, float fStrength ){
|
||||
// no, works a bit differently.
|
||||
void EV_Effect_ScreenShake(int iType){
|
||||
|
||||
View_ShakeCreate( iType );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
//TAGGG - for the specialists, redirecting this to some other logic.
|
||||
void
|
||||
Effect_CreateExplosion(vector vPos)
|
||||
{
|
||||
|
||||
|
||||
#ifdef TS
|
||||
|
||||
#ifdef SSQC
|
||||
Effect_ScreenShake( vPos, 2048, 255 );
|
||||
Effect_Explosion( vPos + [0,0,16] );
|
||||
#else
|
||||
printfline("WARNING!!! Effect_CreateExplosion called clientside, but not supposed to be!");
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
#ifdef SERVER
|
||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||
WriteByte(MSG_MULTICAST, EV_EXPLOSION);
|
||||
WriteCoord(MSG_MULTICAST, vecPos[0]);
|
||||
WriteCoord(MSG_MULTICAST, vecPos[1]);
|
||||
WriteCoord(MSG_MULTICAST, vecPos[2]);
|
||||
msg_entity = self;
|
||||
multicast(vecPos, MULTICAST_PVS);
|
||||
#else
|
||||
Decals_Place(vecPos, sprintf("{scorch%d", floor(random(1,4))));
|
||||
vecPos[2] += 48;
|
||||
env_sprite eExplosion = spawn(env_sprite);
|
||||
setorigin(eExplosion, vecPos);
|
||||
setmodel(eExplosion, "sprites/fexplo.spr");
|
||||
sound(eExplosion, CHAN_WEAPON, sprintf("weapons/explode%d.wav", floor(random() * 3) + 3), 1, ATTN_NORM);
|
||||
|
||||
//eExplosion.think = FX_Explosion_Animate;
|
||||
eExplosion.effects = EF_ADDITIVE;
|
||||
eExplosion.drawmask = MASK_ENGINE;
|
||||
eExplosion.maxframe = modelframecount(eExplosion.modelindex);
|
||||
eExplosion.loops = 0;
|
||||
eExplosion.framerate = 20;
|
||||
eExplosion.nextthink = time + 0.05f;
|
||||
|
||||
pointparticles(FX_EXPLOSION_MAIN, vecPos, [0,0,0], 1);
|
||||
pointparticles(FX_EXPLOSION_BS, vecPos, [0,0,0], 1);
|
||||
#endif
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
25
src/shared/entities.h
Normal file
25
src/shared/entities.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
//TAGGG - Definitely change this!!!
|
||||
// Do we need to use this for powerups? anything else new?
|
||||
|
||||
enum
|
||||
{
|
||||
ENT_POWERUP = ENT_SEPARATOR
|
||||
// ...
|
||||
// etc.
|
||||
};
|
109
src/shared/event_custom.h
Normal file
109
src/shared/event_custom.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
|
||||
|
||||
BOOLEAN TS_Weapon_PrimaryAttackRelease(player localPlayer, BOOLEAN hasAmmo);
|
||||
BOOLEAN TS_Weapon_SecondaryAttackRelease(player localPlayer, BOOLEAN hasAmmo);
|
||||
|
||||
void TS_Weapon_Draw_extra(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void TS_playerEquippedWeapon_Shared(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo);
|
||||
|
||||
#ifdef CSQC
|
||||
void _TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo);
|
||||
|
||||
void TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo);
|
||||
|
||||
#endif
|
||||
#ifdef SSQC
|
||||
int _TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo);
|
||||
|
||||
void TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo);
|
||||
|
||||
void CSEv_TS_playerEquippedWeapon_ii(int newWeaponEquipped, BOOLEAN useAkimbo);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef SSQC
|
||||
void TS_PlayInsertShellSound(player localPlayer);
|
||||
#else
|
||||
void TS_PlayInsertShellSound(player localPlayer);
|
||||
void EV_TS_PlayInsertShellSound(player localPlayer);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef SSQC
|
||||
void TS_resetViewModel(player localPlayer);
|
||||
#else
|
||||
void TS_resetViewModel(player localPlayer);
|
||||
void EV_TS_resetViewModel(void);
|
||||
#endif
|
||||
|
||||
#ifdef SSQC
|
||||
void TS_resetPlayer(player localPlayer, BOOLEAN resetInventory)
|
||||
#else
|
||||
void EV_TS_resetPlayer(player localPlayer, BOOLEAN resetInventory)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef CSQC
|
||||
//NOTICE - clientside components removed, except for clientside being able
|
||||
// to signal that the server should remove a weapon.
|
||||
// This needs no clientside logic equivalent to be mirrored.
|
||||
/*
|
||||
void _TS_playerDropWeapon(player localPlayer);
|
||||
*/
|
||||
void TS_playerDropWeapon(void);
|
||||
/*
|
||||
void EV_TS_playerDropWeapon(player localPlayer);
|
||||
*/
|
||||
#endif
|
||||
#ifdef SSQC
|
||||
//that's it really serverside.
|
||||
void _TS_playerDropWeapon(void);
|
||||
void TS_playerDropWeapon(void);
|
||||
void CSEv_TS_playerDropWeapon_(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
void CSEv_TS_playerChangeFiremode_(void);
|
||||
#endif
|
||||
void TS_playerChangeFiremode(void);
|
||||
void _TS_playerChangeFiremode(void )
|
||||
|
||||
#ifdef SERVER
|
||||
void CSEv_TS_playerUseItems_(void);
|
||||
#endif
|
||||
void TS_playerUseItems(void);
|
||||
void _TS_playerUseItems(void);
|
||||
|
||||
#ifdef SERVER
|
||||
void CSEv_TS_playerUsePowerup_(void);
|
||||
#endif
|
||||
void TS_playerUsePowerup(void);
|
||||
void _TS_playerUsePowerup(void);
|
||||
|
||||
#ifdef SERVER
|
||||
void CSEv_TS_playerCallAlt1_(void);
|
||||
#endif
|
||||
void TS_playerCallAlt1(void);
|
||||
void _TS_playerCallAlt1(void);
|
||||
|
||||
#ifdef SERVER
|
||||
void CSEv_TS_playerCallAlt2_(void);
|
||||
#endif
|
||||
void TS_playerCallAlt2(void);
|
||||
void _TS_playerCallAlt2(void);
|
||||
|
||||
|
||||
void playerEquipIdeal(player localPlayer);
|
||||
void playerEquipIdealSafe(player localPlayer);
|
||||
|
973
src/shared/event_custom.qc
Normal file
973
src/shared/event_custom.qc
Normal file
|
@ -0,0 +1,973 @@
|
|||
|
||||
|
||||
BOOLEAN TS_Weapon_PrimaryAttackRelease(player localPlayer, BOOLEAN hasAmmo){
|
||||
// WARNING! This comment is really out of date, fWeaponEventPlayer is no longer a thing
|
||||
// ------------------------------
|
||||
// what are "fWeaponEventPlayer" and "player_localentnum"?
|
||||
// we need to figure this shizz out
|
||||
// "fWeaponEventPlayer" is in "ts/client/defs.h". its assigned a lot in "ts/client/event.c".
|
||||
// at least "player_localentnum" is part of the builtins.
|
||||
|
||||
player pl = localPlayer;
|
||||
|
||||
if(pl.inventoryEquippedIndex == -1){
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
weapondata_basic_t* basicP = pl.getEquippedWeaponData();
|
||||
|
||||
return (*basicP).vOnPrimaryAttackRelease(pl, dynaRef, hasAmmo);
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN TS_Weapon_SecondaryAttackRelease(player localPlayer, BOOLEAN hasAmmo){
|
||||
player pl = localPlayer;
|
||||
|
||||
if(pl.inventoryEquippedIndex == -1){
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
weapondata_basic_t* basicP = pl.getEquippedWeaponData();
|
||||
|
||||
return (*basicP).vOnSecondaryAttackRelease(pl, dynaRef, hasAmmo);
|
||||
}//END OF TS_Weapon_SecondaryAttackRelease
|
||||
|
||||
|
||||
|
||||
// For client and serverside to call alongside a "Weapons_Draw" call,
|
||||
// even if Weapons_Draw itself is skipped in the clientside weapon-select route.
|
||||
// This does get called by ts/client/view.qc calling Weapons_Draw for clientside though,
|
||||
// so treat this as 'whatever I want to happen on drawing a weapon'.
|
||||
void TS_Weapon_Draw_extra(void){
|
||||
player pl = (player)self;
|
||||
weapondynamic_t dynaRef;
|
||||
|
||||
printfline("PLAYER DREW WEAPON: invID:%i (g_weapons ID:%d)", pl.inventoryEquippedIndex, pl.activeweapon);
|
||||
dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
|
||||
pl.currentZoomChoice = -1;
|
||||
pl.aryNextBurstShotTime_softLength = 0;
|
||||
pl.aryNextBurstShotTime_listenIndex = -1;
|
||||
|
||||
// Cheap, but should work? Some things above may be redundant with this
|
||||
pl.reset(FALSE);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//CLONED FROM weapon.h
|
||||
void TS_Weapon_Draw(player localPlayer, int weaponEquipped, BOOLEAN useAkimbo ) {
|
||||
player pl = localPlayer;
|
||||
//weapondynamic_t dynaRef;
|
||||
//weapondynamic_t dynaRefPRE;
|
||||
|
||||
// Anything equipped already? Call vOnUnEquip of that weapon first
|
||||
if(pl.inventoryEquippedIndex != -1){
|
||||
/*
|
||||
dynaRefPRE = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
weapondata_basic_t* basicP = pl.getEquippedWeaponData();
|
||||
|
||||
(*basicP).vOnUnEquip(pl, dynaRefPRE);
|
||||
*/
|
||||
// How about Nuclide's 'Holster' instead?
|
||||
Weapons_Holster();
|
||||
pl.equippedWeaponDeleteCheck();
|
||||
}
|
||||
|
||||
|
||||
|
||||
pl.setInventoryEquippedIndex_Akimbo(weaponEquipped, useAkimbo);
|
||||
// actually no, I guess we just let rendering pick up on this to know when to call draw
|
||||
// MMMMMmmmmmm sounds odd if Draw containts non-rendering-related startup script,
|
||||
// rather this happen as son as possible for now.
|
||||
|
||||
#ifdef CSQC
|
||||
printfline("TS_Weapon_Draw (client direct call): prev:%i curr:%d", pSeat->m_iLastWeapon, pl.activeweapon);
|
||||
#endif
|
||||
|
||||
|
||||
// This Weapons_Draw call is only for serverside, at clientside, we trust
|
||||
// <gamemod>/src/client/view.qc to notice the weapon change and call Weapons_Draw for us.
|
||||
// This is more accurate to the way seen in other Nuclide gamemods.
|
||||
|
||||
// If Holster animations were supported (anim to play on switching weapons that must
|
||||
// finish before a switch happens, looks like putting the weapon away out of view
|
||||
// first), some delay until calling Weapons_Draw here or in ts/client/view.qc to let
|
||||
// that holster anim finish playing would be a good idea.
|
||||
|
||||
|
||||
// TEST! Force the call now anyway clientside too.
|
||||
//#ifdef SSQC
|
||||
TS_Weapon_Draw_extra();
|
||||
Weapons_Draw();
|
||||
//#else
|
||||
// // so that any choice of weapon, same as before or even nothing, will still
|
||||
// // let client/view.qc do the whole viewmodel routine again
|
||||
// // This might not even be effective here for spawns, see EVENT_TS::SPAWN
|
||||
// pSeat->m_iLastWeapon = -2;
|
||||
//#endif
|
||||
|
||||
// Is that necessary?
|
||||
#ifdef CLIENT
|
||||
HUD_CloseWeaponSelect(TRUE);
|
||||
#endif
|
||||
|
||||
}//TS_Weapon_Draw
|
||||
|
||||
|
||||
|
||||
|
||||
//This will be serverside only. Clientside should never call this.
|
||||
//TAGGG - TODO. Would making this shared be OK? Seems to have worked for grenades and throwing knives
|
||||
// on removing themselves on running out of ammo.
|
||||
// Just remember that spawning the actual entitiy won't be happening clientside in any form.
|
||||
#ifdef SSQC
|
||||
void TS_Weapon_Drop() {
|
||||
player pl = (player)self;
|
||||
|
||||
if(pl.inventoryEquippedIndex == -1){
|
||||
//stop. nothing to drop.. ?
|
||||
return;
|
||||
}
|
||||
|
||||
// in case vOnUnEquip alters the weapon equipped, like a grenade switching out.
|
||||
int currentWeaponMem = pl.inventoryEquippedIndex;
|
||||
|
||||
//TAGGG - like equipping a new weapon, we have to let the existing equipped
|
||||
// weapon know that it's about to be unequipped.
|
||||
if(currentWeaponMem != -1){
|
||||
pl.dropWeapon(currentWeaponMem, FALSE);
|
||||
//}//END OF pl.inventoryEquippedIndex check
|
||||
}
|
||||
////////////////////////////////////
|
||||
|
||||
|
||||
}//TS_Weapon_Drop
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//TAGGG - CRITICAL! TS_playerEquippedWeapon methods dummied out to stop conflicts with
|
||||
// the new Nuclide way
|
||||
|
||||
void TS_playerEquippedWeapon_Shared(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo){
|
||||
|
||||
/*
|
||||
if(localPlayer.inventoryEquippedIndex == newWeaponEquipped && useAkimbo == localPlayer.weaponEquippedAkimbo ){
|
||||
// equipping the same weapon? Ignore this call then!
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
_TS_playerEquippedWeapon(localPlayer, newWeaponEquipped, useAkimbo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef CSQC
|
||||
|
||||
// Actual clientside logic related to this action. Need to change the viewmodel
|
||||
// on a change like this.
|
||||
// Note that viewmodels check for "pl.inventoryEquippedIndex" being changed in ts/client/view.c.
|
||||
// This is OK. pl.inventoryEquippedIndex is updated from server to client in client & server/player.c, read/sendEntity methods. So everything works out.
|
||||
void _TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo){
|
||||
//return;
|
||||
player pl = localPlayer;
|
||||
|
||||
TS_Weapon_Draw(pl, newWeaponEquipped, useAkimbo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// The client set the equippedWeapon, and needs to tell the server to keep it in synch
|
||||
// NOTICE - if we commit the change this very moment, it actually messes with prediction stuff.
|
||||
// So don't do any real client changes from calls when the client starts them.
|
||||
// The client will tell the server to do something, and THEN the server relays that to the client.
|
||||
// Whatever just go with it.
|
||||
void TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo){
|
||||
//pSeat->ePlayer = self = findfloat(world, entnum, player_localentnum);
|
||||
|
||||
//printfline("WELL??! %i %i %d %d", localPlayer.inventoryEquippedIndex, newWeaponEquipped, useAkimbo, localPlayer.weaponEquippedAkimbo);
|
||||
//TODO - CRITICAL! Although - might not be that bad.
|
||||
// Note that any weapon removal (only removing the currently equipped weapon is possible) also
|
||||
// sets the current equipped weapon to -1. So any other choice of weapon after this has to work.
|
||||
// ----------OLD COMMENT--------
|
||||
// Check for having recently deleted a weapon. If so, forget this check. The choice picked
|
||||
// is guaranteed to be ok.
|
||||
// Say there are 3 weapons, #0, #1, #2, and #3. Say #2 is equipped now.
|
||||
// If #2 is deleted (shifts #3 to be the new #2), and #2 is picked to be the new weapon,
|
||||
// that would cause this check to deny picking the 'new' weapon taking up slot #2.
|
||||
if(localPlayer.inventoryEquippedIndex == newWeaponEquipped && useAkimbo == localPlayer.weaponEquippedAkimbo ){
|
||||
// equipping the same weapon? Ignore this call then!
|
||||
return;
|
||||
}
|
||||
|
||||
_TS_playerEquippedWeapon(localPlayer, newWeaponEquipped, useAkimbo);
|
||||
sendevent("TS_playerEquippedWeapon", "ii", newWeaponEquipped, (int)useAkimbo);
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef SSQC
|
||||
//that's it really serverside.
|
||||
int _TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo){
|
||||
player pl = localPlayer;
|
||||
|
||||
TS_Weapon_Draw(pl, newWeaponEquipped, useAkimbo);
|
||||
|
||||
return newWeaponEquipped; //in case any changes were made to this, let the caller know
|
||||
}
|
||||
|
||||
//The server set the equippedWeapon, and needs to tell the client to keep it in synch
|
||||
void TS_playerEquippedWeapon(player localPlayer, int newWeaponEquipped, BOOLEAN useAkimbo){
|
||||
|
||||
_TS_playerEquippedWeapon(localPlayer, newWeaponEquipped, useAkimbo);
|
||||
|
||||
// force a sendoff soon!
|
||||
localPlayer.activeweapon_net = 255;
|
||||
localPlayer.inventoryEquippedIndex_net = 255;
|
||||
|
||||
}
|
||||
|
||||
//This is a received call from the client.
|
||||
//We do need to let the server update first, which then tells the client
|
||||
//of the updated state. Even though the client initiated the call in this case.
|
||||
//The server can initiate the call too.
|
||||
//Either side can call "TS_playerEquippedWeapon" to get the orders right.
|
||||
void CSEv_TS_playerEquippedWeapon_ii(int newWeaponEquipped, BOOLEAN useAkimbo){
|
||||
player pl = (player)self;
|
||||
|
||||
TS_playerEquippedWeapon(pl, newWeaponEquipped, useAkimbo);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SSQC
|
||||
//SERVER ONLY. Send a request to change the animation of the viewmodel directly.
|
||||
void TS_PlayInsertShellSound(player localPlayer){
|
||||
|
||||
/*
|
||||
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
|
||||
WriteByte( MSG_MULTICAST, EVENT_TS::PLAY_INSERT_SHELL_SND );
|
||||
msg_entity = localPlayer;
|
||||
multicast( [0,0,0], MULTICAST_ONE );
|
||||
*/
|
||||
}
|
||||
|
||||
#else
|
||||
void TS_PlayInsertShellSound(player localPlayer){
|
||||
EV_TS_PlayInsertShellSound(localPlayer);
|
||||
}
|
||||
//CLIENTSIDE. now what do I do over here?
|
||||
void EV_TS_PlayInsertShellSound(player localPlayer){
|
||||
//TODO - lower attenuation maybe?
|
||||
// Nevermind, this is a client-only sound for now anyway.
|
||||
localsound("weapons/insert-shell.wav", CHAN_AUTO, 1.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// The server may want to tell the client to reset its viewmodel.
|
||||
// DUMMIED - nevermind that for now, assuming the logic is called from server/client
|
||||
// individually like a lot of weapon's logic.
|
||||
#ifdef SSQC
|
||||
void TS_resetViewModel(player localPlayer){
|
||||
|
||||
/*
|
||||
//localPlayer.lasersightUnlockTime = FALSE; //reset
|
||||
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
|
||||
WriteByte( MSG_MULTICAST, EVENT_TS::RESET_VIEW_MODEL );
|
||||
msg_entity = localPlayer;
|
||||
multicast( [0,0,0], MULTICAST_ONE );
|
||||
*/
|
||||
}
|
||||
#else
|
||||
//CLIENT
|
||||
void TS_resetViewModel(player localPlayer){
|
||||
localPlayer.lasersightUnlockTime = FALSE; //reset
|
||||
EV_TS_resetViewModel();
|
||||
}
|
||||
void EV_TS_resetViewModel(void){
|
||||
pSeat->m_iLastWeapon = -1; //any weapon slot choice will refresh the view model.
|
||||
resetViewModel();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Want to reset the player between spawns?
|
||||
#ifdef SSQC
|
||||
void TS_resetPlayer(player localPlayer, BOOLEAN resetInventory){
|
||||
|
||||
if(localPlayer == NULL){
|
||||
return;
|
||||
}
|
||||
if(localPlayer.classname != "player"){
|
||||
return;
|
||||
}
|
||||
|
||||
//reset our inventory, serverside.
|
||||
localPlayer.reset(resetInventory);
|
||||
|
||||
//And tell the client to do the same.
|
||||
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
|
||||
WriteByte( MSG_MULTICAST, EVENT_TS::RESET_PLAYER );
|
||||
WriteByte( MSG_MULTICAST, resetInventory );
|
||||
msg_entity = localPlayer;
|
||||
multicast( [0,0,0], MULTICAST_ONE );
|
||||
}
|
||||
#else
|
||||
//CLIENT
|
||||
void EV_TS_resetPlayer(player localPlayer, BOOLEAN resetInventory){
|
||||
if(localPlayer == NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: just have this check in receiving events to begin with?
|
||||
if(localPlayer.classname == "player"){
|
||||
localPlayer.reset(resetInventory);
|
||||
}else{
|
||||
// ????????????
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef CSQC
|
||||
//NOTICE - clientside components removed, except for clientside being able
|
||||
// to signal that the server should remove a weapon.
|
||||
// This needs no clientside logic equivalent to be mirrored.
|
||||
/*
|
||||
void _TS_playerDropWeapon(player localPlayer){
|
||||
player pl = localPlayer;
|
||||
|
||||
TS_Weapon_Drop();
|
||||
|
||||
}
|
||||
*/
|
||||
//TAGGG - TODO. Do anything clientside here too, maybe? Unsure.
|
||||
void TS_playerDropWeapon(void){
|
||||
sendevent("TS_playerDropWeapon", "");
|
||||
}//END OF TS_playerDropWeapon
|
||||
/*
|
||||
//This is a received call from the server - no need to update it back
|
||||
//NOTICE - this is the callback telling us to do something. Yes do something.
|
||||
void EV_TS_playerDropWeapon(player localPlayer){
|
||||
player pl = localPlayer;
|
||||
//pSeat->ePlayer = self = findfloat(world, entnum, player_localentnum);
|
||||
|
||||
_TS_playerDropWeapon(localPlayer);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
#ifdef SSQC
|
||||
//that's it really serverside.
|
||||
void _TS_playerDropWeapon(void){
|
||||
player pl = (player)self;
|
||||
|
||||
TS_Weapon_Drop();
|
||||
|
||||
}//_TS_playerDropWeapon
|
||||
|
||||
//The server set the equippedWeapon, and needs to tell the client to keep it in synch
|
||||
void TS_playerDropWeapon(void){
|
||||
player pl = (player)self;
|
||||
//pSeat->ePlayer = self = findfloat(world, entnum, player_localentnum);
|
||||
_TS_playerDropWeapon();
|
||||
|
||||
|
||||
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
|
||||
WriteByte( MSG_MULTICAST, EVENT_TS::DROP_WEAPON );
|
||||
msg_entity = pl;
|
||||
multicast( [0,0,0], MULTICAST_ONE );
|
||||
}
|
||||
|
||||
//This is a received call from the client.
|
||||
//We do need to let the server update first, which then tells the client
|
||||
//of the updated state. Even though the client initiated the call in this case.
|
||||
//The server can initiate the call too.
|
||||
//Either side can call "TS_playerEquippedWeapon" to get the orders right.
|
||||
void CSEv_TS_playerDropWeapon_(){
|
||||
player pl = (player)self;
|
||||
|
||||
TS_playerDropWeapon();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
// Server receives the message
|
||||
void CSEv_TS_playerChangeFiremode_(void){
|
||||
_TS_playerChangeFiremode();
|
||||
}
|
||||
#endif
|
||||
|
||||
// For now, only the client will expect direct calls to these.
|
||||
// Client sends the input, server receives a message in response to perform things on its end too.
|
||||
void TS_playerChangeFiremode(void){
|
||||
player pl = (player)self;
|
||||
if(pl.classname == "spectator" || pl.inventoryEquippedIndex == -1){
|
||||
//well gee that is no good
|
||||
return;
|
||||
}
|
||||
#ifdef CLIENT
|
||||
sendevent("TS_playerChangeFiremode", "");
|
||||
// and do it my side too?
|
||||
_TS_playerChangeFiremode();
|
||||
#else
|
||||
// SHOULD NOT HAPPEN, should call the CSEv_ version instead
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void _TS_playerChangeFiremode(void ) {
|
||||
|
||||
player pl = (player)self;
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
|
||||
if(dynaRef.weaponTypeID == WEAPONDATA_TYPEID_GUN || dynaRef.weaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
weapondata_gun_t* basicPointer = (weapondata_gun_t*) pl.getEquippedWeaponData();
|
||||
weapondata_gun_t basicRef = *(basicPointer);
|
||||
|
||||
// If the bitmask for available firemodes for this weapon is just a flat "0" (none specified?)
|
||||
// OR our bitmask matches the current firemode exactly (only one bit present), there is no point
|
||||
// in trying to switch firemodes.
|
||||
if(basicRef.iBitsFireModes == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
int* fireModeVar;
|
||||
if(!pl.weaponEquippedAkimbo){
|
||||
fireModeVar = &dynaRef.iFireMode;
|
||||
}else{
|
||||
fireModeVar = &dynaRef.iFireModeAkimbo;
|
||||
}
|
||||
|
||||
if( (*fireModeVar) == basicRef.iBitsFireModes){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//push the bits up until we get a match
|
||||
int originalChoice;
|
||||
int currentChoice;
|
||||
|
||||
//little bit of filtering for safety
|
||||
if((*fireModeVar) <= 0 || (*fireModeVar) > 64){
|
||||
//Bad firemode value? Force the current to 1.
|
||||
//The original is 64, to just stop at the greatest bit possible
|
||||
//if nothing else is reached.
|
||||
originalChoice = 64;
|
||||
currentChoice = 1;
|
||||
}else{
|
||||
//safe!
|
||||
originalChoice = (*fireModeVar);
|
||||
currentChoice = originalChoice << 1;
|
||||
}
|
||||
|
||||
while(currentChoice != originalChoice){
|
||||
if(currentChoice >= 64){ // (1 << 6)
|
||||
currentChoice = 1; //reset from the other way.
|
||||
//This checks the bits starting from the beginning too.
|
||||
}
|
||||
|
||||
if(basicRef.iBitsFireModes & currentChoice){
|
||||
//this power of 2 is a valid fireMode? pick it
|
||||
(*fireModeVar) = currentChoice;
|
||||
return;
|
||||
}
|
||||
|
||||
currentChoice = currentChoice << 1;
|
||||
}
|
||||
|
||||
//couldn't change? only fire mode... hopefully anyways.
|
||||
}//END OF "is a gun" - check
|
||||
|
||||
}//_TS_playerChangeFiremode
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
// Server receives the message
|
||||
void CSEv_TS_playerUseItems_(void){
|
||||
_TS_playerUseItems();
|
||||
}
|
||||
#endif
|
||||
|
||||
// For now, only the client will expect direct calls to these.
|
||||
// Client sends the input, server receives a message in response to perform things on its end too.
|
||||
void TS_playerUseItems(void){
|
||||
player pl = (player)self;
|
||||
if(pl.classname == "spectator" || pl.inventoryEquippedIndex == -1){
|
||||
//well gee that is no good
|
||||
return;
|
||||
}
|
||||
#ifdef CLIENT
|
||||
sendevent("TS_playerUseItems", "");
|
||||
// and do it my side too?
|
||||
_TS_playerUseItems();
|
||||
#else
|
||||
// SHOULD NOT HAPPEN, should call the CSEv_ version instead
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// For making the lasersight & flashlights toggle on/off with the "n" key by default. Cycle through the combos of
|
||||
// off/on.
|
||||
void _TS_playerUseItems(void){
|
||||
player pl = (player)self;
|
||||
|
||||
if(pl.inventoryEquippedIndex != -1){
|
||||
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
|
||||
if(dynaRef.weaponTypeID == WEAPONDATA_TYPEID_GUN || dynaRef.weaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
||||
weapondata_basic_t* basicP = pl.getEquippedWeaponData();
|
||||
|
||||
int legalBuyOpts = (dynaRef.iBitsUpgrade & (*basicP).iBitsUpgrade) & (BITMASK_WEAPONOPT_TOGGLEABLE);
|
||||
// We must have the flashlight bit on, AND support it on our weapon, AND it be a possibility according to weapon data.
|
||||
int legalBuyOpts_on = (dynaRef.iBitsUpgrade_on & legalBuyOpts);
|
||||
|
||||
|
||||
int bitCount = count1Bits(legalBuyOpts, BITMASK_WEAPONOPT_TOGGLEABLE_MIN, BITMASK_WEAPONOPT_TOGGLEABLE_MAX);
|
||||
int bitCount_on = count1Bits(legalBuyOpts_on, BITMASK_WEAPONOPT_TOGGLEABLE_MIN, BITMASK_WEAPONOPT_TOGGLEABLE_MAX);
|
||||
|
||||
int firstBit;
|
||||
int firstBit_on;
|
||||
|
||||
if(bitCount == 0){
|
||||
//no togglable buyopts at all? I guess that's it.
|
||||
}else{
|
||||
|
||||
if(bitCount == 1){
|
||||
//one bit available? just toggle on/off then.
|
||||
if(bitCount_on){
|
||||
dynaRef.iBitsUpgrade_on = 0;
|
||||
}else{
|
||||
dynaRef.iBitsUpgrade_on = legalBuyOpts;
|
||||
}
|
||||
|
||||
|
||||
}else{
|
||||
//variable number of bits? See how many bits are on right now.
|
||||
if(bitCount_on == 0){
|
||||
// No bits on?
|
||||
// Turn the first one on.
|
||||
firstBit = findFirst1Bit(legalBuyOpts, BITMASK_WEAPONOPT_TOGGLEABLE_MIN, BITMASK_WEAPONOPT_TOGGLEABLE_MAX);
|
||||
dynaRef.iBitsUpgrade_on |= firstBit;
|
||||
}else if(bitCount_on == 1){
|
||||
// If we're not the last bit, turn the next one on.
|
||||
// Start by looking at the next bit (bit << 1)
|
||||
firstBit_on = findFirst1Bit(legalBuyOpts, legalBuyOpts_on << 1, BITMASK_WEAPONOPT_TOGGLEABLE_MAX);
|
||||
|
||||
if(firstBit_on != 0){
|
||||
//we'll take it!
|
||||
dynaRef.iBitsUpgrade_on = firstBit_on;
|
||||
}else{
|
||||
//ran out of available bits? Take them all.
|
||||
dynaRef.iBitsUpgrade_on = legalBuyOpts;
|
||||
}
|
||||
}else{
|
||||
//more than 1? Turn them all off then.
|
||||
dynaRef.iBitsUpgrade_on = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Let's be a clientside sound only.
|
||||
#ifdef CLIENT
|
||||
localsound("weapons/switch.wav", CHAN_AUTO, 1.0f);
|
||||
#endif
|
||||
|
||||
|
||||
// (METHOD MADE SINCE: TS_Weapons_PlaySoundChannelDirect)
|
||||
|
||||
// Any bits? Play this sound, some change happened
|
||||
//sound(pl, CHAN_ITEM, "weapons/switch.wav", 1, ATTN_NONE, 100.0f, SOUNDFLAG_PLAYER_SHARED, 0);
|
||||
|
||||
/*
|
||||
//TAGGG
|
||||
// unicast demo . Why does the sound play clientside the first few times anyway?
|
||||
// It shouldn't ever, with the clientside-call here disabled.
|
||||
#ifdef CLIENT
|
||||
//sound(pl, CHAN_ITEM, "weapons/switch.wav", 1, ATTN_NONE, 100.0f, SOUNDFLAG_NONE, 0);
|
||||
#else
|
||||
sound(pl, CHAN_ITEM, "weapons/switch.wav", 1, ATTN_NONE, 100.0f, SOUNDFLAG_UNICAST, 0);
|
||||
#endif
|
||||
*/
|
||||
}//END OF bitcount checks
|
||||
|
||||
|
||||
|
||||
}///END OF _GUN or _IRONSIGHT type checks
|
||||
}//END OF weaponEquippedID check
|
||||
|
||||
|
||||
printfline("CSEv_TS_playerUseItems_");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
// Server receives the message
|
||||
void CSEv_TS_playerUsePowerup_(void){
|
||||
_TS_playerUsePowerup();
|
||||
}
|
||||
#endif
|
||||
|
||||
// For now, only the client will expect direct calls to these.
|
||||
// Client sends the input, server receives a message in response to perform things on its end too.
|
||||
void TS_playerUsePowerup(void){
|
||||
player pl = (player)self;
|
||||
if(pl.classname == "spectator" || pl.inventoryEquippedIndex == -1){
|
||||
//well gee that is no good
|
||||
return;
|
||||
}
|
||||
#ifdef CLIENT
|
||||
sendevent("TS_playerUsePowerup", "");
|
||||
// and do it my side too?
|
||||
_TS_playerUsePowerup();
|
||||
#else
|
||||
// SHOULD NOT HAPPEN, should call the CSEv_ version instead
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//TAGGG - TODO.
|
||||
// Applies the currently equipped player powerup if there is one.
|
||||
void _TS_playerUsePowerup(void){
|
||||
player pl = (player)self;
|
||||
|
||||
// ...
|
||||
|
||||
printfline("CSEv_TS_playerUsePowerup_");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
// Server receives the message
|
||||
void CSEv_TS_playerCallAlt1_(void){
|
||||
_TS_playerCallAlt1();
|
||||
}
|
||||
#endif
|
||||
|
||||
// For now, only the client will expect direct calls to these.
|
||||
// Client sends the input, server receives a message in response to perform things on its end too.
|
||||
void TS_playerCallAlt1(void){
|
||||
player pl = (player)self;
|
||||
if(pl.classname == "spectator" || pl.inventoryEquippedIndex == -1){
|
||||
//well gee that is no good
|
||||
return;
|
||||
}
|
||||
#ifdef CLIENT
|
||||
sendevent("TS_playerCallAlt1", "");
|
||||
// and do it my side too?
|
||||
_TS_playerCallAlt1();
|
||||
#else
|
||||
// SHOULD NOT HAPPEN, should call the CSEv_ version instead
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// middle mouse wheel? Use for some stunts, at least ones that don't necessarily involve being mid-air.
|
||||
// double-tapping space does that. I think?
|
||||
void _TS_playerCallAlt1( void ) {
|
||||
player pl = (player)self;
|
||||
|
||||
// ...
|
||||
|
||||
printfline("CSEv_TS_playerCallAlt1_");
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
// Server receives the message
|
||||
void CSEv_TS_playerCallAlt2_(void){
|
||||
_TS_playerCallAlt2();
|
||||
}
|
||||
#endif
|
||||
|
||||
// For now, only the client will expect direct calls to these.
|
||||
// Client sends the input, server receives a message in response to perform things on its end too.
|
||||
void TS_playerCallAlt2(void){
|
||||
player pl = (player)self;
|
||||
if(pl.classname == "spectator" || pl.inventoryEquippedIndex == -1){
|
||||
//well gee that is no good
|
||||
return;
|
||||
}
|
||||
#ifdef CLIENT
|
||||
sendevent("TS_playerCallAlt2", "");
|
||||
// and do it my side too?
|
||||
_TS_playerCallAlt2();
|
||||
#else
|
||||
// SHOULD NOT HAPPEN, should call the CSEv_ version instead
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// coldcock / weapon-melee?
|
||||
void _TS_playerCallAlt2(void){
|
||||
player pl = (player)self;
|
||||
printfline("CSEv_TS_playerCallAlt2_");
|
||||
|
||||
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
||||
weapondata_basic_t* basicP = pl.getEquippedWeaponData();
|
||||
if(basicP != NULL){
|
||||
(*basicP).vOnColdCock(pl, dynaRef);
|
||||
}else{
|
||||
// what. how.
|
||||
printfline("WHAT. HOW.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//!!!! SERVER EVENTS ONLY BELOW HERE
|
||||
#ifdef SSQC
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// !!! DEBUG STUFF?
|
||||
|
||||
void CSEv_TS_Debug_getOrigin_(void ) {
|
||||
player pl = (player)self;
|
||||
if(pl != NULL){
|
||||
printfline("Client origin: %.1f %.1f %.1f", pl.origin.x, pl.origin.y, pl.origin.z);
|
||||
}else{
|
||||
printfline("ERROR: client null?");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CSEv_TS_Debug_getAngle_(void ) {
|
||||
player pl = (player)self;
|
||||
if(pl != NULL){
|
||||
printfline("Client angles : %.1f %.1f %.1f", pl.angles.x, pl.angles.y, pl.angles.z);
|
||||
printfline("Client v_angle: %.1f %.1f %.1f", pl.v_angle.x, pl.v_angle.y, pl.v_angle.z);
|
||||
}else{
|
||||
printfline("ERROR: client null?");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//NOTICE - these event methods are server-side only.
|
||||
// anything in the "#ifdef S..." something. I dunno.
|
||||
// The client sends a variety of messages at one time to the server to see if the player can really afford what they are
|
||||
// requesting. Secure... probably.
|
||||
|
||||
|
||||
//TODO - PROTOTYPE THIS OR SET IT UP. whatever.
|
||||
// Buys ammo of a certain type (ID), and ammount.
|
||||
// Note that trying to exceed an ammo's max allowed ammount still caps at the ammmo's capacity.
|
||||
// And if the player can't afford the ammount, the next greatest ammount to use the remaining money
|
||||
// will be used instead.
|
||||
void CSEv_PlayerBuyAmmo_TS_i( int iAmmoTypeID, int iAmmoAmount ) {
|
||||
player pl = (player)self;
|
||||
|
||||
//Using this new var for safety.
|
||||
int iActualAmmoAmount = iAmmoAmount;
|
||||
|
||||
if(iAmmoTypeID != -1){
|
||||
ammodata_t ammoRef = *ary_ammoData[iAmmoTypeID];
|
||||
|
||||
//iActualAmmoAmount must be between 0 and ammoRef.iMax, inclusive on both. If the player had 0 ammo to begin with.
|
||||
if(pl.ary_ammoTotal[iAmmoTypeID] + iActualAmmoAmount > ammoRef.iMax){
|
||||
iActualAmmoAmount = ammoRef.iMax - pl.ary_ammoTotal[iAmmoTypeID];
|
||||
}
|
||||
if(iActualAmmoAmount <= 0){
|
||||
//iActualAmmoAmount = 0;
|
||||
//just stop, we aren't buying anything.
|
||||
return;
|
||||
}
|
||||
|
||||
int ammoCost = (int)ceil(iActualAmmoAmount * ammoRef.fPricePerBullet);
|
||||
|
||||
//11 / 16
|
||||
//want to buy 5.
|
||||
//but you have 36 dollars
|
||||
//each ammo costs 10 dollars.
|
||||
//
|
||||
//50 dollers...
|
||||
// 36 >= 50. nope.
|
||||
//bulletsAfforded = floor(36 / 10)
|
||||
//bulletsAfforded = 3
|
||||
//11 + 3 is 14.
|
||||
//iActualAmmoAmount = 3 .
|
||||
|
||||
//what if...
|
||||
//11 + 8 is 19.
|
||||
//11 + 8 = 19, > 16.
|
||||
//so...
|
||||
//iActualAmmoAmount = 16 - 11 (5)
|
||||
|
||||
if(!bRule_MoneyAllowed || pl.money >= ammoCost){
|
||||
//afforded all of it. N ochange in iActualAmmoAmount.
|
||||
}else{
|
||||
//see what we can actually afford.
|
||||
int bulletsAfforded = floor(pl.money / ammoRef.fPricePerBullet);
|
||||
if(pl.ary_ammoTotal[iAmmoTypeID] + bulletsAfforded > ammoRef.iMax){
|
||||
//Even if we could afford more, we can't buy any more than the max allowed.
|
||||
//...not that this should be possible in this case.
|
||||
iActualAmmoAmount = ammoRef.iMax - pl.ary_ammoTotal[iAmmoTypeID];
|
||||
}else{
|
||||
//If buying this many bullets does not put us over the limit, go ahead.
|
||||
iActualAmmoAmount = bulletsAfforded;
|
||||
}
|
||||
//and so this is the new cost.
|
||||
ammoCost = iActualAmmoAmount * ammoRef.fPricePerBullet;
|
||||
}
|
||||
|
||||
//At this point we are buying "iActualAmmoAmount".
|
||||
if(bRule_MoneyAllowed){
|
||||
pl.money -= ammoCost;
|
||||
}
|
||||
pl.ary_ammoTotal[iAmmoTypeID] += iActualAmmoAmount;
|
||||
|
||||
}//END OF iAmmoTypeID valid check
|
||||
|
||||
}//END OF CSEv_PlayerBuyAmmo_TS_i
|
||||
|
||||
|
||||
|
||||
//This is used to signify that we're done sending weapon config over and want to pick a weapon to start equipped with.
|
||||
//Order appears to be the highest slot number first, unless there is a weapon in slot 3 (small machine guns).
|
||||
//Then it gets equipped instead.
|
||||
void CSEv_PlayerBuyWeapon_TS_end_( void ) {
|
||||
player pl = (player)self;
|
||||
|
||||
printfline("CSEv_PlayerBuyWeapon_TS_end_");
|
||||
playerEquipIdeal(pl);
|
||||
}
|
||||
|
||||
//Support upgrades, akimbo, etc. Include ammo if necessary!
|
||||
//ACTUALLY... ammo will be signaled for purchase separately. The client still determines how much ammo to buy though.
|
||||
void CSEv_PlayerBuyWeapon_TS_ii( int iWeaponTypeID, int iBuyOpts ) {
|
||||
player pl = (player)self;
|
||||
printf("Buying weaponTypeID:%i\n", iWeaponTypeID);
|
||||
|
||||
attemptBuyWeapon(pl, iWeaponTypeID, iBuyOpts, 1);
|
||||
}
|
||||
|
||||
// Version that takes a count of the weapon, since throwables stack in place.
|
||||
void CSEv_PlayerBuyWeaponThw_TS_iii( int iWeaponTypeID, int iBuyOpts, int iCount ) {
|
||||
player pl = (player)self;
|
||||
|
||||
printf("Buying weaponTypeID (Thw):%i COUNT? %i\n", iWeaponTypeID, iCount);
|
||||
//printfline("HOW MUCH MONEY WE GOT %.2f invCount:%i", pl.money, pl.ary_myWeapons_softMax);
|
||||
//if ( Rules_BuyingPossible() == FALSE ) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
//attemptAddWeaponToConfig(pl, iWeaponTypeID, iBuyOpts);
|
||||
attemptBuyWeapon(pl, iWeaponTypeID, iBuyOpts, iCount);
|
||||
}
|
||||
|
||||
#endif// SERVER
|
||||
|
||||
|
||||
|
||||
//Goes through the available weapons and picks what would make sense to equip.
|
||||
//TAGGG TODO - SUGGESTION. Don't equip a weapon automatically if it's out of ammo,
|
||||
// assuming we have an option that isn't out of ammo.
|
||||
//TAGGG TODO MAJOR - check to see what place was previously selected, and then look to see where we can grab a new weapon if that changes things.
|
||||
//That is, we have a few cases with special circumstances.
|
||||
//If a singular version of an akimbo weapon was dropped, equip it's now singular version (1 of the two weapons left in inventory).
|
||||
//I think that's it actually.
|
||||
//nah, just check for that special case when dropping a weapon
|
||||
void playerEquipIdeal(player localPlayer){
|
||||
|
||||
//go through all the players weapons. whichever has the highest slot number, except for 5, gets picked
|
||||
player pl = localPlayer;
|
||||
|
||||
int bestSlotYet = 0;
|
||||
|
||||
weapondynamic_t weapon_DynaRef = NULL;
|
||||
|
||||
int weaponDynamicID_toEquip = -1;
|
||||
|
||||
//printfline("playerEquipIdeal: invcount: %i", pl.ary_myWeapons_softMax);
|
||||
|
||||
//work backwards, so the last item in a slot satisfies us
|
||||
for(int i = pl.ary_myWeapons_softMax-1; i >= 0; i--){
|
||||
|
||||
//weapon_configRef = (weaponconfig_weapon_t) pl.weaponconfig_temp.ary_myWeapons[i];
|
||||
weapon_DynaRef = (weapondynamic_t) pl.ary_myWeapons[i];
|
||||
|
||||
|
||||
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_weaponData[weapon_DynaRef.weaponID];
|
||||
weapondata_basic_t basicRef = *(basicPointer);
|
||||
|
||||
BOOLEAN hasAkimbo = FALSE; //really only used if we bother to do this check.
|
||||
|
||||
if(bestSlotYet <= 2){
|
||||
//check for akimbo...
|
||||
|
||||
//hold on, do a few checks before we allow this.
|
||||
if((basicRef.iAkimboID > 0 && basicRef.iAkimboID < WEAPON_AKIMBO_UPGRADE_ID::LAST_ID ) && (weapon_DynaRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO) && (basicRef.iBitsUpgrade & BITS_WEAPONOPT_AKIMBO) ){
|
||||
//yes.
|
||||
hasAkimbo = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//We also give a preference to weapons with akimbo.
|
||||
if(basicRef.iInventorySlot > bestSlotYet || (hasAkimbo) ){
|
||||
bestSlotYet = basicRef.iInventorySlot;
|
||||
weaponDynamicID_toEquip = i;
|
||||
}
|
||||
|
||||
}//END OF for loop through all inventory weapons
|
||||
|
||||
// it is ok to blank the current weapon too (when _toEquip is -1)
|
||||
// The "TRUE" is for using akimbo, if the default weapon
|
||||
// we picked has it. It's fine if not, then this does nothing.
|
||||
// Also, the shared version does not send a message to clientside to update the viewmodel,
|
||||
// this causes issues with the grenade removing itself on running out of ammo.
|
||||
// Throwing knives can use either version here though, but everything works with _shared.
|
||||
// Plus changes to what's serverside are sent to the client soon enough anyway, doing so here
|
||||
// seems unnecessary.
|
||||
//TAGGG - TODO, CRITICAL. Check other places.
|
||||
// Could anywhere else benefit from TS_playerEquippedWeapon being changed to TS_playerEquippedWeapon_Shared ?
|
||||
|
||||
TS_playerEquippedWeapon_Shared(pl, weaponDynamicID_toEquip, TRUE);
|
||||
|
||||
}//playerEquipIdeal
|
||||
|
||||
|
||||
// Call to above with a few extra lines included, commonly used so bundled into a method
|
||||
void playerEquipIdealSafe(player localPlayer){
|
||||
TS_resetViewModel(localPlayer);
|
||||
localPlayer.setInventoryEquippedIndex(-1);
|
||||
playerEquipIdeal(localPlayer);
|
||||
|
||||
printfline("playerEquipIdealSafe: stats, activeWeap:%d invEqIndex:%i", localPlayer.activeweapon, localPlayer.inventoryEquippedIndex);
|
||||
}
|
||||
|
19
src/shared/event_enum.h
Normal file
19
src/shared/event_enum.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
|
||||
|
||||
enum EVENT_TS{
|
||||
SPAWN = EV_SEPARATOR,
|
||||
RESET_VIEW_MODEL,
|
||||
RESET_PLAYER,
|
||||
// Is this in nuclide now? I don't think so, verify.
|
||||
// Same for EXPLOSION / SHAKE below. DROP_WEAPON probably is if we use
|
||||
// the nuclide way for that.
|
||||
PLAYER_DEATH,
|
||||
EFFECT_EXPLOSION,
|
||||
EFFECT_SHAKE,
|
||||
DROP_WEAPON,
|
||||
//TAGGG - NEW. Like EV_IMPACT but to paint a decal only.
|
||||
EV_PLACEDECAL,
|
||||
EV_IMPACT_MELEE,
|
||||
TEST,
|
||||
};
|
||||
|
66
src/shared/flags.h
Normal file
66
src/shared/flags.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
//TAGGG - NOTE. These are used for the "gflags" variable on all (most?) entities.
|
||||
// the "flags" variable uses the ones from nuclide defines instead and should not be used with any
|
||||
// new constants, or ones not already in nuclide's own src/shared/flags.
|
||||
// So here can start over counting from bit #0 because it uses a new var, gflags instead of flags.
|
||||
|
||||
// ALSO... nuclide depends on the GF_SEMI_TOGGLED constant being defined somewhere in the gamemod?
|
||||
// If not any mentions of it in nuclide's src or base/src files that happen to be included
|
||||
// (base/src/shared/weapons_common.qc comes to mind) will cause errors.
|
||||
// Strange as gflags should be handled by the gamemod only.
|
||||
|
||||
// There is also a new var called "identity" in CBaseEntity, part of Nuclide now.
|
||||
// identity can be set to "1", and it looks like it is for everything ever made in Nuclide.
|
||||
// It signifies that the entity can be respawned if the server is looking for things to call
|
||||
// ".Respawn" on.
|
||||
// Making a constant here to make that easier to spot when used here.
|
||||
|
||||
#define IDENTITY_CANRESPAWN (1<<0)
|
||||
|
||||
|
||||
// Also, no GF_CANRESPAWN anymore. Was used in the old base, now using the IDENTITY_CANRESPAWN thing above.
|
||||
|
||||
|
||||
|
||||
/* game flags */
|
||||
#define GF_SEMI_TOGGLED (1<<0)
|
||||
// TAGGG - added for secondary to also be able to tell if this
|
||||
// is the first frame being pressed. Needs to be involved a tiny bit
|
||||
// manually as this one wasn't part of Nuclide.
|
||||
#define GF_SEMI_SECONDARY_TOGGLED (1<<1)
|
||||
#define GF_UNUSED3 (1<<2)
|
||||
#define GF_UNUSED4 (1<<3)
|
||||
#define GF_UNUSED5 (1<<4)
|
||||
#define GF_UNUSED6 (1<<5)
|
||||
#define GF_UNUSED7 (1<<6)
|
||||
#define GF_UNUSED8 (1<<7)
|
||||
#define GF_UNUSED9 (1<<8)
|
||||
#define GF_UNUSED10 (1<<9)
|
||||
#define GF_UNUSED11 (1<<10)
|
||||
#define GF_UNUSED12 (1<<11)
|
||||
#define GF_UNUSED13 (1<<12)
|
||||
#define GF_UNUSED14 (1<<14)
|
||||
#define GF_UNUSED15 (1<<16)
|
||||
#define GF_UNUSED16 (1<<13)
|
||||
#define GF_UNUSED17 (1<<17)
|
||||
#define GF_UNUSED18 (1<<18)
|
||||
#define GF_UNUSED19 (1<<19)
|
||||
#define GF_UNUSED20 (1<<20)
|
||||
#define GF_UNUSED21 (1<<21)
|
||||
#define GF_UNUSED22 (1<<22)
|
||||
#define GF_UNUSED23 (1<<23)
|
110
src/shared/fx_blood.qc
Normal file
110
src/shared/fx_blood.qc
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef CLIENT
|
||||
var float PARTICLE_BLOOD;
|
||||
var int DECAL_BLOOD;
|
||||
|
||||
void
|
||||
FX_Blood_Init(void)
|
||||
{
|
||||
precache_model("sprites/bloodspray.spr");
|
||||
precache_model("sprites/blood.spr");
|
||||
PARTICLE_BLOOD = particleeffectnum("part_blood");
|
||||
DECAL_BLOOD = particleeffectnum("decal_blood.effect");
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
FX_Blood(vector pos, vector color)
|
||||
{
|
||||
#ifdef SERVER
|
||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||
WriteByte(MSG_MULTICAST, EV_BLOOD);
|
||||
WriteCoord(MSG_MULTICAST, pos[0]);
|
||||
WriteCoord(MSG_MULTICAST, pos[1]);
|
||||
WriteCoord(MSG_MULTICAST, pos[2]);
|
||||
WriteByte(MSG_MULTICAST, color[0] * 255);
|
||||
WriteByte(MSG_MULTICAST, color[1] * 255);
|
||||
WriteByte(MSG_MULTICAST, color[2] * 255);
|
||||
msg_entity = self;
|
||||
multicast(pos, MULTICAST_PVS);
|
||||
#else
|
||||
static void Blood_Touch(void)
|
||||
{
|
||||
if (serverkeyfloat("*bspversion") == BSPVER_HL)
|
||||
Decals_Place(self.origin, sprintf("{blood%d", floor(random(1,9))));
|
||||
else {
|
||||
decal_pickwall(self, self.origin);
|
||||
pointparticles(DECAL_BLOOD, g_tracedDecal.endpos, g_tracedDecal.normal, 1);
|
||||
}
|
||||
remove(self);
|
||||
}
|
||||
|
||||
if (cvar("violence_hblood") <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
env_sprite eBlood = spawn(env_sprite);
|
||||
setorigin(eBlood, pos);
|
||||
setmodel(eBlood, "sprites/bloodspray.spr");
|
||||
|
||||
eBlood.drawmask = MASK_ENGINE;
|
||||
eBlood.maxframe = modelframecount(eBlood.modelindex);
|
||||
eBlood.loops = 0;
|
||||
eBlood.scale = 1.0f;
|
||||
|
||||
#ifdef GS_RENDERFX
|
||||
eBlood.m_vecRenderColor = color;
|
||||
eBlood.m_iRenderMode = RM_COLOR;
|
||||
eBlood.m_flRenderAmt = 1.0f;
|
||||
#else
|
||||
eBlood.colormod = color;
|
||||
#endif
|
||||
|
||||
eBlood.framerate = 20;
|
||||
eBlood.nextthink = time + 0.05f;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
env_sprite ePart = spawn(env_sprite);
|
||||
setorigin(ePart, pos);
|
||||
setmodel(ePart, "sprites/blood.spr");
|
||||
ePart.movetype = MOVETYPE_BOUNCE;
|
||||
ePart.gravity = 0.5f;
|
||||
ePart.scale = 0.5f;
|
||||
ePart.drawmask = MASK_ENGINE;
|
||||
ePart.maxframe = modelframecount(ePart.modelindex);
|
||||
ePart.loops = 0;
|
||||
|
||||
#ifdef GS_RENDERFX
|
||||
ePart.m_vecRenderColor = color;
|
||||
ePart.m_iRenderMode = RM_COLOR;
|
||||
ePart.m_flRenderAmt = 1.0f;
|
||||
#else
|
||||
ePart.colormod = color;
|
||||
#endif
|
||||
ePart.framerate = 15;
|
||||
ePart.nextthink = time + 0.1f;
|
||||
ePart.velocity = randomvec() * 64;
|
||||
ePart.touch = Blood_Touch;
|
||||
ePart.solid = SOLID_BBOX;
|
||||
/* ignore player physics */
|
||||
ePart.dimension_solid = 1;
|
||||
ePart.dimension_hit = 1;
|
||||
setsize(ePart, [0,0,0], [0,0,0]);
|
||||
}
|
||||
#endif
|
||||
}
|
164
src/shared/fx_breakmodel.qc
Normal file
164
src/shared/fx_breakmodel.qc
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef CLIENT
|
||||
void
|
||||
FX_BreakModel_Init(void)
|
||||
{
|
||||
precache_model("models/glassgibs.mdl");
|
||||
precache_model("models/woodgibs.mdl");
|
||||
precache_model("models/metalplategibs.mdl");
|
||||
precache_model("models/fleshgibs.mdl");
|
||||
precache_model("models/ceilinggibs.mdl");
|
||||
precache_model("models/computergibs.mdl");
|
||||
precache_model("models/rockgibs.mdl");
|
||||
precache_model("models/cindergibs.mdl");
|
||||
precache_sound("debris/bustglass1.wav");
|
||||
precache_sound("debris/bustglass2.wav");
|
||||
precache_sound("debris/bustglass3.wav");
|
||||
precache_sound("debris/bustcrate1.wav");
|
||||
precache_sound("debris/bustcrate2.wav");
|
||||
precache_sound("debris/bustcrate3.wav");
|
||||
precache_sound("debris/bustmetal1.wav");
|
||||
precache_sound("debris/bustmetal2.wav");
|
||||
precache_sound("debris/bustflesh1.wav");
|
||||
precache_sound("debris/bustflesh2.wav");
|
||||
precache_sound("debris/bustconcrete1.wav");
|
||||
precache_sound("debris/bustconcrete2.wav");
|
||||
precache_sound("debris/bustceiling.wav");
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
FX_BreakModel(int count, vector vMins, vector vMaxs, vector vVel, float fStyle)
|
||||
{
|
||||
#ifdef SERVER
|
||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||
WriteByte(MSG_MULTICAST, EV_MODELGIB);
|
||||
WriteCoord(MSG_MULTICAST, vMins[0]);
|
||||
WriteCoord(MSG_MULTICAST, vMins[1]);
|
||||
WriteCoord(MSG_MULTICAST, vMins[2]);
|
||||
WriteCoord(MSG_MULTICAST, vMaxs[0]);
|
||||
WriteCoord(MSG_MULTICAST, vMaxs[1]);
|
||||
WriteCoord(MSG_MULTICAST, vMaxs[2]);
|
||||
WriteByte(MSG_MULTICAST, fStyle);
|
||||
WriteByte(MSG_MULTICAST, count);
|
||||
|
||||
msg_entity = self;
|
||||
|
||||
vector vWorldPos;
|
||||
vWorldPos[0] = vMins[0] + (0.5 * (vMaxs[0] - vMins[0]));
|
||||
vWorldPos[1] = vMins[1] + (0.5 * (vMaxs[1] - vMins[1]));
|
||||
vWorldPos[2] = vMins[2] + (0.5 * (vMaxs[2] - vMins[2]));
|
||||
multicast(vWorldPos, MULTICAST_PVS);
|
||||
#else
|
||||
static void FX_BreakModel_Remove(void) { remove(self) ; }
|
||||
|
||||
float fModelCount = 0;
|
||||
vector vecPos;
|
||||
string sModel = "";
|
||||
|
||||
switch (fStyle) {
|
||||
case GSMATERIAL_GLASS:
|
||||
case GSMATERIAL_GLASS_UNBREAKABLE:
|
||||
sModel = "models/glassgibs.mdl";
|
||||
fModelCount = 8;
|
||||
break;
|
||||
case GSMATERIAL_WOOD:
|
||||
sModel = "models/woodgibs.mdl";
|
||||
fModelCount = 3;
|
||||
break;
|
||||
case GSMATERIAL_METAL:
|
||||
sModel = "models/metalplategibs.mdl";
|
||||
fModelCount = 13;
|
||||
break;
|
||||
case GSMATERIAL_FLESH:
|
||||
sModel = "models/fleshgibs.mdl";
|
||||
fModelCount = 4;
|
||||
break;
|
||||
case GSMATERIAL_TILE:
|
||||
sModel = "models/ceilinggibs.mdl";
|
||||
fModelCount = 4;
|
||||
break;
|
||||
case GSMATERIAL_COMPUTER:
|
||||
sModel = "models/computergibs.mdl";
|
||||
fModelCount = 15;
|
||||
break;
|
||||
case GSMATERIAL_ROCK:
|
||||
sModel = "models/rockgibs.mdl";
|
||||
fModelCount = 3;
|
||||
break;
|
||||
default:
|
||||
case GSMATERIAL_CINDER:
|
||||
sModel = "models/cindergibs.mdl";
|
||||
fModelCount = 9;
|
||||
break;
|
||||
}
|
||||
|
||||
vector vWorldPos;
|
||||
vWorldPos = vMins + (0.5 * (vMaxs - vMins));
|
||||
|
||||
switch (fStyle) {
|
||||
case GSMATERIAL_GLASS:
|
||||
pointsound(vWorldPos, sprintf("debris/bustglass%d.wav", random(1, 4)), 1.0f, ATTN_NORM);
|
||||
break;
|
||||
case GSMATERIAL_WOOD:
|
||||
pointsound(vWorldPos, sprintf("debris/bustcrate%d.wav", random(1, 4)), 1.0f, ATTN_NORM);
|
||||
break;
|
||||
case GSMATERIAL_METAL:
|
||||
case GSMATERIAL_COMPUTER:
|
||||
pointsound(vWorldPos, sprintf("debris/bustmetal%d.wav", random(1, 3)), 1.0f, ATTN_NORM);
|
||||
break;
|
||||
case GSMATERIAL_FLESH:
|
||||
pointsound(vWorldPos, sprintf("debris/bustflesh%d.wav", random(1, 3)), 1.0f, ATTN_NORM);
|
||||
break;
|
||||
case GSMATERIAL_CINDER:
|
||||
case GSMATERIAL_ROCK:
|
||||
pointsound(vWorldPos, sprintf("debris/bustconcrete%d.wav", random(1, 4)), 1.0f, ATTN_NORM);
|
||||
break;
|
||||
case GSMATERIAL_TILE:
|
||||
pointsound(vWorldPos, "debris/bustceiling.wav", 1.0f, ATTN_NORM);
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
entity eGib = spawn();
|
||||
eGib.classname = "gib";
|
||||
|
||||
vecPos[0] = vMins[0] + (random() * (vMaxs[0] - vMins[0]));
|
||||
vecPos[1] = vMins[1] + (random() * (vMaxs[1] - vMins[1]));
|
||||
vecPos[2] = vMins[2] + (random() * (vMaxs[2] - vMins[2]));
|
||||
|
||||
setorigin(eGib, vecPos);
|
||||
setmodel(eGib, sModel);
|
||||
setcustomskin(eGib, "", sprintf("geomset 0 %f\n", random(1, fModelCount + 1)));
|
||||
eGib.movetype = MOVETYPE_BOUNCE;
|
||||
eGib.solid = SOLID_NOT;
|
||||
|
||||
eGib.avelocity[0] = random()*600;
|
||||
eGib.avelocity[1] = random()*600;
|
||||
eGib.avelocity[2] = random()*600;
|
||||
eGib.think = FX_BreakModel_Remove;
|
||||
eGib.nextthink = time + 10;
|
||||
|
||||
if ((fStyle == GSMATERIAL_GLASS) || (fStyle == GSMATERIAL_GLASS_UNBREAKABLE)) {
|
||||
eGib.alpha = 0.5f;
|
||||
}
|
||||
|
||||
eGib.drawmask = MASK_ENGINE;
|
||||
}
|
||||
#endif
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue