ts/src/shared/event_custom.qc

1058 lines
32 KiB
Plaintext

void
TS_Weapon_PrimaryAttackRelease(player pl){
if(pl.inventoryEquippedIndex == -1){
return;
}
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
weapondata_basic_t* basicP = pl.getEquippedWeaponData();
(*basicP).funOnPrimaryAttackRelease(pl, dynaRef);
}
void
TS_Weapon_SecondaryAttackRelease(player pl){
if(pl.inventoryEquippedIndex == -1){
return;
}
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
weapondata_basic_t* basicP = pl.getEquippedWeaponData();
(*basicP).funOnSecondaryAttackRelease(pl, dynaRef);
}
// 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;
printfline("PLAYER DREW WEAPON: invID:%i (g_weapons ID:%d)", pl.inventoryEquippedIndex, pl.activeweapon);
pl.resetZoom();
pl.aryNextBurstShotTime_softLength = 0;
pl.aryNextBurstShotTime_listenIndex = -1;
// Cheap, but should work? Some things above may be redundant with this
pl.reset(FALSE);
}
void
TS_Weapon_Draw(player pl, int weaponEquipped, BOOL useAkimbo ) {
//weapondynamic_t dynaRef;
//weapondynamic_t dynaRefPRE;
// Anything equipped already?
if(pl.inventoryEquippedIndex != -1){
// How about Nuclide's 'Holster' instead?
Weapons_Holster();
pl.equippedWeaponDeleteCheck();
}
printfline("setInventoryEquipped Flag E");
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 CLIENT
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 SERVER
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
TS_HUD_CloseWeaponSelect(TRUE);
#endif
}//TS_Weapon_Draw
// This will be serverside only. Clientside should never call this.
// 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 SERVER
void
TS_Weapon_Drop() {
player pl = (player)self;
if(pl.inventoryEquippedIndex == -1){
// stop. nothing to drop?
return;
}
if(pl.inventoryEquippedIndex != -1){
pl.dropWeapon(pl.inventoryEquippedIndex, FALSE);
}
}//TS_Weapon_Drop
#endif
// This version does not send a message to the client. It assumes the server and client
// are running the same commands so there is no need for that.
void
TS_playerEquippedWeapon_Shared(player pl, int newWeaponEquipped, BOOL useAkimbo){
/*
if(pl.inventoryEquippedIndex == newWeaponEquipped && useAkimbo == pl.weaponEquippedAkimbo ){
// equipping the same weapon? Ignore this call then!
return;
}
*/
_TS_playerEquippedWeapon(pl, newWeaponEquipped, useAkimbo);
}
#ifdef CLIENT
// 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 pl, int newWeaponEquipped, BOOL useAkimbo){
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 pl, int newWeaponEquipped, BOOL useAkimbo){
//pSeat->ePlayer = self = findfloat(world, entnum, player_localentnum);
//printfline("WELL??! %i %i %d %d", pl.inventoryEquippedIndex, newWeaponEquipped, useAkimbo, pl.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(pl.inventoryEquippedIndex == newWeaponEquipped && useAkimbo == pl.weaponEquippedAkimbo ){
// equipping the same weapon? Ignore this call then!
return;
}
_TS_playerEquippedWeapon(pl, newWeaponEquipped, useAkimbo);
sendevent("TS_playerEquippedWeapon", "ii", newWeaponEquipped, (int)useAkimbo);
// EXPERIMENTAL
pl.equippedWeaponWaitingForCallback = TRUE;
// Not sure how you'd deal with a ping worse than 0.5 seconds
pl.equippedWeaponWaitingForCallback_maxWaitTime = time + 0.8;
}
#endif
#ifdef SERVER
// that's it really serverside.
void
_TS_playerEquippedWeapon(player pl, int newWeaponEquipped, BOOL useAkimbo){
TS_Weapon_Draw(pl, newWeaponEquipped, useAkimbo);
}
//The server set the equippedWeapon, and needs to tell the client to keep it in synch
void
TS_playerEquippedWeapon(player pl, int newWeaponEquipped, BOOL useAkimbo){
_TS_playerEquippedWeapon(pl, newWeaponEquipped, useAkimbo);
// force a sendoff soon!
//pl.activeweapon_net = 255;
//pl.inventoryEquippedIndex_net = 255;
// The client is waiting for a callback? Go on!
// Trying the reliable (_R) version for now
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
WriteByte( MSG_MULTICAST, EVENT_TS::EQUIP_CALLBACK );
msg_entity = pl;
multicast( [0,0,0], MULTICAST_ONE_R );
}
// 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, BOOL useAkimbo){
player pl = (player)self;
TS_playerEquippedWeapon(pl, newWeaponEquipped, useAkimbo);
}
#endif
#ifdef SERVER
void
CSEv_TS_Test_i(int arg){
player pl = (player)self;
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
WriteByte( MSG_MULTICAST, EVENT_TS::TEST_CALLBACK );
WriteInt( MSG_MULTICAST, arg );
msg_entity = pl;
multicast( [0,0,0], MULTICAST_ONE_R );
}
#else
// begins from clientside! Manually call from wherever
// DUMMIED FOR NOW. If seriously used, the player needs a global counter
// that goes up with each call of this function (where globTemp is).
void TestCallback_Initiate(void){
/*
globTemp = ((float)globTemp + 1); //% 255; // use a byte or not?
pl.equippedWeaponWaitingForCallback = TRUE;
pl.equippedWeaponWaitingForCallback_ID = globTemp;
pl.equippedWeaponWaitingForCallback_maxWaitTime = time + 0.8;
sendevent("TS_Test", "i", pl.equippedWeaponWaitingForCallback_ID);
*/
}
// EXPERIMENTAL. If the currently equipped weapon has been changed, reject
// messages received between the time I sent an order to change the current wepaon
// and the time it takes to get a response that the new weapon was seen by the server.
// This stops out-of-date calls coming in from reverting the zoom (or whatever other var)
// back to the old state.
// Or, example:
// Var A is networked (server sends updates to the client of its current value to keep it
// from going too far out of sync).
// var A starts at 30 both places. Say the ping is 500 milliseconds.
// Client sets var A to 12, sends a message to the server to have it changed too.
// But, during those 500 milliseconds to reach the server, messages sent from the server
// with the old A value (30), sent before the client sent the A-update to the server, reach
// the client and rever the value of A back to that 30.
// Only after the A-update of 12 reaches the server and the server updates the client back
// does that stop.
void
EV_EquipCallback(void){
player pl = (player)self;
pl.equippedWeaponWaitingForCallback = FALSE;
}
void
EV_TestCallback(int arg_messageID){
player pl = (player)self;
if(pl.equippedWeaponWaitingForCallback == FALSE){
printfline("??? Not expecting a callback message!");
}
if(arg_messageID == pl.equippedWeaponWaitingForCallback_ID){
printfline("EV_TestCallback: Received up to date message!");
pl.equippedWeaponWaitingForCallback = FALSE;
}else{
printfline("EV_TestCallback: Message out of date! Exp:%i Recv:%i", pl.equippedWeaponWaitingForCallback_ID, arg_messageID);
}
}
#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 SERVER
void
TS_resetViewModel(player pl){
/*
//pl.lasersightUnlockTime = FALSE; //reset
WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET );
WriteByte( MSG_MULTICAST, EVENT_TS::RESET_VIEW_MODEL );
msg_entity = pl;
multicast( [0,0,0], MULTICAST_ONE );
*/
// IDEA:
// I don't think resetting w_idle_next and w_next_attack here would be very
// helpful. Same for in EV_TS_resetViewModel.
}
#else
//CLIENT
void
TS_resetViewModel(player pl){
EV_TS_resetViewModel();
}
void
EV_TS_resetViewModel(void){
player pl = (player)self;
pSeat->m_iLastWeapon = -1; // any weapon slot choice will refresh the view model.
pl.lasersightUnlockTime = FALSE;
TS_View_ResetViewModel();
}
#endif
// Want to reset the player between spawns?
#ifdef SERVER
void
TS_resetPlayer(player pl, BOOL resetInventory){
if(pl == NULL){
return;
}
//reset our inventory, serverside.
pl.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 = pl;
multicast( [0,0,0], MULTICAST_ONE );
}
#else
//CLIENT
void
EV_TS_resetPlayer(player pl, BOOL resetInventory){
if(pl == NULL){
return;
}
pl.reset(resetInventory);
}
#endif
#ifdef CLIENT
// Called by the GameRules, PlayerDeath, for the client to do something as soon as
// possible.
// WHOOPS. All vars since have been dummied or changes here are ineffective,
// keeping the event in case it has some use later.
void
EV_PlayerDeath(void){
printfline("EV_PlayerDeath");
// 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;
// Unfortunately not as effective as it may look.
// On the player using "kill" in console, a CSQC_Parse_Damage call happens shortly
// after this PlayerDeath one, somehow. Stopping damage-drawing on the "*dead" key
// being 1 works.
//pSeat->m_flDamageAlpha = 0;
player pl = (player)self;
pl.resetZoom();
}
#endif
#ifdef CLIENT
//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 pl){
TS_Weapon_Drop();
}
*/
// TODO. Do anything clientside here too, maybe? Unsure.
// Could call the player's DropWeapon method here, but it's hard to trust FTE with prediction
// when it comes to altering inventories it seems.
void
TS_playerDropWeapon(void){
sendevent("TS_playerDropWeapon", "");
}
/*
void EV_TS_playerDropWeapon(player pl){
//pSeat->ePlayer = self = findfloat(world, entnum, player_localentnum);
_TS_playerDropWeapon(pl);
}
*/
#endif
#ifdef SERVER
//that's it really serverside.
void
_TS_playerDropWeapon(void){
player pl = (player)self;
TS_Weapon_Drop();
}//_TS_playerDropWeapon
// Will this ever be needed? When weapons can be knocked out of other player's
// hands by some karate attacks and possibly cold-cocking (I forget), probably.
void
TS_playerDropWeapon(void){
player pl = (player)self;
//pSeat->ePlayer = self = findfloat(world, entnum, player_localentnum);
_TS_playerDropWeapon();
// No need for this message, the changed weapon value should be
// cause for an update that handles that.
// Unless there were anything special about losing a weapon that way
// that the client neets to be aware of? Can't think of anything though.
/*
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.
void
CSEv_TS_playerDropWeapon_(){
_TS_playerDropWeapon();
}
#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.inventoryEquippedIndex == -1){
return;
}
// always now
_TS_playerChangeFiremode();
}
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.
}// "is a gun" - check
}//_TS_playerChangeFiremode
// 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.inventoryEquippedIndex == -1){
return;
}
#if OTHER_PREDICTION_TEST == 0
#ifdef CLIENT
sendevent("TS_playerUseItems", "");
// and do it my side too?
_TS_playerUseItems();
#else
// SHOULD NOT HAPPEN, should call the CSEv_ version instead
#endif
#else
// always do this then
_TS_playerUseItems();
#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;
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
#if OTHER_PREDICTION_TEST == 1
// enforce a check that this is the first inputframe, not repeats, to avoid sound spam
if(custom_input_sequence==custom_clientcommandframe){
#elif OTHER_PREDICTION_TEST == 2
if(canPlaySwitchSound){
// only happens once, not during prediction rewinds.
// that would get spammy
canPlaySwitchSound = FALSE;
#else
// nothing special
if(1){
#endif
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);
/*
// 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
*/
}// bitcount checks
}// _GUN or _IRONSIGHT type checks
}
#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.inventoryEquippedIndex == -1){
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
}
// 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.inventoryEquippedIndex == -1){
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.inventoryEquippedIndex == -1){
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).funOnColdCock(pl, dynaRef);
}else{
// what. how.
printfline("WHAT. HOW.");
}
}
///////////////////////////////////////////////////////////////////////////////////////
// SERVER EVENTS ONLY BELOW HERE
#ifdef SERVER
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
// 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.
// !!! PENDING REMOVAL.
// Nothing calls this, and it's the wrong name anyway (_i, should be _ii at the end I belive).
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;
}// iAmmoTypeID valid check
}// 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_");
pl.completeInventorySend = TRUE;
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.
// SUGGESTION. Don't equip a weapon automatically if it's out of ammo,
// assuming we have an option that isn't out of ammo.
// 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 pl){
// go through all the players weapons. whichever has the highest slot number, except for 5, gets picked
int bestSlotYet = 0;
weapondynamic_t weapon_DynaRef = NULL;
int weaponDynamicID_toEquip = -1;
int i;
//printfline("playerEquipIdeal: invcount: %i", pl.ary_myWeapons_softMax);
// work backwards, so the last item in a slot satisfies us
for(i = pl.ary_myWeapons_softMax-1; i >= 0; 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);
BOOL 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;
}
}// 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.
// TODO, CRITICAL. Check other places.
// Could anywhere else benefit from TS_playerEquippedWeapon being changed to
// TS_playerEquippedWeapon_Shared ?
printfline("setInventoryEquippedIndex: FLAG D");
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 pl){
TS_resetViewModel(pl);
printfline("setInventoryEquippedIndex: FLAG C");
pl.setInventoryEquippedIndex(-1);
playerEquipIdeal(pl);
printfline("playerEquipIdealSafe: stats, activeWeap:%d invEqIndex:%i", pl.activeweapon, pl.inventoryEquippedIndex);
}