diff --git a/source/src/bot/ac_bot.cpp b/source/src/bot/ac_bot.cpp index 9831cdb1..58e6260d 100644 --- a/source/src/bot/ac_bot.cpp +++ b/source/src/bot/ac_bot.cpp @@ -27,10 +27,12 @@ void CACBot::Spawn() m_pMyEnt->eyeheight = 4.25f; m_pMyEnt->aboveeye = 0.7f; m_pMyEnt->radius = 1.1f; + m_pMyEnt->primary = m_pMyEnt->nextprimary = rnd(3)+GUN_PISTOL; spawnplayer(m_pMyEnt); m_pMyEnt->targetyaw = m_pMyEnt->yaw = m_pMyEnt->targetpitch = m_pMyEnt->pitch = 0.0f; + m_pMyEnt->primary = m_pMyEnt->nextprimary = GUN_ASSULT; m_pMyEnt->move = 0; m_pMyEnt->enemy = NULL; m_pMyEnt->maxspeed = 16.0f; @@ -40,8 +42,9 @@ void CACBot::Spawn() m_pMyEnt->roll = 0; m_pMyEnt->state = CS_ALIVE; m_pMyEnt->anger = 0; - m_pMyEnt->pBot = this; - loopi(NUMGUNS) m_pMyEnt->ammo[i] = 0; + m_pMyEnt->pBot = this; +// loopi(NUMGUNS) m_pMyEnt->ammo[i] = m_pMyEnt->mag[i] = 0; + /* UNDONE m_pMyEnt->ammo[GUN_FIST] = 1; if(m_noitems) @@ -92,9 +95,9 @@ void CACBot::Spawn() }*/ m_pMyEnt->ammo[GUN_KNIFE] = 1; - m_pMyEnt->ammo[GUN_SEMIPISTOL] = 5; - m_pMyEnt->mag[GUN_SEMIPISTOL] = 5; - SelectGun(GUN_SEMIPISTOL); + m_pMyEnt->ammo[GUN_PISTOL] = 5; + m_pMyEnt->mag[GUN_PISTOL] = 5; + SelectGun(m_pMyEnt->primary); m_eCurrentBotState = STATE_NORMAL; m_iShootDelay = m_iChangeWeaponDelay = 0; @@ -137,7 +140,8 @@ void CACBot::Spawn() void CACBot::BotPain(int damage, dynent *d) { if(m_pMyEnt->state!=CS_ALIVE || editmode || intermission) return; - int ad = damage*(m_pMyEnt->armourtype+1)*20/100; // let armour absorb when possible +//fixmebot + int ad = damage/**(m_pMyEnt->armourtype+1)*20/100*/; // let armour absorb when possible if(ad>m_pMyEnt->armour) ad = m_pMyEnt->armour; m_pMyEnt->armour -= ad; damage -= ad; @@ -242,17 +246,11 @@ void CACBot::PickUp(int n) int ammo = np*2; switch(ents[n].type) { - case I_SEMIPISTOL: AddItem(n, m_pMyEnt->ammo[1], ammo); break; - case I_AUTOPISTOL: AddItem(n, m_pMyEnt->ammo[2], ammo); break; - case I_SHOTGUN: AddItem(n, m_pMyEnt->ammo[3], ammo); break; - case I_SNIPER: AddItem(n, m_pMyEnt->ammo[4], ammo); break; - case I_SUBGUN: AddItem(n, m_pMyEnt->ammo[5], ammo); break; - case I_CARBINE: AddItem(n, m_pMyEnt->ammo[6], ammo); break; - case I_SEMIRIFLE: AddItem(n, m_pMyEnt->ammo[7], ammo); break; - case I_AUTORIFLE: AddItem(n, m_pMyEnt->ammo[8], ammo); break; - case I_GRENADE: AddItem(n, m_pMyEnt->ammo[9], ammo); break; + case I_CLIPS: AddItem(n, m_pMyEnt->ammo[GUN_PISTOL], ammo); break; + case I_AMMO: AddItem(n, m_pMyEnt->ammo[m_pMyEnt->primary], ammo); break; + case I_GRENADE: AddItem(n, m_pMyEnt->ammo[GUN_GRENADE], ammo); break; - case I_HELMET: +/* case I_HELMET: // (100h/100g only absorbs 166 damage) if(m_pMyEnt->armourtype==A_YELLOW && m_pMyEnt->armour>66) break; AddItem(n, m_pMyEnt->armour, 20); @@ -264,9 +262,9 @@ void CACBot::PickUp(int n) case OBJ_ITEM: AddItem(n, m_pMyEnt->quadmillis, 60); - break; + break;*/ - case TRIGGER: +/* case TRIGGER: ents[n].spawned = false; triggertime = lastmillis; trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer @@ -280,8 +278,8 @@ void CACBot::PickUp(int n) m_iCheckEntsDelay = lastmillis + RandomLong(5000, 10000); } - BotManager.PickNextTrigger(); - break; + BotManager.PickNextTrigger(); + break;*/ }; }; @@ -290,10 +288,11 @@ void CACBot::AddItem(int i, int &v, int spawnsec) if((i>=sents.length()) || (!sents[i].spawned)) return; - if(v>=itemstats[ents[i].type-I_SEMIPISTOL].max) // don't pick up if not needed +//fixmebot + if(v>=itemstats[ents[i].type-1].max) // don't pick up if not needed return; - itemstat &is = itemstats[ents[i].type-I_SEMIPISTOL]; + itemstat &is = itemstats[ents[i].type-1]; ents[i].spawned = false; v += is.add; if(v>is.max) v = is.max; @@ -301,7 +300,7 @@ void CACBot::AddItem(int i, int &v, int spawnsec) sents[i].spawned = false; sents[i].spawnsecs = spawnsec; - addmsg(1, 4, SV_ITEMPICKUP, i, m_classicsp ? 100000 : spawnsec, false); + addmsg(1, 3, SV_BOTITEMPICKUP, i, m_classicsp ? 100000 : spawnsec); // HACK: Reset ent goal if bot was looking for for this ent if (m_pTargetEnt == &ents[i]) @@ -310,7 +309,7 @@ void CACBot::AddItem(int i, int &v, int spawnsec) m_vGoal = g_vecZero; ResetWaypointVars(); m_iCheckEntsDelay = lastmillis + RandomLong(5000, 10000); - } + }; } // AC Bot class end diff --git a/source/src/bot/ac_bot.h b/source/src/bot/ac_bot.h index e81e3896..502d5627 100644 --- a/source/src/bot/ac_bot.h +++ b/source/src/bot/ac_bot.h @@ -29,6 +29,7 @@ public: // AI Functions virtual bool ChoosePreferredWeapon(void); + void Reload(int Gun); virtual entity *SearchForEnts(bool bUseWPs, float flRange=9999.0f, float flMaxHeight=JUMP_HEIGHT); virtual bool HeadToTargetEnt(void); diff --git a/source/src/bot/ac_bot_ai.cpp b/source/src/bot/ac_bot_ai.cpp index 6fe3f244..745e45f6 100644 --- a/source/src/bot/ac_bot_ai.cpp +++ b/source/src/bot/ac_bot_ai.cpp @@ -21,39 +21,105 @@ weaponinfo_s WeaponInfoTable[MAX_WEAPONS] = { // KNIFE { TYPE_MELEE, 0.0f, 3.5f, 0.0f, 5.0f, 1 }, - // SEMI PISTOL - { TYPE_NORMAL, 5.0f, 12.0f, 0.0f, 50.0f, 4 }, - // AUTO PISTOL - { TYPE_AUTO, 10.0f, 20.0f, 0.0f, 100.0f, 10 }, + // PISTOL + { TYPE_NORMAL, 5.0f, 12.0f, 0.0f, 50.0f, 6 }, // SHOTGUN - { TYPE_SHOTGUN, 15.0f, 25.0f, 10.0f, 70.0f, 3 }, - // SNIPER - { TYPE_SNIPER, 12.0f, 25.0f, 0.0f, 100.0f, 4 }, + { TYPE_SHOTGUN, 0.0f, 20.0f, 0.0f, 70.0f, 4 }, // SUBGUN - { TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 4 }, - - /* EDIT: Adrian Henke - those are not used yet - // CARBINE - { TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 4 }, - // SEMI RIFLE - { TYPE_SNIPER, 12.0f, 25.0f, 0.0f, 100.0f, 4 }, - // AUTO RIFLE - { TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 4 }, - */ - + { TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 15 }, + // SNIPER + { TYPE_SNIPER, 15.0f, 25.0f, 0.0f, 100.0f, 3 }, + // ASSAULT + { TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 10 }, // GRENADE - { TYPE_GRENADE, 12.0f, 25.0f, 0.0f, 100.0f, 4 } + { TYPE_GRENADE, 12.0f, 25.0f, 0.0f, 50.0f, 1 } }; // Code of CACBot - Start bool CACBot::ChoosePreferredWeapon() { - if (CBot::ChoosePreferredWeapon()) - return true; - else - return SelectGun(GUN_KNIFE); -} + printf("chose weapon\n"); + TMultiChoice WeaponChoices; + short sWeaponScore; + float flDist = GetDistance(m_pMyEnt->enemy->o); + + if ((m_iChangeWeaponDelay > lastmillis) && (m_pMyEnt->ammo[m_pMyEnt->gunselect])) + { + if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType != TYPE_MELEE) || (flDist <= 3.5f)) + return true; + } + + // Choose a weapon + for(int i=0;iammo[i] == 0) continue; + + sWeaponScore = 5; // Minimal score for a weapon + + if ((flDist >= WeaponInfoTable[i].flMinDesiredDistance) && + (flDist <= WeaponInfoTable[i].flMaxDesiredDistance)) + { + // In desired range for this weapon + sWeaponScore += 5; // Increase score much + } + else if ((flDist < WeaponInfoTable[i].flMinFireDistance) || + (flDist > WeaponInfoTable[i].flMaxFireDistance)) + continue; // Wrong distance for this weapon + + // The ideal distance would be between the Min and Max desired distance. + // Score on the difference of the avarage of the Min and Max desired distance. + float flAvarage = (WeaponInfoTable[i].flMinDesiredDistance + + WeaponInfoTable[i].flMaxDesiredDistance) / 2.0f; + float flIdealDiff = fabs(flDist - flAvarage); + + if (flIdealDiff < 0.5f) // Close to ideal distance + sWeaponScore += 4; + else if (flIdealDiff <= 1.0f) + sWeaponScore += 2; + + // Now rate the weapon on available ammo... + if (WeaponInfoTable[i].sMinDesiredAmmo > 0) + { + // Calculate how much percent of the min desired ammo the bot has + float flDesiredPercent = (float(m_pMyEnt->mag[i]) / + float(WeaponInfoTable[i].sMinDesiredAmmo)) * + 100.0f; + + if(flDesiredPercent >= 175.0f) + sWeaponScore += 4; + else if(flDesiredPercent >= 100.0f) + sWeaponScore += 3; + } + + WeaponChoices.Insert(i, sWeaponScore); + } + + int WeaponSelect; + if (WeaponChoices.GetSelection(WeaponSelect)) + { + m_iChangeWeaponDelay = lastmillis + RandomLong(2000, 8000); + m_bShootAtFeet = ((WeaponInfoTable[WeaponSelect].eWeaponType==TYPE_ROCKET) && + (RandomLong(1, 100) <= + m_pBotSkill->sShootAtFeetWithRLPercent)); + if(SelectGun(WeaponSelect)) + { + if(m_pMyEnt->mag[WeaponSelect]==0) reload(m_pMyEnt); // fixme, more intelligent reload + printf("gun selected %i\n", WeaponSelect); + return true; + } + } + + return false; + + +}; + +void CACBot::Reload(int Gun) +{ + +}; entity *CACBot::SearchForEnts(bool bUseWPs, float flRange, float flMaxHeight) { @@ -86,56 +152,26 @@ entity *CACBot::SearchForEnts(bool bUseWPs, float flRange, float flMaxHeight) if (OUTBORD(e.x, e.y)) continue; bool bInteresting = false; - short sAmmo = 0, sMaxAmmo = itemstats[e.type-I_SEMIPISTOL].max; - + short sAmmo = 0, sMaxAmmo = 0; + //fixmebot switch(e.type) { - case I_SEMIPISTOL: - bInteresting = (m_pMyEnt->ammo[1]ammo[1]; + case I_CLIPS: + sMaxAmmo = itemstats[GUN_PISTOL-1].max; + bInteresting = (m_pMyEnt->ammo[GUN_PISTOL]ammo[GUN_PISTOL]; break; - case I_AUTOPISTOL: - bInteresting = (m_pMyEnt->ammo[2]ammo[2]; + case I_AMMO: + sMaxAmmo = itemstats[m_pMyEnt->primary-1].max; + bInteresting = (m_pMyEnt->ammo[m_pMyEnt->primary]ammo[m_pMyEnt->primary]; break; - case I_SHOTGUN: - bInteresting = (m_pMyEnt->ammo[3]ammo[3]; - break; - case I_SNIPER: - bInteresting = (m_pMyEnt->ammo[4]ammo[4]; - break; - case I_SUBGUN: - bInteresting = (m_pMyEnt->ammo[5]ammo[4]; - break; - case I_CARBINE: - bInteresting = (m_pMyEnt->ammo[6]ammo[4]; - break; - case I_SEMIRIFLE: - bInteresting = (m_pMyEnt->ammo[7]ammo[4]; - break; - case I_AUTORIFLE: - bInteresting = (m_pMyEnt->ammo[8]ammo[4]; - break; case I_GRENADE: - bInteresting = (m_pMyEnt->ammo[9]ammo[4]; - break; - case I_HELMET: - bInteresting = ((m_pMyEnt->armourarmourtype!=A_YELLOW || m_pMyEnt->armour<=66)); - sAmmo = m_pMyEnt->armour; + sMaxAmmo = itemstats[GUN_GRENADE-1].max; + bInteresting = (m_pMyEnt->ammo[GUN_GRENADE]ammo[GUN_GRENADE]; break; - case I_ARMOUR: - bInteresting = (m_pMyEnt->armourarmour; - break; - } + }; if (!bInteresting) continue; // Not an interesting item, skip diff --git a/source/src/bot/bot.cpp b/source/src/bot/bot.cpp index 59f72a6b..4cdfcd85 100644 --- a/source/src/bot/bot.cpp +++ b/source/src/bot/bot.cpp @@ -376,9 +376,7 @@ bool CBot::SelectGun(int Gun) if (m_pMyEnt->ammo[Gun]) { //temporary doc out argh - //if (m_pMyEnt->gunselect != Gun) - // botplaysound(S_WEAPLOAD, m_pMyEnt); - +/* if (m_pMyEnt->gunselect != Gun) botplaysound(S_WEAPLOAD, m_pMyEnt); */ m_pMyEnt->gunselect = Gun; return true; } diff --git a/source/src/bot/bot.h b/source/src/bot/bot.h index 522ebf1c..db4a3c16 100644 --- a/source/src/bot/bot.h +++ b/source/src/bot/bot.h @@ -86,6 +86,10 @@ inline void debugbeam(vec &s, vec &e) { /*if (!dedserv) particle_trail(1, 500, s #define DOWN (1<<6) #define DIR_NONE 0 +//fixmebot +#define m_sp 1000 +#define m_classicsp 1000 + enum EBotCommands // For voting of bot commands { COMMAND_ADDBOT=0, @@ -146,7 +150,7 @@ enum ECurrentBotState STATE_HUNT, // Bot is hunting an enemy STATE_ENT, // Bot is heading to an entity STATE_SP, // Bot is doing sp specific stuff - STATE_NORMAL // Bot is doing normal navaigation + STATE_NORMAL // Bot is doing normal navigation }; struct unreachable_ent_s @@ -233,10 +237,10 @@ public: void AimToIdeal(void); float ChangeAngle(float speed, float ideal, float current); bool SelectGun(int Gun); - virtual void CheckItemPickup(void) = NULL; - virtual void PickUp(int n) = NULL; - virtual void AddItem(int i, int &v, int spawnsec) = NULL; - virtual void BotPain(int damage, dynent *d) = NULL; + virtual void CheckItemPickup(void) = 0; + virtual void PickUp(int n) = 0; + virtual void AddItem(int i, int &v, int spawnsec) = 0; + virtual void BotPain(int damage, dynent *d) = 0; void SendBotInfo(void); float GetDistance(const vec &o); float GetDistance(const vec &v1, const vec &v2); @@ -275,10 +279,10 @@ public: virtual bool ChoosePreferredWeapon(void); virtual entity *SearchForEnts(bool bUseWPs, float flRange=9999.0f, float flMaxHeight=JUMP_HEIGHT) = NULL; - virtual bool HeadToTargetEnt(void) = NULL; + virtual bool HeadToTargetEnt(void) = 0; bool CheckItems(void); bool InUnreachableList(entity *e); - virtual bool DoSPStuff(void) = NULL; + virtual bool DoSPStuff(void) = 0; vec GetEnemyPos(dynent *d); bool AStar(void); float AStarCost(waypoint_s *pWP1, waypoint_s *pWP2); diff --git a/source/src/bot/bot_ai.cpp b/source/src/bot/bot_ai.cpp index e75b7016..c3fadb31 100644 --- a/source/src/bot/bot_ai.cpp +++ b/source/src/bot/bot_ai.cpp @@ -919,7 +919,7 @@ void CBot::ShootEnemy() // Add shoot delay m_iShootDelay = lastmillis + GetShootDelay(); } - ChoosePreferredWeapon(); + ChoosePreferredWeapon(); } #ifndef RELEASE_BUILD else @@ -2190,9 +2190,10 @@ void CBot::HearSound(int n, vec *o) if (m_pMyEnt->enemy) return; // Is the sound not interesting? - if ((n==S_DIE1) || (n==S_DIE2) || (n==S_DEATHR) || (n==S_DEATHE) || (n==S_DEATHS) || +//fixmebot +/* if ((n==S_DIE1) || (n==S_DIE2) || (n==S_DEATHR) || (n==S_DEATHE) || (n==S_DEATHS) || (n==S_DEATHB) || (n==S_DEATHH) || (n==S_DEATHD) || (n==S_PIGGR2)) - return; + return;*/ int soundvol = m_pBotSkill->iMaxHearVolume - (int)(GetDistance(*o)*3*m_pBotSkill->iMaxHearVolume/255); diff --git a/source/src/bot/bot_util.h b/source/src/bot/bot_util.h index f3327a02..1b9d4dd5 100644 --- a/source/src/bot/bot_util.h +++ b/source/src/bot/bot_util.h @@ -471,7 +471,7 @@ private: // ================================================================== -// Code of TMutiChoice - Begin +// Code of TMultiChoice - Begin // ================================================================== template class TMultiChoice diff --git a/source/src/bot/botmanager.cpp b/source/src/bot/botmanager.cpp index 0e4b0789..b8920eee 100644 --- a/source/src/bot/botmanager.cpp +++ b/source/src/bot/botmanager.cpp @@ -970,15 +970,6 @@ dynent *CBotManager::CreateBot(const char *team, const char *skill, const char * m->pBot->Spawn(); - // edit: driAn - loopi(3) - { - m->mdl[i] = i; - m->animstate[i].frm = 0; - m->animstate[i].anim = 0; - m->animstate[i].lastTime = 0; - }; - bots.add(m); return m; diff --git a/source/src/client.cpp b/source/src/client.cpp index e5011809..19bb1541 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -41,27 +41,27 @@ void throttle() }; void ctf_team(char *name) -{ - bool isvalidteam = strcmp(name, "RVSF") == 0 || strcmp(name, "CLA") == 0; - if(strcmp(name, player1->team) == 0 && isvalidteam) return; - c2sinit = false; - ctf_death(); - if(isvalidteam) strcpy(player1->team, name); - else strcpy(player1->team, rb_team_string(rnd(2))); - player1->lastaction = lastmillis; +{ + bool isvalidteam = strcmp(name, "RVSF") == 0 || strcmp(name, "CLA") == 0; + if(strcmp(name, player1->team) == 0 && isvalidteam) return; + c2sinit = false; + ctf_death(); + if(isvalidteam) strcpy(player1->team, name); + else strcpy(player1->team, rb_team_string(rnd(2))); + player1->lastaction = lastmillis; player1->state = CS_DEAD; }; -void newname(char *name) { c2sinit = false; strn0cpy(player1->name, name, 16); }; -void newskin(int skin) { c2sinit=false; player1->nextskin = max(0, min(skin, (rb_team_int(player1->team)==TEAM_CLA ? 3 : 5))); }; -void newteam(char *name) -{ - if(m_teammode) ctf_team(name); - else { strn0cpy(player1->team, name, 5); c2sinit=false; }; +void newname(char *name) { c2sinit = false; strn0cpy(player1->name, name, 16); }; +void newskin(int skin) { player1->nextskin = max(0, min(skin, (rb_team_int(player1->team)==TEAM_CLA ? 3 : 5))); }; +void newteam(char *name) +{ + if(m_teammode) ctf_team(name); + else { strn0cpy(player1->team, name, 5); c2sinit=false; }; }; COMMANDN(team, newteam, ARG_1STR); -COMMANDN(name, newname, ARG_1STR); +COMMANDN(name, newname, ARG_1STR); COMMANDN(skin, newskin, ARG_1INT); void connects(char *servername) @@ -262,7 +262,7 @@ void c2sinfo(dynent *d) // send update to the server c2sinit = true; putint(p, SV_INITC2S); sendstring(player1->name, p); - sendstring(player1->team, p); + sendstring(player1->team, p); putint(p, player1->skin); putint(p, player1->lifesequence); }; @@ -326,14 +326,8 @@ void gets2c() // get updates from the server return; } }; - -void lsinfo() -{ - printf("lifesequences:\nyou\t%i\n", player1->lifesequence); - loopv(players) - { - dynent *d = players[i]; - if(!d) continue; - printf("%s\t%i\n", d->name, d->lifesequence); - } -}; COMMAND(lsinfo, ARG_NONE); + +void lsinfo() +{ + printf("%i %i\n", player1->ammo[player1->gunselect], player1->mag[player1->gunselect]); +}; COMMAND(lsinfo, ARG_NONE); diff --git a/source/src/clientextras.cpp b/source/src/clientextras.cpp index 44e6848a..9a89ccc3 100644 --- a/source/src/clientextras.cpp +++ b/source/src/clientextras.cpp @@ -45,13 +45,13 @@ void renderclient(dynent *d, bool team, char *mdlname, bool vwep, float scale) rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d->o.x, mz, d->o.y, d->yaw+90, d->pitch/2, team, scale, speed, 0, basetime); }; -extern int democlientnum; - -void renderplayer(dynent *d) -{ - if(!d) return; - - int team = rb_team_int(d->team); +extern int democlientnum; + +void renderplayer(dynent *d) +{ + if(!d) return; + + int team = rb_team_int(d->team); sprintf_sd(mdl)("playermodels/%s/0%i", team==TEAM_CLA ? "terrorist" : "counterterrorist", 1 + max(0, min(d->skin, (team==TEAM_CLA ? 3 : 5)))); renderclient(d, isteam(player1->team, d->team), mdl, false, 1.6f); @@ -59,7 +59,7 @@ void renderplayer(dynent *d) { sprintf_sd(vwep)("weapons/%s/world", hudgunnames[d->gunselect]); renderclient(d, isteam(player1->team, d->team), vwep, true, 1.6f); - }; + }; } void renderclients() @@ -73,9 +73,9 @@ void renderclients() d->gunselect = player1->gunselect; d->moving = true; d->health != 1 ? d->state=CS_ALIVE : d->state=CS_DEAD; - }; + }; renderplayer(d); - }; + }; if(player1->state==CS_DEAD) renderplayer(player1); }; @@ -144,6 +144,10 @@ void renderscores() scorelines.setsize(0); if(!demoplayback) renderscore(player1); loopv(players) if(players[i]) renderscore(players[i]); + + // Added by Rick: Render Score for bots + BotManager.RenderBotScore(); + sortmenu(menu, 0, scorelines.length()); if(m_teammode) { diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 03b5993a..08dfdbd3 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -1,626 +1,679 @@ -// clientgame.cpp: core game related stuff - -#include "cube.h" -#define sleep my_sleep -int nextmode = 0; // nextmode becomes gamemode after next map load -VAR(gamemode, 1, 0, 0); - -flaginfo flaginfos[2]; - -void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; -COMMAND(mode, ARG_1INT); - -bool intermission = false; - -dynent *player1 = newdynent(); // our client -dvector players; // other clients - -vector physents; - -VAR(sensitivity, 0, 10, 1000); -VAR(sensitivityscale, 1, 1, 100); -VAR(invmouse, 0, 0, 1); - -int lastmillis = 0; -int curtime; -string clientmap; - -extern int framesinmap; - -char *getclientmap() { return clientmap; }; - -void resetmovement(dynent *d) -{ - d->k_left = false; - d->k_right = false; - d->k_up = false; - d->k_down = false; - d->jumpnext = false; - d->strafe = 0; - d->move = 0; -}; - -extern bool c2sinit; - -void spawnstate(dynent *d) // reset player state not persistent accross spawns -{ - resetmovement(d); - d->vel.x = d->vel.y = d->vel.z = 0; - d->onfloor = false; - d->timeinair = 0; - d->health = 100; - d->armour = 0; - //d->hasarmour = false; - //d->armourtype = A_BLUE; - d->akimbomillis = 0; - d->gunselect = GUN_PISTOL; - d->gunwait = 0; - d->attacking = false; - d->lastaction = 0; - if(d==player1) - { - gun_changed = true; - player1->primary = player1->nextprimary; - if(player1->skin!=player1->nextskin) - { - c2sinit=false; - player1->skin=player1->nextskin; - } - scoped = false; - }; - radd(d); - d->akimbo = false; -}; - -dynent *newdynent() // create a new blank player or monster -{ - dynent *d = (dynent *)gp()->alloc(sizeof(dynent)); - d->o.x = 0; - d->o.y = 0; - d->o.z = 0; - d->yaw = 270; - d->pitch = 0; - d->roll = 0; - d->maxspeed = 16; //16 max speed, 14 max with armour - d->outsidemap = false; - d->inwater = false; - d->radius = 1.1f; - d->eyeheight = 4.25f; - d->aboveeye = 0.7f; - d->frags = 0; - d->flagscore = 0; // EDIT: AH - d->plag = 0; - d->ping = 0; - d->lastupdate = lastmillis; - d->enemy = NULL; - d->monsterstate = 0; - d->name[0] = d->team[0] = 0; - d->blocked = false; - d->lifesequence = 0; - d->state = CS_ALIVE; - d->shots = 0; - d->reloading = false; - d->primary = d->nextprimary = GUN_ASSULT; - d->hasarmour = false; - d->gunselect = GUN_PISTOL; - d->onladder = false; - d->isphysent = false; - d->skin = d->nextskin = 0; - spawnstate(d); - return d; -}; - -void ctf_death() // EDIT: AH -{ - int flag = rb_opposite(rb_team_int(player1->team)); - flaginfo &f = flaginfos[flag]; - if(f.state==CTFF_STOLEN && f.thief==player1) - { - addmsg(1, 2, SV_FLAGDROP, flag); - f.flag->spawned = false; - f.state = CTFF_DROPPED; - }; -}; - -void respawnself() -{ - spawnplayer(player1); - showscores(false); -}; - -void respawn() -{ - if(player1->state==CS_DEAD) - { - player1->attacking = false; - if(m_arena) { conoutf("waiting for new round to start..."); return; }; - respawnself(); - }; -}; - -void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, char *&lastname, bool &oneteam) -//void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam) -{ - if(d->state!=CS_DEAD) - { - alive++; - if(lastteam && strcmp(lastteam, d->team)) oneteam = false; - lastteam = d->team; - lastname = d->name; - } - else - { - dead++; - }; -}; - -int arenarespawnwait = 0; -int arenadetectwait = 0; - -void arenarespawn() -{ - if (!m_arena) return; - - if(arenarespawnwait) - { - if(arenarespawnwait0 && (alive<=1 || (m_teammode && oneteam))) - { - conoutf("arena round is over! next round in 5 seconds..."); - if(alive) - { - if(m_teammode) - conoutf("team %s has won the round", (int)lastteam); - else - conoutf("%s is the survior!", (int)lastname); - } - else conoutf("everyone died!"); - arenarespawnwait = lastmillis+5000; - arenadetectwait = lastmillis+10000; - player1->roll = 0; - }; - }; -}; - - -void checkakimbo(int time) -{ - if(player1->gunselect==GUN_PISTOL && player1->akimbo && (player1->akimbomillis -= time)<0) - { - player1->akimbomillis = 0; - player1->akimbo = false; - playsoundc(S_PUPOUT); - //conoutf("quad damage is over"); - }; -}; - -void zapdynent(dynent *&d) -{ - if(d) gp()->dealloc(d, sizeof(dynent)); - d = NULL; -}; - -extern int democlientnum; - -void otherplayers() -{ - loopv(players) if(players[i]) - { - const int lagtime = lastmillis-players[i]->lastupdate; - if(lagtime>1000 && players[i]->state==CS_ALIVE) - { - players[i]->state = CS_LAGGED; - continue; - }; - if(lagtime && players[i]->state != CS_DEAD && (!demoplayback || i!=democlientnum)) moveplayer(players[i], 2, false); // use physics to extrapolate player position - }; -}; - -extern void explode_nade(physent *i); - -void mphysents() -{ - loopv(physents) if(physents[i]) - { - physent *p = physents[i]; - if(p->state == NADE_THROWED) moveplayer(p, 2, false); - - if(lastmillis - p->millis >= p->timetolife) - { - if(p->state==NADE_ACTIVATED || p->state==NADE_THROWED) explode_nade(physents[i]); - physents.remove(i); - i--; - }; - }; -}; - -int sleepwait = 0; -string sleepcmd; -void sleep(char *msec, char *cmd) { sleepwait = atoi(msec)+lastmillis; strcpy_s(sleepcmd, cmd); }; -COMMAND(sleep, ARG_2STR); - -void updateworld(int millis) // main game update loop -{ - if(lastmillis) - { - curtime = millis - lastmillis; - if(sleepwait && lastmillis>sleepwait) { execute(sleepcmd); sleepwait = 0; }; - physicsframe(); - checkakimbo(curtime); - //if(m_arena) arenarespawn(); - arenarespawn(); - moveprojectiles((float)curtime); - demoplaybackstep(); - if(!demoplayback) - { - if(getclientnum()>=0) shoot(player1, worldpos); // only shoot when connected to server - gets2c(); // do this first, so we have most accurate information when our player moves - }; - mphysents(); - otherplayers(); - if(!demoplayback) - { - //put game mode extra call here - if(player1->state==CS_DEAD) - { - if(lastmillis-player1->lastaction<2000) - { - player1->move = player1->strafe = 0; - moveplayer(player1, 10, false); - }; - } - else if(!intermission) - { - moveplayer(player1, 20, true); - checkitems(); - }; - c2sinfo(player1); // do this last, to reduce the effective frame lag - }; - }; - lastmillis = millis; -}; - -void entinmap(dynent *d) // brute force but effective way to find a free spawn spot in the map -{ - loopi(100) // try max 100 times - { - float dx = (rnd(21)-10)/10.0f*i; // increasing distance - float dy = (rnd(21)-10)/10.0f*i; - d->o.x += dx; - d->o.y += dy; - if(collide(d, true, 0, 0)) return; - d->o.x -= dx; - d->o.y -= dy; - }; - conoutf("can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y); - // leave ent at original pos, possibly stuck -}; - -// EDIT: AH -int securespawndist = 15; - -// Returns -1 for a free place, if not it returns the vdist to the nearest enemy -int nearestenemy(vec *v, string team) -{ - float nearestPlayerDistSquared = -1; - loopv(players) - { - dynent *other = players[i]; - if(!other) continue; - if(isteam(team,other->team))continue; // its a teammate - vec place = {v->x, v->y, v->z}; - vdistsquared(distsquared, t, place, other->o); - if(nearestPlayerDistSquared == -1) nearestPlayerDistSquared = distsquared; // first run - else if(distsquared < nearestPlayerDistSquared) nearestPlayerDistSquared = distsquared; // if a player is closer - }; - if(nearestPlayerDistSquared >= securespawndist * securespawndist || nearestPlayerDistSquared == -1) return -1; // a distance more than securespawndist means the place is free - else return sqrt(nearestPlayerDistSquared); -}; - -int spawncycle = -1; -int fixspawn = 2; - -void spawnplayer(dynent *d, bool secure) // place at random spawn -{ - loopj(10) // EDIT: AH - { - int r = fixspawn-->0 ? 4 : rnd(10)+1; - loopi(r) spawncycle = findplayerstart(m_teammode ? rb_team_int(d->team) : 100, spawncycle+1); - if(spawncycle!=-1 && secure) - { - entity &e = ents[spawncycle]; - vec pos = { e.x, e.y, e.z }; - if(nearestenemy(&pos, d->team) == -1) break; - } else break; - }; - if(spawncycle!=-1) - { - d->o.x = ents[spawncycle].x; - d->o.y = ents[spawncycle].y; - d->o.z = ents[spawncycle].z; - d->yaw = ents[spawncycle].attr1; - d->pitch = 0; - d->roll = 0; - } - else - { - d->o.x = d->o.y = (float)ssize/2; - d->o.z = 4; - }; - entinmap(d); - spawnstate(d); - d->state = CS_ALIVE; -}; - -// movement input code - -#define dir(name,v,d,s,os) void name(bool isdown) { player1->s = isdown; player1->v = isdown ? d : (player1->os ? -(d) : 0); player1->lastmove = lastmillis; }; - -dir(backward, move, -1, k_down, k_up); -dir(forward, move, 1, k_up, k_down); -dir(left, strafe, 1, k_left, k_right); -dir(right, strafe, -1, k_right, k_left); - -void attack(bool on) -{ - if(intermission) return; - if(editmode) editdrag(on); - else if(player1->attacking = on) respawn(); -}; - -void jumpn(bool on) { if(!intermission && (player1->jumpnext = on) ) respawn(); }; - -COMMAND(backward, ARG_DOWN); -COMMAND(forward, ARG_DOWN); -COMMAND(left, ARG_DOWN); -COMMAND(right, ARG_DOWN); -COMMANDN(jump, jumpn, ARG_DOWN); -COMMAND(attack, ARG_DOWN); -COMMAND(showscores, ARG_DOWN); - -void fixplayer1range() -{ - const float MAXPITCH = 90.0f; - if(player1->pitch>MAXPITCH) player1->pitch = MAXPITCH; - if(player1->pitch<-MAXPITCH) player1->pitch = -MAXPITCH; - while(player1->yaw<0.0f) player1->yaw += 360.0f; - while(player1->yaw>=360.0f) player1->yaw -= 360.0f; -}; - -void mousemove(int dx, int dy) -{ - if(player1->state==CS_DEAD || intermission) return; - const float SENSF = 33.0f; // try match quake sens - player1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); - player1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); - fixplayer1range(); -}; - -// damage arriving from the network, monsters, yourself, all ends up here. - -void selfdamage(int damage, int actor, dynent *act) -{ printf("selfdmg\n"); - if(player1->state!=CS_ALIVE || editmode || intermission) return; - damageblend(damage); - demoblend(damage); - int ad = damage*30/100;//int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible - if(ad>player1->armour) ad = player1->armour; - player1->armour -= ad; - damage -= ad; - float droll = damage/0.5f; - player1->roll += player1->roll>0 ? droll : (player1->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage - if((player1->health -= damage)<=0) - { - if(actor==-2) - { - conoutf("you got killed by %s!", (int)&act->name); - } - else if(actor==-1) - { - actor = getclientnum(); - conoutf("you suicided!"); - addmsg(1, 2, SV_FRAGS, --player1->frags); - } - else - { - dynent *a = getclient(actor); - if(a) - { - if(isteam(a->team, player1->team)) - { - conoutf("you got fragged by a teammate (%s)", (int)a->name); - } - else - { - conoutf("you got fragged by %s", (int)a->name); - }; - }; - }; - // EDIT: AH - if(m_ctf) ctf_death(); - showscores(true); - addmsg(1, 2, SV_DIED, actor); - player1->lifesequence++; - player1->attacking = false; - player1->state = CS_DEAD; - player1->oldpitch = player1->pitch; - player1->pitch = 0; - player1->roll = 60; - playsound(S_DIE1+rnd(2)); - spawnstate(player1); - player1->lastaction = lastmillis; - } - else - { - playsound(S_PAIN6); - }; -}; - -void timeupdate(int timeremain) -{ - if(!timeremain) - { - intermission = true; - player1->attacking = false; - conoutf("intermission:"); - conoutf("game has ended!"); - showscores(true); - } - else - { - conoutf("time remaining: %d minutes", timeremain); - }; -}; - -dynent *getclient(int cn) // ensure valid entity -{ - if(cn<0 || cn>=MAXCLIENTS) - { - neterr("clientnum"); - return NULL; - }; - while(cn>=players.length()) players.add(NULL); - return players[cn] ? players[cn] : (players[cn] = newdynent()); -}; - -void initclient() -{ - clientmap[0] = 0; - initclientnet(); -}; - -entity flagdummies[2]; // in case the map does not provide flags - -void preparectf(bool cleanonly=false) -{ - loopi(2) flaginfos[i].flag = &flagdummies[i]; - if(!cleanonly) - { - loopv(ents) - { - entity &e = ents[i]; - if(e.type==CTF_FLAG) - { - e.spawned = true; - if(e.attr2>2) { conoutf("invalid ctf-flag entity (%i)", i); e.attr2 = 0; }; - flaginfo &f = flaginfos[e.attr2]; - f.flag = &e; - f.state = CTFF_INBASE; - f.originalpos.x = (float) e.x; - f.originalpos.y = (float) e.y; - f.originalpos.z = (float) e.z; - }; - }; - newteam(player1->team); // ensure valid team - }; -}; - -void startmap(char *name) // called just after a map load -{ - //if(netmapstart()) { gamemode = 0;}; //needs fixed to switch modes? - netmapstart(); //should work - sleepwait = 0; - //put call to clear/restart game mode extras here - projreset(); - if(m_ctf) preparectf(); - shotlinereset(); - spawncycle = -1; - spawnplayer(player1, true); - player1->frags = 0; - player1->flagscore = 0; - loopv(players) if(players[i]) players[i]->frags = 0; - resetspawns(); - strcpy_s(clientmap, name); - if(editmode) toggleedit(); - setvar("gamespeed", 100); - setvar("fog", 180); - setvar("fogcolour", 0x8099B3); - showscores(false); - intermission = false; - framesinmap = 0; - conoutf("game mode is %s", (int)modestr(gamemode)); -}; - -COMMANDN(map, changemap, ARG_1STR); - -void suicide() -{ - if (multiplayer()) conoutf("there are others willing to kill you..."); - else conoutf("this is not the answer"); - selfdamage(1000, -1, player1); - //if(d==player1) selfdamage(dm, -1, d); - //else { addmsg(1, 4, SV_DAMAGE, d, dm, d->lifesequence); playsound(S_FALL1+rnd(5), &d->o); }; - //particle_splash(3, 1000, 1000, player1->o); //edit out? - demodamage(1000, player1->o); - playsound(S_SUICIDE, &player1->o); -}; - -COMMAND(suicide, ARG_NONE); - -void killdummies() -{ - loopv(players) if(strcmp(players[i]->name, "dummy") == 0) { players[i]->lastaction = lastmillis; players[i]->health=1; }; -}; COMMAND(killdummies, ARG_NONE); - -// EDIT: AH -void flagaction(int flag, int action) -{ - flaginfo &f = flaginfos[flag]; - if(!f.thief) return; - bool ownflag = flag == rb_team_int(player1->team); - switch(action) - { - case SV_FLAGPICKUP: - { - playsound(S_FLAGPICKUP); - if(f.thief==player1) - { - conoutf("you got the enemy flag"); - f.pick_ack = true; - } - else conoutf("%s got %s flag", (int) f.thief->name, (int)(ownflag ? "your": "the enemy")); - break; - }; - case SV_FLAGDROP: - { - playsound(S_FLAGDROP); - if(f.thief==player1) conoutf("you lost the flag"); - else conoutf("%s lost %s flag", (int) f.thief->name, (int)(ownflag ? "your" : "the enemy")); - break; - }; - case SV_FLAGRETURN: - { - playsound(S_FLAGRETURN); - if(f.thief==player1) conoutf("you returned your flag"); - else conoutf("%s returned %s flag", (int) f.thief->name, (int)(ownflag ? "your" : "the enemy")); - break; - }; - case SV_FLAGSCORE: - { - playsound(S_FLAGSCORE); - if(f.thief==player1) - { - conoutf("you scored"); - addmsg(1, 2, SV_FLAGS, ++player1->flagscore); - } - else conoutf("%s scored for %s team", (int) f.thief->name, (int)(ownflag ? "the enemy" : "your")); - break; - }; - default: break; - }; -}; +// clientgame.cpp: core game related stuff + +#include "cube.h" +#define sleep my_sleep +int nextmode = 0; // nextmode becomes gamemode after next map load +VAR(gamemode, 1, 0, 0); + +flaginfo flaginfos[2]; + +void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; +COMMAND(mode, ARG_1INT); + +bool intermission = false; + +dynent *player1 = newdynent(); // our client +dvector players; // other clients + +vector physents; + +VAR(sensitivity, 0, 10, 1000); +VAR(sensitivityscale, 1, 1, 100); +VAR(invmouse, 0, 0, 1); + +int lastmillis = 0; +int curtime; +string clientmap; + +extern int framesinmap; + +char *getclientmap() { return clientmap; }; + +void resetmovement(dynent *d) +{ + d->k_left = false; + d->k_right = false; + d->k_up = false; + d->k_down = false; + d->jumpnext = false; + d->strafe = 0; + d->move = 0; +}; + +extern bool c2sinit; + +void spawnstate(dynent *d) // reset player state not persistent accross spawns +{ + resetmovement(d); + d->vel.x = d->vel.y = d->vel.z = 0; + d->onfloor = false; + d->timeinair = 0; + d->health = 100; + d->armour = 0; + //d->hasarmour = false; + //d->armourtype = A_BLUE; + d->akimbomillis = 0; + d->gunselect = GUN_PISTOL; + d->gunwait = 0; + d->attacking = false; + d->lastaction = 0; + if(d==player1) + { + gun_changed = true; + player1->primary = player1->nextprimary; + if(player1->skin!=player1->nextskin) + { + c2sinit=false; + player1->skin=player1->nextskin; + } + scoped = false; + }; + radd(d); + d->akimbo = false; +}; + +dynent *newdynent() // create a new blank player or monster +{ + dynent *d = (dynent *)gp()->alloc(sizeof(dynent)); + d->o.x = 0; + d->o.y = 0; + d->o.z = 0; + d->yaw = 270; + d->pitch = 0; + d->roll = 0; + d->maxspeed = 16; //16 max speed, 14 max with armour + d->outsidemap = false; + d->inwater = false; + d->radius = 1.1f; + d->eyeheight = 4.25f; + d->aboveeye = 0.7f; + d->frags = 0; + d->flagscore = 0; // EDIT: AH + d->plag = 0; + d->ping = 0; + d->lastupdate = lastmillis; + d->enemy = NULL; + d->monsterstate = 0; + d->name[0] = d->team[0] = 0; + d->blocked = false; + d->lifesequence = 0; + d->state = CS_ALIVE; + d->shots = 0; + d->reloading = false; + d->primary = d->nextprimary = GUN_ASSULT; + d->hasarmour = false; + d->gunselect = GUN_PISTOL; + d->onladder = false; + d->isphysent = false; + d->skin = d->nextskin = 0; + spawnstate(d); + return d; +}; + +void ctf_death() // EDIT: AH +{ + int flag = rb_opposite(rb_team_int(player1->team)); + flaginfo &f = flaginfos[flag]; + if(f.state==CTFF_STOLEN && f.thief==player1) + { + addmsg(1, 2, SV_FLAGDROP, flag); + f.flag->spawned = false; + f.state = CTFF_DROPPED; + }; +}; + +void respawnself() +{ + spawnplayer(player1); + showscores(false); +}; + +void respawn() +{ + if(player1->state==CS_DEAD) + { + player1->attacking = false; + if(m_arena) { conoutf("waiting for new round to start..."); return; }; + respawnself(); + }; +}; + +void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, char *&lastname, bool &oneteam) +{ + if(d->state!=CS_DEAD) + { + alive++; + if(lastteam && strcmp(lastteam, d->team)) oneteam = false; + lastteam = d->team; + lastname = d->name; + } + else + { + dead++; + }; +}; + +int arenarespawnwait = 0; +int arenadetectwait = 0; + +void arenarespawn() +{ + if (!m_arena) return; + + if(arenarespawnwait) + { + if(arenarespawnwait0 && (alive<=1 || (m_teammode && oneteam))) + { + conoutf("arena round is over! next round in 5 seconds..."); + if(alive) + { + if(m_teammode) + conoutf("team %s has won the round", (int)lastteam); + else + conoutf("%s is the survior!", (int)lastname); + } + else conoutf("everyone died!"); + arenarespawnwait = lastmillis+5000; + arenadetectwait = lastmillis+10000; + player1->roll = 0; + }; + }; +}; + + +void checkakimbo(int time) +{ + if(player1->gunselect==GUN_PISTOL && player1->akimbo && (player1->akimbomillis -= time)<0) + { + player1->akimbomillis = 0; + player1->akimbo = false; + playsoundc(S_PUPOUT); + //conoutf("quad damage is over"); + }; +}; + +void zapdynent(dynent *&d) +{ + if(d) gp()->dealloc(d, sizeof(dynent)); + d = NULL; +}; + +extern int democlientnum; + +void otherplayers() +{ + loopv(players) if(players[i]) + { + const int lagtime = lastmillis-players[i]->lastupdate; + if(lagtime>1000 && players[i]->state==CS_ALIVE) + { + players[i]->state = CS_LAGGED; + continue; + }; + if(lagtime && players[i]->state != CS_DEAD && (!demoplayback || i!=democlientnum)) moveplayer(players[i], 2, false); // use physics to extrapolate player position + }; + // Added by Rick + if (!ishost()) + { + loopv(bots) + { + if(bots[i] && bots[i]->state != CS_DEAD && (!demoplayback)) + moveplayer(bots[i], 2, false); // use physics to extrapolate bot position + } + } + // End add +}; + +extern void explode_nade(physent *i); + +void mphysents() +{ + loopv(physents) if(physents[i]) + { + physent *p = physents[i]; + if(p->state == NADE_THROWED) moveplayer(p, 2, false); + + if(lastmillis - p->millis >= p->timetolife) + { + if(p->state==NADE_ACTIVATED || p->state==NADE_THROWED) explode_nade(physents[i]); + physents.remove(i); + i--; + }; + }; +}; + +int sleepwait = 0; +string sleepcmd; +void sleep(char *msec, char *cmd) { sleepwait = atoi(msec)+lastmillis; strcpy_s(sleepcmd, cmd); }; +COMMAND(sleep, ARG_2STR); + +void updateworld(int millis) // main game update loop +{ + if(lastmillis) + { + curtime = millis - lastmillis; + if(sleepwait && lastmillis>sleepwait) { execute(sleepcmd); sleepwait = 0; }; + physicsframe(); + checkakimbo(curtime); + //if(m_arena) arenarespawn(); + arenarespawn(); + moveprojectiles((float)curtime); + demoplaybackstep(); + if(!demoplayback) + { + if(getclientnum()>=0) shoot(player1, worldpos); // only shoot when connected to server + gets2c(); // do this first, so we have most accurate information when our player moves + }; + mphysents(); + otherplayers(); + if(!demoplayback) + { + //monsterthink(); + + // Added by Rick: let bots think + BotManager.Think(); + + //put game mode extra call here + if(player1->state==CS_DEAD) + { + if(lastmillis-player1->lastaction<2000) + { + player1->move = player1->strafe = 0; + moveplayer(player1, 10, false); + }; + } + else if(!intermission) + { + moveplayer(player1, 20, true); + checkitems(); + }; + c2sinfo(player1); // do this last, to reduce the effective frame lag + }; + }; + lastmillis = millis; +}; + +void entinmap(dynent *d) // brute force but effective way to find a free spawn spot in the map +{ + loopi(100) // try max 100 times + { + float dx = (rnd(21)-10)/10.0f*i; // increasing distance + float dy = (rnd(21)-10)/10.0f*i; + d->o.x += dx; + d->o.y += dy; + if(collide(d, true, 0, 0)) return; + d->o.x -= dx; + d->o.y -= dy; + }; + conoutf("can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y); + // leave ent at original pos, possibly stuck +}; + +// EDIT: AH +int securespawndist = 15; + +// Returns -1 for a free place, if not it returns the vdist to the nearest enemy +int nearestenemy(vec *v, string team) +{ + float nearestPlayerDistSquared = -1; + loopv(players) + { + dynent *other = players[i]; + if(!other) continue; + if(isteam(team,other->team))continue; // its a teammate + vec place = {v->x, v->y, v->z}; + vdistsquared(distsquared, t, place, other->o); + if(nearestPlayerDistSquared == -1) nearestPlayerDistSquared = distsquared; // first run + else if(distsquared < nearestPlayerDistSquared) nearestPlayerDistSquared = distsquared; // if a player is closer + }; + if(nearestPlayerDistSquared >= securespawndist * securespawndist || nearestPlayerDistSquared == -1) return -1; // a distance more than securespawndist means the place is free + else return sqrt(nearestPlayerDistSquared); +}; + +int spawncycle = -1; +int fixspawn = 2; + +void spawnplayer(dynent *d, bool secure) // place at random spawn +{ + loopj(10) // EDIT: AH + { + int r = fixspawn-->0 ? 4 : rnd(10)+1; + loopi(r) spawncycle = findplayerstart(m_teammode ? rb_team_int(d->team) : 100, spawncycle+1); + if(spawncycle!=-1 && secure) + { + entity &e = ents[spawncycle]; + vec pos = { e.x, e.y, e.z }; + if(nearestenemy(&pos, d->team) == -1) break; + } else break; + }; + if(spawncycle!=-1) + { + d->o.x = ents[spawncycle].x; + d->o.y = ents[spawncycle].y; + d->o.z = ents[spawncycle].z; + d->yaw = ents[spawncycle].attr1; + d->pitch = 0; + d->roll = 0; + } + else + { + d->o.x = d->o.y = (float)ssize/2; + d->o.z = 4; + }; + entinmap(d); + spawnstate(d); + d->state = CS_ALIVE; +}; + +// movement input code + +#define dir(name,v,d,s,os) void name(bool isdown) { player1->s = isdown; player1->v = isdown ? d : (player1->os ? -(d) : 0); player1->lastmove = lastmillis; }; + +dir(backward, move, -1, k_down, k_up); +dir(forward, move, 1, k_up, k_down); +dir(left, strafe, 1, k_left, k_right); +dir(right, strafe, -1, k_right, k_left); + +void attack(bool on) +{ + if(intermission) return; + if(editmode) editdrag(on); + else if(player1->attacking = on) respawn(); +}; + +void jumpn(bool on) { if(!intermission && (player1->jumpnext = on) ) respawn(); }; + +COMMAND(backward, ARG_DOWN); +COMMAND(forward, ARG_DOWN); +COMMAND(left, ARG_DOWN); +COMMAND(right, ARG_DOWN); +COMMANDN(jump, jumpn, ARG_DOWN); +COMMAND(attack, ARG_DOWN); +COMMAND(showscores, ARG_DOWN); + +void fixplayer1range() +{ + const float MAXPITCH = 90.0f; + if(player1->pitch>MAXPITCH) player1->pitch = MAXPITCH; + if(player1->pitch<-MAXPITCH) player1->pitch = -MAXPITCH; + while(player1->yaw<0.0f) player1->yaw += 360.0f; + while(player1->yaw>=360.0f) player1->yaw -= 360.0f; +}; + +void mousemove(int dx, int dy) +{ + if(player1->state==CS_DEAD || intermission) return; + const float SENSF = 33.0f; // try match quake sens + player1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); + player1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); + fixplayer1range(); +}; + +// damage arriving from the network, monsters, yourself, all ends up here. + +void selfdamage(int damage, int actor, dynent *act) +{ printf("selfdmg\n"); + if(player1->state!=CS_ALIVE || editmode || intermission) return; + damageblend(damage); + demoblend(damage); + int ad = damage*30/100;//int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible + if(ad>player1->armour) ad = player1->armour; + player1->armour -= ad; + damage -= ad; + float droll = damage/0.5f; + player1->roll += player1->roll>0 ? droll : (player1->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage + if((player1->health -= damage)<=0) + { + if(actor==-2) + { + conoutf("you got killed by %s!", (int)&act->name); + } + else if(actor==-1) + { + actor = getclientnum(); + conoutf("you suicided!"); + addmsg(1, 2, SV_FRAGS, --player1->frags); + } + else + { + // Modified by Rick + //dynent *a = getclient(actor); + dynent *a; + if (act->bIsBot) a = act; + else a = getclient(actor); + // End mod + + if(a) + { + if(isteam(a->team, player1->team)) + { + conoutf("you got fragged by a teammate (%s)", (int)a->name); + } + else + { + conoutf("you got fragged by %s", (int)a->name); + }; + }; + }; + // EDIT: AH + if(m_ctf) ctf_death(); + showscores(true); + if(act->bIsBot) addmsg(1, 2, SV_DIEDBYBOT, actor); + else addmsg(1, 2, SV_DIED, actor); + player1->lifesequence++; + player1->attacking = false; + player1->state = CS_DEAD; + player1->oldpitch = player1->pitch; + player1->pitch = 0; + player1->roll = 60; + playsound(S_DIE1+rnd(2)); + spawnstate(player1); + player1->lastaction = lastmillis; + } + else + { + playsound(S_PAIN6); + }; +}; + +void timeupdate(int timeremain) +{ + if(!timeremain) + { + intermission = true; + player1->attacking = false; + conoutf("intermission:"); + conoutf("game has ended!"); + showscores(true); + } + else + { + conoutf("time remaining: %d minutes", timeremain); + }; +}; + +dynent *getclient(int cn) // ensure valid entity +{ + if(cn<0 || cn>=MAXCLIENTS) + { + neterr("clientnum"); + return NULL; + }; + while(cn>=players.length()) players.add(NULL); + return players[cn] ? players[cn] : (players[cn] = newdynent()); +}; + +// Added by Rick +dynent *getbot(int cn) // ensure valid entity +{ + if(cn<0 || cn>=MAXCLIENTS) + { + neterr("botnum"); + return NULL; + }; + + while(cn>=bots.length()) bots.add(NULL); + if (!bots[cn]) + { + bots[cn] = newdynent(); + if (bots[cn]) + { + bots[cn]->pBot = NULL; + bots[cn]->bIsBot = true; + } + } + + return bots[cn]; +}; +// End add by Rick + +void initclient() +{ + clientmap[0] = 0; + initclientnet(); +}; + +entity flagdummies[2]; // in case the map does not provide flags + +void preparectf(bool cleanonly=false) +{ + loopi(2) flaginfos[i].flag = &flagdummies[i]; + if(!cleanonly) + { + loopv(ents) + { + entity &e = ents[i]; + if(e.type==CTF_FLAG) + { + e.spawned = true; + if(e.attr2>2) { conoutf("invalid ctf-flag entity (%i)", i); e.attr2 = 0; }; + flaginfo &f = flaginfos[e.attr2]; + f.flag = &e; + f.state = CTFF_INBASE; + f.originalpos.x = (float) e.x; + f.originalpos.y = (float) e.y; + f.originalpos.z = (float) e.z; + }; + }; + newteam(player1->team); // ensure valid team + }; +}; + +void startmap(char *name) // called just after a map load +{ + //if(netmapstart()) { gamemode = 0;}; //needs fixed to switch modes? + netmapstart(); //should work + sleepwait = 0; + //monsterclear(); + // Added by Rick + BotManager.BeginMap(name); + // End add by Rick + projreset(); + if(m_ctf) preparectf(); + shotlinereset(); + spawncycle = -1; + spawnplayer(player1, true); + player1->frags = 0; + player1->flagscore = 0; + loopv(players) if(players[i]) players[i]->frags = 0; + resetspawns(); + strcpy_s(clientmap, name); + if(editmode) toggleedit(); + setvar("gamespeed", 100); + setvar("fog", 180); + setvar("fogcolour", 0x8099B3); + showscores(false); + intermission = false; + framesinmap = 0; + conoutf("game mode is %s", (int)modestr(gamemode)); +}; + +COMMANDN(map, changemap, ARG_1STR); + +void suicide() +{ + if (multiplayer()) conoutf("there are others willing to kill you..."); + else conoutf("this is not the answer"); + selfdamage(1000, -1, player1); + //if(d==player1) selfdamage(dm, -1, d); + //else { addmsg(1, 4, SV_DAMAGE, d, dm, d->lifesequence); playsound(S_FALL1+rnd(5), &d->o); }; + //particle_splash(3, 1000, 1000, player1->o); //edit out? + demodamage(1000, player1->o); + playsound(S_SUICIDE, &player1->o); +}; + +COMMAND(suicide, ARG_NONE); + +void killdummies() +{ + loopv(players) if(strcmp(players[i]->name, "dummy") == 0) { players[i]->lastaction = lastmillis; players[i]->health=1; }; +}; COMMAND(killdummies, ARG_NONE); + +// EDIT: AH +void flagaction(int flag, int action) +{ + flaginfo &f = flaginfos[flag]; + if(!f.thief) return; + bool ownflag = flag == rb_team_int(player1->team); + switch(action) + { + case SV_FLAGPICKUP: + { + playsound(S_FLAGPICKUP); + if(f.thief==player1) + { + conoutf("you got the enemy flag"); + f.pick_ack = true; + } + else conoutf("%s got %s flag", (int) f.thief->name, (int)(ownflag ? "your": "the enemy")); + break; + }; + case SV_FLAGDROP: + { + playsound(S_FLAGDROP); + if(f.thief==player1) conoutf("you lost the flag"); + else conoutf("%s lost %s flag", (int) f.thief->name, (int)(ownflag ? "your" : "the enemy")); + break; + }; + case SV_FLAGRETURN: + { + playsound(S_FLAGRETURN); + if(f.thief==player1) conoutf("you returned your flag"); + else conoutf("%s returned %s flag", (int) f.thief->name, (int)(ownflag ? "your" : "the enemy")); + break; + }; + case SV_FLAGSCORE: + { + playsound(S_FLAGSCORE); + if(f.thief==player1) + { + conoutf("you scored"); + addmsg(1, 2, SV_FLAGS, ++player1->flagscore); + } + else conoutf("%s scored for %s team", (int) f.thief->name, (int)(ownflag ? "the enemy" : "your")); + break; + }; + default: break; + }; +}; diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 225311b8..1be9a668 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -24,6 +24,29 @@ void changemap(char *name) // request map change, server ma strcpy_s(toservermap, name); }; +// Added by Rick +void botcommand(uchar *&p, char *text) +{ + int type = getint(p); + switch(EBotCommands(type)) + { + case COMMAND_ADDBOT: + getint(p); + getint(p); + sgetstr(); + sgetstr(); + break; + case COMMAND_KICKBOT: + if (getint(p)==1) // Kick a specific bot + sgetstr(); + break; + case COMMAND_BOTSKILL: + getint(p); + break; + } +} +// End add + // update the position of other clients in the game in our world // don't care if he's in the scenery or other players, // just don't overlap with our client @@ -60,6 +83,7 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th int cn = -1, type; dynent *d = NULL; bool mapchanged = false; + bool c2si = false, killedbybot=false; while(po.x = getint(p)/DMF; d->o.y = getint(p)/DMF; d->o.z = getint(p)/DMF; @@ -161,13 +186,19 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th c2sinit = false; // send new players my info again conoutf("connected: %s", (int)&text); gun_changed = true; + // Added by Rick: If we are the host("the bot owner"), tell the bots + // to update their stats + if (ishost()) + BotManager.LetBotsUpdateStats(); + // End add by Rick }; strcpy_s(d->name, text); sgetstr(); - strcpy_s(d->team, text); + strcpy_s(d->team, text); d->skin = getint(p); - d->lifesequence = getint(p); - printf("SV_INIT2C\tskin:%i\tlifes:%i\n", d->skin, d->lifesequence); + d->lifesequence = getint(p); + c2si = true; + printf("SV_INITC2S\n"); break; }; @@ -203,16 +234,36 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th if(target==clientnum) { if(ls==player1->lifesequence) selfdamage(damage, cn, d); else printf("wrong lifesequence\n"); } else playsound(S_PAIN1+rnd(5), &getclient(target)->o); break; - }; - + }; + + case SV_BOT2CLIENTDMG: + { + + int target = getint(p); + int damage = getint(p); + int ls = getint(p); + int damager = getint(p); + + if(target==clientnum) + { + dynent *a = getbot(damager); + if(ls==player1->lifesequence) + selfdamage(damage, cn, a); + } + else playsound(S_PAIN1+rnd(5), &getclient(target)->o); + break; + } + + case SV_DIEDBYBOT: + killedbybot = true; case SV_DIED: { int actor = getint(p); - if(actor==cn) + if(actor==cn && !killedbybot) { conoutf("%s suicided", (int)d->name); } - else if(actor==clientnum) + else if(actor==clientnum && !killedbybot) { int frags; if(isteam(player1->team, d->team)) @@ -229,7 +280,7 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th } else { - dynent *a = getclient(actor); + dynent *a = killedbybot ? getbot(actor) : getclient(actor); if(a) { if(isteam(a->team, d->name)) @@ -243,15 +294,19 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th }; }; playsound(S_DIE1+rnd(2), &d->o); - d->lifesequence++; printf("SV_DIED\n"); + if(!c2si) d->lifesequence++; + killedbybot = false; break; - }; + }; + + case SV_FRAGS: players[cn]->frags = getint(p); break; - case SV_ITEMPICKUP: + case SV_ITEMPICKUP: + case SV_BOTITEMPICKUP: setspawn(getint(p), false); getint(p); break; @@ -430,7 +485,186 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th for(int n = getint(p); n; n--) getint(p); break; }; -*/ +*/ + // Added by Rick: Bot specific messages + case SV_BOTSOUND: + { + int Sound = getint(p); + dynent *pBot = getbot(getint(p)); + + if (pBot) + playsound(Sound, &pBot->o); + + break; + } + + case SV_ADDBOT: // Bot joined the game + { + dynent *b = getbot(getint(p)); + if (!b) break; + + sgetstr(); + if(b->name[0]) // already connected + { + if(strcmp(b->name, text)) + conoutf("%s is now known as %s", (int)b->name, (int)&text); + } + else // new client + { + //c2sinit = false; // send new players my info again + conoutf("connected: %s", (int)&text); + }; + strcpy_s(b->name, text); + sgetstr(); + strcpy_s(b->team, text); + b->lifesequence = getint(p); + b->bIsBot = true; + b->pBot = NULL; + break; + }; + + case SV_BOTDIS: + { + int n = getint(p); + dynent *b = getbot(n); + + if (!b) + break; + + conoutf("bot %s disconnected", (int)(b->name[0] ? b->name : "[incompatible client]")); + delete b->pBot; + zapdynent(bots[n]); + bots.remove(n); + break; + } + + case SV_CLIENT2BOTDMG: + case SV_BOT2BOTDMG: + { + int target = getint(p); + int damage = getint(p); + int damager = getint(p); + dynent *b = getbot(target); + + dynent *a; + if (damager == -1) + { + // HACK! if the local client who sended the message is the damager, its -1 + a = d; + } + else if (type == SV_CLIENT2BOTDMG) + { + if (damager == clientnum) a = player1; + else a = getclient(damager); + } + else + a = getbot(damager); + + if (b && a) + { + // Do we know the bot info? if so we are the host... + if (b->pBot) b->pBot->BotPain(damage, a); + playsound(S_PAIN1+rnd(5), &b->o); + } + break; + } + + case SV_BOTDIED: + { + int b = getint(p); + int killer = getint(p); + bool KilledByABot = getint(p); + dynent *bot = getbot(b); + + if((b==killer) && KilledByABot) + { + conoutf("%s suicided", (int)bot->name); + } + else if((killer==clientnum) && !KilledByABot) + { + int frags; + if(isteam(player1->team, bot->team)) + { + frags = -1; + conoutf("you fragged a teammate (%s)", (int)bot->name); + } + else + { + frags = 1; + conoutf("you fragged %s", (int)bot->name); + }; + addmsg(1, 2, SV_FRAGS, player1->frags += frags); + } + else + { + dynent *k; + if (KilledByABot) + k = getbot(killer); + else if (killer == -1) + // if killer = -1, 'a player1' sended the message(hack) + k = d; + else + k = getclient(killer); + + if(bot && k) + { + if(isteam(bot->team, k->name)) + { + conoutf("%s fragged his teammate (%s)", (int)bot->name, (int)k->name); + } + else + { + conoutf("%s fragged %s", (int)bot->name, (int)k->name); + } + } + } + + playsound(S_DIE1+rnd(2), &bot->o); + bot->lifesequence++; + break; + }; + + case SV_BOTFRAGS: + { + dynent *b = getbot(getint(p)); + if (b) b->frags = getint(p); + break; + } + + case SV_BOTUPDATE: + { + int n = getint(p); + dynent *b = getbot(n); + if(!b) return; + b->o.x = getint(p)/DMF; + b->o.y = getint(p)/DMF; + b->o.z = getint(p)/DMF; + b->yaw = getint(p)/DAF; + b->pitch = getint(p)/DAF; + b->roll = getint(p)/DAF; + b->vel.x = getint(p)/DVF; + b->vel.y = getint(p)/DVF; + b->vel.z = getint(p)/DVF; + int f = getint(p); + b->strafe = (f&3)==3 ? -1 : f&3; + f >>= 2; + b->move = (f&3)==3 ? -1 : f&3; + b->onfloor = (f>>2)&1; + int state = f>>3; + if(state==CS_DEAD && b->state!=CS_DEAD) b->lastaction = lastmillis; + b->state = state; + + if (!b->bIsBot) + b->bIsBot = true; + + if(!demoplayback) updatepos(b); + break; + } + case SV_BOTCOMMAND: + botcommand(p, text); + break; + + // End add default: neterr("type"); diff --git a/source/src/cube.h b/source/src/cube.h index b90a1d30..b2a85820 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -91,8 +91,44 @@ struct vec { float x, y, z; }; struct block { int x, y, xs, ys; }; struct mapmodelinfo { int rad, h, zoff, snap; char *name; }; -enum { GUN_KNIFE = 0, GUN_PISTOL, GUN_SHOTGUN, GUN_SUBGUN, GUN_SNIPER, GUN_ASSULT, GUN_GRENADE, NUMGUNS }; -enum { G_PRIMARY=0, G_SECONDARY, G_MELEE, G_GRENADE, G_NUM }; +enum { GUN_KNIFE = 0, GUN_PISTOL, GUN_SHOTGUN, GUN_SUBGUN, GUN_SNIPER, GUN_ASSULT, GUN_GRENADE, NUMGUNS }; +enum { G_PRIMARY=0, G_SECONDARY, G_MELEE, G_GRENADE, G_NUM }; + + + +// Added by Rick +class CBot; + +#define MAX_STORED_LOCATIONS 7 + +extern int lastmillis; // last time (Moved up by Rick) + +class CPrevLocation +{ + int nextupdate; + +public: + vec prevloc[MAX_STORED_LOCATIONS]; + + CPrevLocation(void) : nextupdate(0) { Reset(); }; + void Reset(void) { loopi(MAX_STORED_LOCATIONS) prevloc[i].x=prevloc[i].y=prevloc[i].z=0.0f; }; + void Update(const vec &o) + { + extern float GetDistance(vec v1, vec v2); + + if (nextupdate > lastmillis) return; + if (GetDistance(o, prevloc[0]) >= 4.0f) + { + for(int i=(MAX_STORED_LOCATIONS-1);i>=1;i--) + prevloc[i] = prevloc[i-1]; + prevloc[0] = o; + } + nextupdate = lastmillis + 100; + }; +}; + +struct itemstat { int add, start, max, sound; }; +// End add struct md3state { @@ -147,7 +183,7 @@ struct dynent // players & monsters int lifesequence; // sequence id for each respawn, used in damage test int state; // one of CS_* below int frags; - int health, armour; //armourtype, quadmillis; + int health, armour; //armourtype, quadmillis; int akimbomillis; int gunselect, gunwait; int lastaction, lastattackgun, lastmove; @@ -157,6 +193,9 @@ struct dynent // players & monsters int monsterstate; // one of M_* below, M_NONE means human int mtype; // see monster.cpp dynent *enemy; // monster wants to kill this entity + // Added by Rick: targetpitch + float targetpitch; // monster wants to look in this direction + // End add float targetyaw; // monster wants to look in this direction bool blocked, moving; // used by physics to signal ai int trigger; // millis at which transition to another monsterstate takes place @@ -168,7 +207,7 @@ struct dynent // players & monsters int shots; //keeps track of shots from auto weapons bool reloading, hasarmour; int primary; //primary gun - int nextprimary; + int nextprimary; int skin, nextskin; md3state animstate[3]; @@ -177,7 +216,20 @@ struct dynent // players & monsters bool isphysent; // hack int thrownademillis; int akimbo; + // Added by Rick + CBot *pBot; // Only used if this is a bot, points to the bot class if we are the host, + // for other clients its NULL + bool bIsBot; // Is this dynent a bot? + CPrevLocation PrevLocations; // Previous stored locations of this player + // End add by Rick }; +// Moved from server.cpp by Rick +struct server_entity // server side version of "entity" type +{ + bool spawned; + int spawnsecs; +}; +// End move // EDIT: AH enum { CTFF_INBASE = 0, CTFF_STOLEN, CTFF_DROPPED }; @@ -223,6 +275,11 @@ enum SV_SENDMAP, SV_RECVMAP, SV_SERVMSG, SV_ITEMLIST, SV_WEAPCHANGE, SV_MODELSKIN, SV_FLAGPICKUP, SV_FLAGDROP, SV_FLAGRETURN, SV_FLAGSCORE, SV_FLAGINFO, SV_FLAGS, //EDIT: AH + // Added by Rick: Bot specific messages + SV_BOTSOUND, SV_BOTDIS, SV_BOTDIED, SV_CLIENT2BOTDMG, SV_BOT2BOTDMG, + SV_BOTFRAGS, SV_ADDBOT, SV_BOTUPDATE, SV_BOTCOMMAND, + // End add + SV_BOTITEMPICKUP, SV_BOT2CLIENTDMG, SV_DIEDBYBOT, SV_EXT, }; @@ -247,9 +304,9 @@ enum S_FALL1, S_FALL2, //31 S_FLAUNCH, S_FEXPLODE, //33 S_SPLASH1, S_SPLASH2, - S_GRUNT1, S_GRUNT2, S_RUMBLE, - S_FLAGDROP, S_FLAGPICKUP, S_FLAGRETURN, S_FLAGSCORE, - S_GRENADEPULL, S_GRENADETHROW, + S_GRUNT1, S_GRUNT2, S_RUMBLE, + S_FLAGDROP, S_FLAGPICKUP, S_FLAGRETURN, S_FLAGSCORE, + S_GRENADEPULL, S_GRENADETHROW, S_RAKIMBO, S_NULL }; @@ -303,7 +360,9 @@ extern bool demoplayback; #define vdistsquared(d,v,e,s) vec v = s; vsub(v,e); float d = (dotprod(v,v)); #define vreject(v,u,max) ((v).x>(u).x+(max) || (v).x<(u).x-(max) || (v).y>(u).y+(max) || (v).y<(u).y-(max)) #define vlinterp(v,f,u,g) { (v).x = (v).x*f+(u).x*g; (v).y = (v).y*f+(u).y*g; (v).z = (v).z*f+(u).z*g; } - +// Added by Rick (compares 2 vectors) +#define vis(v1,v2) ((v1.x==v2.x) && (v1.y==v2.y) && (v1.z==v2.z)) +// End add by Rick #define sgetstr() { char *t = text; int tlen=0; do { *t = getint(p); } while(*t++ && ++tlen +// Added by Rick +extern ENetHost *clienthost; +inline bool ishost(void) { return !clienthost; }; + +void splaysound(int n, vec *loc=0); +void addteamscore(dynent *d); +void renderscore(dynent *d); +extern void conoutf(const char *s, int a = 0, int b = 0, int c = 0); // Moved from protos.h +extern void particle_trail(int type, int fade, vec &from, vec &to); // Moved from protos.h +extern bool listenserv; +extern bool intermission; +#include "bot/bot.h" +// End add by Rick + #include "protos.h" // external function decls diff --git a/source/src/entities.cpp b/source/src/entities.cpp index cc5d664e..1b69f717 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -76,7 +76,7 @@ void renderentities() }; }; -struct itemstat { int add, start, max, sound; } itemstats[] = +itemstat itemstats[] = { 16, 8, 72, S_ITEMAMMO, //pistol 14, 7, 21, S_ITEMAMMO, //shotgun @@ -90,6 +90,9 @@ struct itemstat { int add, start, max, sound; } itemstats[] = }; void baseammo(int gun) { player1->ammo[gun] = itemstats[gun-1].add*2; }; +// Added by Rick: baseammo for bots +void botbaseammo(int gun, dynent *d) { d->ammo[gun] = itemstats[gun-1].add*2; }; +// End add // these two functions are called when the server acknowledges that you really // picked up the item (in multiplayer someone may grab it before you). @@ -112,42 +115,37 @@ void radditem(int i, int &v, int t) v += is.add; if(v>is.max) v = is.max; playsoundc(is.sound); -}; - -extern void weapon(int gun); +}; + +extern void weapon(int gun); void realpickup(int n, dynent *d) { switch(ents[n].type) { - //case I_PISTOL: radditem(n, d->ammo[1]); break; - //case I_SHOTGUN: radditem(n, d->ammo[2]); break; - //case I_SUBGUN: radditem(n, d->ammo[3]); break; - //case I_SNIPER: radditem(n, d->ammo[4]); break; - //case I_ASSULT: radditem(n, d->ammo[5]); break; - case I_CLIPS: - - radditem(n, d->ammo[1], 1); - break; - - case I_AMMO: - radditem(n, d->ammo[d->primary], d->primary); - break; - case I_GRENADE: radditem(n, d->mag[6], 6); break; - case I_HEALTH: radditem(n, d->health, 7); break; - - case I_ARMOUR: + case I_CLIPS: + radditem(n, d->ammo[1], 1); + break; + case I_AMMO: + radditem(n, d->ammo[d->primary], d->primary); + break; + case I_GRENADE: + radditem(n, d->mag[6], 6); + break; + case I_HEALTH: + radditem(n, d->health, 7); + break; + case I_ARMOUR: radditem(n, d->armour, 8); //d->hasarmour = true; break; - case I_AKIMBO: - d->akimbo = true; + d->akimbo = true; d->akimbomillis = 30000; d->mag[GUN_PISTOL] = 16; - radditem(n, d->ammo[1], 9); + radditem(n, d->ammo[1], 9); weapon(G_SECONDARY); - conoutf("a lesser man would use a single pistol"); + if(d==player1) conoutf("a lesser man would use a single pistol"); break; }; }; @@ -183,18 +181,13 @@ void pickup(int n, dynent *d) int ammo = np*2; switch(ents[n].type) { - //case I_PISTOL: additem(n, d->ammo[1], ammo); break; - //case I_SHOTGUN: additem(n, d->ammo[2], ammo); break; - //case I_SUBGUN: additem(n, d->ammo[3], ammo); break; - //case I_SNIPER: additem(n, d->ammo[4], ammo); break; - //case I_ASSULT: additem(n, d->ammo[5], ammo); break; case I_CLIPS: - additem(n, d->ammo[1], ammo, 1); - break; - case I_AMMO: - additem(n, d->ammo[d->primary], ammo, d->primary); - break; - case I_GRENADE: additem(n, d->mag[6], ammo, 6); break; + additem(n, d->ammo[1], ammo, 1); + break; + case I_AMMO: + additem(n, d->ammo[d->primary], ammo, d->primary); + break; + case I_GRENADE: additem(n, d->mag[6], ammo, 6); break; case I_HEALTH: additem(n, d->health, np*5, 7); break; case I_ARMOUR: @@ -287,27 +280,21 @@ void radd(dynent *d) d->mag[GUN_KNIFE] = 1; d->mag[GUN_PISTOL] = 8; - d->ammo[GUN_PISTOL] = 24; + d->ammo[GUN_PISTOL] = 24; d->mag[GUN_GRENADE] = d->ammo[GUN_GRENADE] = 0; if (m_pistol || d->primary==GUN_PISTOL) // || pistol only mode! { - d->primary = GUN_PISTOL; - d->ammo[d->primary] = 72; + d->primary = GUN_PISTOL; + d->ammo[d->primary] = 72; } else if (d->primary>GUN_PISTOL && d->primaryammo[d->primary] = itemstats[d->primary-1].max; + d->mag[d->primary] = itemstats[d->primary-1].start; + printf("player is you: %i\n", d==player1?1:0); + }; - d->mag[d->primary] = itemstats[d->primary-1].start; - } - -/* - if (d->primary==GUN_GRENADE) - { - //conoutf("you don't have to worry about blowing your hand off just yet..."); - } -*/ if (d->hasarmour) { if(gamemode==m_arena) @@ -331,6 +318,48 @@ COMMAND(item,ARG_1INT); void akimbo() { conoutf("akimbo=%d",player1->akimbo); -}; +}; COMMAND(akimbo, ARG_NONE); + +// Added by Rick +bool intersect(entity *e, vec &from, vec &to, vec *end) // if lineseg hits entity bounding box(entity version) +{ + mapmodelinfo &mmi = getmminfo(e->attr2); + if(!&mmi || !mmi.h) return false; + + float lo = (float)(S(e->x, e->y)->floor+mmi.zoff+e->attr3); + float hi = lo+mmi.h; + vec v = to, w = { e->x, e->y, lo + (fabs(hi-lo)/2.0f) }, *p; + vsub(v, from); + vsub(w, from); + float c1 = dotprod(w, v); + + if(c1<=0) p = &from; + else + { + float c2 = dotprod(v, v); + if(c2<=c1) p = &to; + else + { + float f = c1/c2; + vmul(v, f); + vadd(v, from); + p = &v; + }; + }; + + if (p->x <= e->x+mmi.rad + && p->x >= e->x-mmi.rad + && p->y <= e->y+mmi.rad + && p->y >= e->y-mmi.rad + && p->z <= hi + && p->z >= lo) + { + if (end) *end = *p; + return true; + } + return false; +}; +// End add by Ricks + diff --git a/source/src/menus.cpp b/source/src/menus.cpp index 1b651be0..58c46d72 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -1,223 +1,223 @@ -// menus.cpp: ingame menu system (also used for scores and serverlist) - -#include "cube.h" - -#define FIRSTMENU 120 - -struct mitem { char *text, *action, *hoveraction; }; - -struct gmenu -{ - char *name; - vector items; - int mwidth; - int menusel; - int tex; // (optional) bg tex - char *mdl; // (optional) md2 mdl - int frame, range, rotspeed, scale; -}; - -vector menus; - -int vmenu = -1; - -ivector menustack; - -void menuset(int menu) // EDIT: AH -{ - if((vmenu = menu)>=1 && menu != 2) resetmovement(player1); - if(vmenu==1) menus[1].menusel = 0; -}; - -void showmenu(char *name) -{ - loopv(menus) if(i>1 && strcmp(menus[i].name, name)==0) - { - menuset(i); - return; - }; -}; - -int menucompare(mitem *a, mitem *b) -{ - int x = atoi(a->text); - int y = atoi(b->text); - if(x>y) return -1; - if(x0 && vmenu!=2 ? "[ %s menu ]" : "%s", m.name); // EDIT: AH - int mdisp = m.items.length(); - int w = 0; - loopi(mdisp) - { - int x = text_width(m.items[i].text); - if(x>w) w = x; - }; - int tw = text_width(title); - if(tw>w) w = tw; - int step = FONTH/4*5; - int h = (mdisp+2)*step; - int y = (VIRTH-h)/2; - int x = (VIRTW-w)/2; - blendbox(x-FONTH/2*3, y-FONTH, x+w+FONTH/2*3, y+h+FONTH, true, m.tex >= 0 ? FIRSTMENU + m.tex : -1); - draw_text(title, x, y,2); - y += FONTH*2; - if(vmenu && vmenu!=2) - { - int bh = y+m.menusel*step; - blendbox(x-FONTH, bh-10, x+w+FONTH, bh+FONTH+10, false); - }; - loopj(mdisp) - { - draw_text(m.items[j].text, x, y, 2); - y += step; - }; - return true; -}; - -void rendermenumdl() -{ - if(vmenu<0) { menustack.setsize(0); return; }; - if(vmenu==1) refreshservers(); - gmenu &m = menus[vmenu]; - if(!m.mdl) return; - - glPushMatrix (); - - glTranslatef(player1->o.x, player1->o.z, player1->o.y); - glRotatef(player1->yaw+90+180, 0, -1, 0); - glRotatef(player1->pitch, 0, 0, 1); - - if(strstr(m.mdl, "playermodel")==NULL) glTranslatef(2.0f, 1.7f, 0.0f); - else glTranslatef(2.0f, 1.0f, 1.5f); - if(m.rotspeed) glRotatef(lastmillis/5.0f/100.0f*m.rotspeed, 0, 1, 0); - - rendermodel(m.mdl, m.frame, m.range, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0, false, (float)(m.scale ? m.scale/25.0f : 1), 100, 0, 0, false); - glPopMatrix(); -} - -void newmenu(char *name) -{ - gmenu &menu = menus.add(); - menu.name = newstring(name); - menu.menusel = 0; - menu.tex = -1; - menu.mdl = NULL; -}; - -void menumanual(int m, int n, char *text) -{ - if(!n) menus[m].items.setsize(0); - mitem &mitem = menus[m].items.add(); - mitem.text = text; - mitem.action = ""; -} - -void menuitem(char *text, char *action, char *hoveraction) -{ - gmenu &menu = menus.last(); - mitem &mi = menu.items.add(); - mi.text = newstring(text); - mi.action = action[0] ? newstring(action) : mi.text; - if(hoveraction) mi.hoveraction = newstring(hoveraction); - else mi.hoveraction = NULL; -}; - -void menubg(char *tex) -{ - gmenu &menu = menus.last(); - int curmtex = 0; - loopv(menus) curmtex = max(curmtex, menus[i].tex); - if(curmtex>=100) return; - sprintf_sd(path)("packages/%s", tex); - int xs, ys; - if(installtex(FIRSTMENU + curmtex, path, xs, ys, false, true)) - menu.tex = curmtex; -}; - -void menumdl(char *mdl, char *frame, char *range, char *rotspeed, char *scale) -{ - if(!mdl || !frame || !range) return; - gmenu &menu = menus.last(); - menu.mdl = newstring(mdl, _MAXDEFSTR); - menu.frame = max(0, atoi(frame)); - menu.range = max(1, atoi(range)); - menu.rotspeed = max(0, min(atoi(rotspeed), 100)); - menu.scale = max(0, min(atoi(scale), 100)); -}; - -void chmenumdl(char *menu, char *mdl, char *frame, char *range, char *rotspeed, char *scale) -{ - if(!menu || !mdl) return; - loopv(menus) - { - gmenu &m = menus[i]; - if(strcmp(m.name, menu) == 0) - { - if(m.mdl) strcpy_s(m.mdl, mdl); - else m.mdl = newstring(mdl, _MAXDEFSTR); - m.frame = atoi(frame); - m.range = atoi(range); - m.rotspeed = max(0, min(atoi(rotspeed), 100)); - m.scale = max(0, min(atoi(scale), 100)); - return; - }; - }; -}; - - -COMMAND(menuitem, ARG_3STR); -COMMAND(showmenu, ARG_1STR); -COMMAND(newmenu, ARG_1STR); -COMMAND(menubg, ARG_1STR); -COMMAND(menumdl, ARG_5STR); -COMMAND(chmenumdl, ARG_6STR); - -bool menukey(int code, bool isdown) -{ - if(vmenu<=0 || vmenu == 2) return false; // EDIT: AH - int menusel = menus[vmenu].menusel; - if(isdown) - { - int oldmenusel = menusel; - if(code==SDLK_ESCAPE) - { - menuset(-1); - if(!menustack.empty()) menuset(menustack.pop()); - return true; - } - else if(code==SDLK_UP || code==-4) menusel--; - else if(code==SDLK_DOWN || code==-5) menusel++; - int n = menus[vmenu].items.length(); - if(menusel<0) menusel = n-1; - else if(menusel>=n) menusel = 0; - menus[vmenu].menusel = menusel; - if(menusel != oldmenusel)execute(menus[vmenu].items[menusel].hoveraction, true); - } - else - { - if(code==SDLK_RETURN || code==-2) - { - char *action = menus[vmenu].items[menusel].action; - if(vmenu==1) connects(getservername(menusel)); - menustack.add(vmenu); - menuset(-1); - execute(action, true); - }; - }; - return true; -}; +// menus.cpp: ingame menu system (also used for scores and serverlist) + +#include "cube.h" + +#define FIRSTMENU 120 + +struct mitem { char *text, *action, *hoveraction; }; + +struct gmenu +{ + char *name; + vector items; + int mwidth; + int menusel; + int tex; // (optional) bg tex + char *mdl; // (optional) md2 mdl + int frame, range, rotspeed, scale; +}; + +vector menus; + +int vmenu = -1; + +ivector menustack; + +void menuset(int menu) // EDIT: AH +{ + if((vmenu = menu)>=1 && menu != 2) resetmovement(player1); + if(vmenu==1) menus[1].menusel = 0; +}; + +void showmenu(char *name) +{ + loopv(menus) if(i>1 && strcmp(menus[i].name, name)==0) + { + menuset(i); + return; + }; +}; + +int menucompare(mitem *a, mitem *b) +{ + int x = atoi(a->text); + int y = atoi(b->text); + if(x>y) return -1; + if(x0 && vmenu!=2 ? "[ %s menu ]" : "%s", m.name); // EDIT: AH + int mdisp = m.items.length(); + int w = 0; + loopi(mdisp) + { + int x = text_width(m.items[i].text); + if(x>w) w = x; + }; + int tw = text_width(title); + if(tw>w) w = tw; + int step = FONTH/4*5; + int h = (mdisp+2)*step; + int y = (VIRTH-h)/2; + int x = (VIRTW-w)/2; + blendbox(x-FONTH/2*3, y-FONTH, x+w+FONTH/2*3, y+h+FONTH, true, m.tex >= 0 ? FIRSTMENU + m.tex : -1); + draw_text(title, x, y,2); + y += FONTH*2; + if(vmenu && vmenu!=2) + { + int bh = y+m.menusel*step; + blendbox(x-FONTH, bh-10, x+w+FONTH, bh+FONTH+10, false); + }; + loopj(mdisp) + { + draw_text(m.items[j].text, x, y, 2); + y += step; + }; + return true; +}; + +void rendermenumdl() +{ + if(vmenu<0) { menustack.setsize(0); return; }; + if(vmenu==1) refreshservers(); + gmenu &m = menus[vmenu]; + if(!m.mdl) return; + + glPushMatrix (); + + glTranslatef(player1->o.x, player1->o.z, player1->o.y); + glRotatef(player1->yaw+90+180, 0, -1, 0); + glRotatef(player1->pitch, 0, 0, 1); + + if(strstr(m.mdl, "playermodel")==NULL) glTranslatef(2.0f, 1.7f, 0.0f); + else glTranslatef(2.0f, 1.0f, 1.5f); + if(m.rotspeed) glRotatef(lastmillis/5.0f/100.0f*m.rotspeed, 0, 1, 0); + + rendermodel(m.mdl, m.frame, m.range, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0, false, (float)(m.scale ? m.scale/25.0f : 1), 100, 0, 0, false); + glPopMatrix(); +} + +void newmenu(char *name) +{ + gmenu &menu = menus.add(); + menu.name = newstring(name); + menu.menusel = 0; + menu.tex = -1; + menu.mdl = NULL; +}; + +void menumanual(int m, int n, char *text) +{ + if(!n) menus[m].items.setsize(0); + mitem &mitem = menus[m].items.add(); + mitem.text = text; + mitem.action = ""; +} + +void menuitem(char *text, char *action, char *hoveraction) +{ + gmenu &menu = menus.last(); + mitem &mi = menu.items.add(); + mi.text = newstring(text); + mi.action = action[0] ? newstring(action) : mi.text; + if(hoveraction) mi.hoveraction = newstring(hoveraction); + else mi.hoveraction = NULL; +}; + +void menubg(char *tex) +{ + gmenu &menu = menus.last(); + int curmtex = 0; + loopv(menus) curmtex = max(curmtex, menus[i].tex); + if(curmtex>=100) return; + sprintf_sd(path)("packages/%s", tex); + int xs, ys; + if(installtex(FIRSTMENU + curmtex, path, xs, ys, false, true)) + menu.tex = curmtex; +}; + +void menumdl(char *mdl, char *frame, char *range, char *rotspeed, char *scale) +{ + if(!mdl || !frame || !range) return; + gmenu &menu = menus.last(); + menu.mdl = newstring(mdl, _MAXDEFSTR); + menu.frame = max(0, atoi(frame)); + menu.range = max(1, atoi(range)); + menu.rotspeed = max(0, min(atoi(rotspeed), 100)); + menu.scale = max(0, min(atoi(scale), 100)); +}; + +void chmenumdl(char *menu, char *mdl, char *frame, char *range, char *rotspeed, char *scale) +{ + if(!menu || !mdl) return; + loopv(menus) + { + gmenu &m = menus[i]; + if(strcmp(m.name, menu) == 0) + { + if(m.mdl) strcpy_s(m.mdl, mdl); + else m.mdl = newstring(mdl, _MAXDEFSTR); + m.frame = atoi(frame); + m.range = atoi(range); + m.rotspeed = max(0, min(atoi(rotspeed), 100)); + m.scale = max(0, min(atoi(scale), 100)); + return; + }; + }; +}; + + +COMMAND(menuitem, ARG_3STR); +COMMAND(showmenu, ARG_1STR); +COMMAND(newmenu, ARG_1STR); +COMMAND(menubg, ARG_1STR); +COMMAND(menumdl, ARG_5STR); +COMMAND(chmenumdl, ARG_6STR); + +bool menukey(int code, bool isdown) +{ + if(vmenu<=0 || vmenu == 2) return false; // EDIT: AH + int menusel = menus[vmenu].menusel; + if(isdown) + { + int oldmenusel = menusel; + if(code==SDLK_ESCAPE) + { + menuset(-1); + if(!menustack.empty()) menuset(menustack.pop()); + return true; + } + else if(code==SDLK_UP || code==-4) menusel--; + else if(code==SDLK_DOWN || code==-5) menusel++; + int n = menus[vmenu].items.length(); + if(menusel<0) menusel = n-1; + else if(menusel>=n) menusel = 0; + menus[vmenu].menusel = menusel; + if(menusel != oldmenusel)execute(menus[vmenu].items[menusel].hoveraction, true); + } + else + { + if(code==SDLK_RETURN || code==-2) + { + char *action = menus[vmenu].items[menusel].action; + if(vmenu==1) connects(getservername(menusel)); + menustack.add(vmenu); + menuset(-1); + execute(action, true); + }; + }; + return true; +}; diff --git a/source/src/physics.cpp b/source/src/physics.cpp index f4088252..c617ed55 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -5,6 +5,7 @@ #include "cube.h" +/* Modified by Rick: Fix, so we can jump on players aswell bool plcollide(dynent *d, dynent *o, float &headspace) // collide with player or monster { if(o->state!=CS_ALIVE) return true; @@ -18,6 +19,24 @@ bool plcollide(dynent *d, dynent *o, float &headspace) // collide with }; return true; }; +*/ + +bool plcollide(dynent *d, dynent *o, float &headspace, float &hi, float &lo) // collide with player or monster +{ + if(o->state!=CS_ALIVE) return true; + const float r = o->radius+d->radius; + if(fabs(o->o.x-d->o.x)o.y-d->o.y)o.z-d->eyeheighto.z-o->eyeheight) { if(o->o.z-o->eyeheighto.z-o->eyeheight-1; } + else if(o->o.z+o->aboveeye>lo) lo = o->o.z+o->aboveeye+1; + + if(fabs(o->o.z-d->o.z)aboveeye+d->eyeheight) return false; + //if(d->monsterstate) return false; // hack + headspace = d->o.z-o->o.z-o->aboveeye-d->eyeheight; + if(headspace<0) headspace = 10; + }; + return true; +}; bool cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by, int &bs) // recursively collide with a mipmapped corner cube { @@ -109,14 +128,28 @@ bool collide(dynent *d, bool spawn, float drop, float rise) if(hi-lo < d->eyeheight+d->aboveeye) return false; + // Modified by Rick: plcollide now takes hi and lo in account aswell, that way we can jump/walk on players + float headspace = 10; loopv(players) // collide with other players { dynent *o = players[i]; if(!o || o==d) continue; - if(!plcollide(d, o, headspace)) return false; + if(!plcollide(d, o, headspace, hi, lo)) return false; }; - if(d!=player1 && d->mtype!=M_NADE) if(!plcollide(d, player1, headspace)) return false; + loopv(bots) // Added by Rick: collide with other bots + { + dynent *o = bots[i]; + if(!o || o==d) continue; + if(!plcollide(d, o, headspace, hi, lo)) return false; + }; + + if(d!=player1 && d->mtype!=M_NADE) if(!plcollide(d, player1, headspace, hi, lo)) return false; + //dvector &v = getmonsters(); + // this loop can be a performance bottleneck with many monster on a slow cpu, + // should replace with a blockmap but seems mostly fast enough + // Modified by Rick: Added v[i] pointer check + //loopv(v) if(v[i] && !vreject(d->o, v[i]->o, 7.0f) && d!=v[i] && !plcollide(d, v[i], headspace, hi, lo)) return false; headspace -= 0.01f; mmcollide(d, hi, lo); // collide with map models @@ -274,11 +307,13 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) if(water) { pl->vel.x /= 8; pl->vel.y /= 8; }; // dampen velocity change even harder, gives correct water feel if(local) playsoundc(S_JUMP); else if(pl->monsterstate) playsound(S_JUMP, &pl->o); + else if(pl->bIsBot) botplaysound(S_JUMP, pl); // Added by Rick } /*else if(pl->timeinair>800) // if we land after long time must have been a high jump, make thud sound { if(local) playsoundc(S_LAND); else if(pl->monsterstate) playsound(S_LAND, &pl->o); + else if(pl->bIsBot) botplaysound(S_LAND, pl); // Added by Rick };*/ pl->timeinair = 0; if(pl->isphysent) pl->vel.z *= 0.7f; @@ -369,9 +404,12 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) if(!pl->inwater && water) { playsound(S_SPLASH2, &pl->o); pl->vel.z = 0; } else if(pl->inwater && !water) playsound(S_SPLASH1, &pl->o); pl->inwater = water; + // Added by Rick: Easy hack to store previous locations of all players/monsters/bots + pl->PrevLocations.Update(pl->o); + // End add }; void moveplayer(dynent *pl, int moveres, bool local) { loopi(physicsrepeat) moveplayer(pl, moveres, local, i ? curtime/physicsrepeat : curtime-curtime/physicsrepeat*(physicsrepeat-1)); -}; +}; diff --git a/source/src/protos.h b/source/src/protos.h index fb4353a5..03f3287c 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -18,7 +18,7 @@ extern void writecfg(); // console extern void keypress(int code, bool isdown, int cooked); extern void renderconsole(); -extern void conoutf(const char *s, int a = 0, int b = 0, int c = 0); +// extern void conoutf(const char *s, int a = 0, int b = 0, int c = 0); Moved to cube.h by Rick extern char *getcurcommand(); // menus @@ -89,6 +89,7 @@ extern char *getclientmap(); extern const char *modestr(int n); extern void zapdynent(dynent *&d); extern dynent *getclient(int cn); +extern dynent *getbot(int cn); // Added by Rick extern void timeupdate(int timeremain); extern void resetmovement(dynent *d); extern void fixplayer1range(); @@ -169,13 +170,14 @@ extern void damageblend(int n); extern void addshotline(dynent *d, vec &from, vec &to); extern void rendershotlines(); extern void shotlinereset(); -extern void renderphysents(); +extern void renderphysents(); extern bool scoped; // renderparticles extern void setorient(vec &r, vec &u); extern void particle_splash(int type, int num, int fade, vec &p); -extern void particle_trail(int type, int fade, vec &from, vec &to); +//extern void particle_splash(int num, int fade, vec &p); +// extern void particle_trail(int type, int fade, vec &from, vec &to); Moved to cube.h by Rick extern void render_particles(int time); // worldio @@ -204,6 +206,7 @@ extern void playsound(int n, vec *loc = 0); extern void playsoundc(int n); extern void initsound(); extern void cleansound(); +extern void botplaysound(int n, dynent *b); // Added by Rick // rendermd2 extern void rendermodel(char *mdl, int frame, int range, int tex, float rad, float x, float y, float z, float yaw, float pitch, bool teammate, float scale, float speed, int snap = 0, int basetime = 0, bool oculling=true); @@ -240,14 +243,19 @@ extern void createrays(vec &from, vec &to); extern void moveprojectiles(float time); extern void projreset(); extern dynent *playerincrosshair(); -extern int reloadtime(int gun); +extern int reloadtime(int gun); +extern void reload(dynent *d); extern int attackdelay(int gun); extern int magsize(int gun); extern int kick_rot(int gun); extern int kick_back(int gun); -extern bool gun_changed; -extern bool akimboside; +extern bool gun_changed; +extern bool akimboside; extern int akimbolastaction[2]; +// Added by Rick +extern char *botincrosshair(); +extern bool intersect(dynent *d, vec &from, vec &to, vec *end = NULL); +// End add by Rick // entities extern void renderents(); @@ -260,6 +268,10 @@ extern void setspawn(int i, bool on); extern void baseammo(int gun); extern void checkitems(); extern void radd(dynent *d); +// Added by Rick +extern void botbaseammo(int gun, dynent *d); +extern bool intersect(entity *e, vec &from, vec &to, vec *end=NULL); +// End add by Rick // rndmap extern void perlinarea(block &b, int scale, int seed, int psize); diff --git a/source/src/renderextras.cpp b/source/src/renderextras.cpp index 1c89c5a8..1e1dda31 100644 --- a/source/src/renderextras.cpp +++ b/source/src/renderextras.cpp @@ -1,566 +1,567 @@ -// renderextras.cpp: misc gl render code and the HUD - -#include "cube.h" - -void line(int x1, int y1, float z1, int x2, int y2, float z2) -{ - glBegin(GL_POLYGON); - glVertex3f((float)x1, z1, (float)y1); - glVertex3f((float)x1, z1, y1+0.01f); - glVertex3f((float)x2, z2, y2+0.01f); - glVertex3f((float)x2, z2, (float)y2); - glEnd(); - xtraverts += 4; -}; - -void linestyle(float width, int r, int g, int b) -{ - glLineWidth(width); - glColor3ub(r,g,b); -}; - -void box(block &b, float z1, float z2, float z3, float z4) -{ - glBegin(GL_POLYGON); - glVertex3f((float)b.x, z1, (float)b.y); - glVertex3f((float)b.x+b.xs, z2, (float)b.y); - glVertex3f((float)b.x+b.xs, z3, (float)b.y+b.ys); - glVertex3f((float)b.x, z4, (float)b.y+b.ys); - glEnd(); - xtraverts += 4; -}; - -void dot(int x, int y, float z) -{ - const float DOF = 0.1f; - glBegin(GL_POLYGON); - glVertex3f(x-DOF, (float)z, y-DOF); - glVertex3f(x+DOF, (float)z, y-DOF); - glVertex3f(x+DOF, (float)z, y+DOF); - glVertex3f(x-DOF, (float)z, y+DOF); - glEnd(); - xtraverts += 4; -}; - -void blendbox(int x1, int y1, int x2, int y2, bool border, int tex) -{ - glDepthMask(GL_FALSE); - glDisable(GL_TEXTURE_2D); - if(tex>=0) - { - glBindTexture(GL_TEXTURE_2D, tex); - glEnable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - } - else - { - if(border) glColor3d(0.7, 0.7, 0.7); //glColor3d(0.5, 0.3, 0.4); - else glColor3d(1.0, 1.0, 1.0); - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - }; - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2i(x1, y1); - glTexCoord2f(1, 0); glVertex2i(x2, y1); - glTexCoord2f(1, 1); glVertex2i(x2, y2); - glTexCoord2f(0, 1); glVertex2i(x1, y2); - glEnd(); - glDisable(GL_BLEND); - if(tex>=0) glDisable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glBegin(GL_POLYGON); - glColor3d(0.6, 0.6, 0.6); //glColor3d(0.2, 0.7, 0.4); - glVertex2i(x1, y1); - glVertex2i(x2, y1); - glVertex2i(x2, y2); - glVertex2i(x1, y2); - glEnd(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - xtraverts += 8; - glEnable(GL_BLEND); - glEnable(GL_TEXTURE_2D); - glDepthMask(GL_TRUE); -}; - -const int MAXSPHERES = 50; -struct sphere { vec o; float size, max; int type; sphere *next; }; -sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL; -bool sinit = false; - -void newsphere(vec &o, float max, int type) -{ - if(!sinit) - { - loopi(MAXSPHERES) - { - spheres[i].next = sempty; - sempty = &spheres[i]; - }; - sinit = true; - }; - if(sempty) - { - sphere *p = sempty; - sempty = p->next; - p->o = o; - p->max = max; - p->size = 1; - p->type = type; - p->next = slist; - slist = p; - }; -}; - -void renderspheres(int time) -{ - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glBindTexture(GL_TEXTURE_2D, 4); - - for(sphere *p, **pp = &slist; p = *pp;) - { - glPushMatrix(); - float size = p->size/p->max; - glColor4f(1.0f, 1.0f, 1.0f, 1.0f-size); - glTranslatef(p->o.x, p->o.z, p->o.y); - glRotatef(lastmillis/5.0f, 1, 1, 1); - glScalef(p->size, p->size, p->size); - glCallList(1); - glScalef(0.8f, 0.8f, 0.8f); - glCallList(1); - glPopMatrix(); - xtraverts += 12*6*2; - - if(p->size>p->max) - { - *pp = p->next; - p->next = sempty; - sempty = p; - } - else - { - p->size += time/100.0f; - pp = &p->next; - }; - }; - - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); -}; - -string closeent; -char *entnames[] = -{ - "none?", "light", "playerstart", - "clips", "ammobox","grenades", - "health", "armour", "akimbo", - "mapmodel", "trigger", - "ladder", "ctf-flag", "?", "?", "?", -}; - -void renderents() // show sparkly thingies for map entities in edit mode -{ - closeent[0] = 0; - if(!editmode) return; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==NOTUSED) continue; - vec v = { e.x, e.y, e.z }; - particle_splash(2, 2, 40, v); - }; - int e = closestent(); - if(e>=0) - { - entity &c = ents[e]; - sprintf_s(closeent)("closest entity = %s (%d, %d, %d, %d), selection = (%d, %d)", entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, getvar("selxs"), getvar("selys")); - }; -}; - -void loadsky(char *basename) -{ - static string lastsky = ""; - if(strcmp(lastsky, basename)==0) return; - char *side[] = { "ft", "bk", "lf", "rt", "dn", "up" }; - int texnum = 14; - loopi(6) - { - sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]); - int xs, ys; - if(!installtex(texnum+i, path(name), xs, ys, true)) conoutf("could not load sky textures"); - }; - strcpy_s(lastsky, basename); -}; - -COMMAND(loadsky, ARG_1STR); - -float cursordepth = 0.9f; -GLint viewport[4]; -GLdouble mm[16], pm[16]; -vec worldpos; - -void readmatrices() -{ - glGetIntegerv(GL_VIEWPORT, viewport); - glGetDoublev(GL_MODELVIEW_MATRIX, mm); - glGetDoublev(GL_PROJECTION_MATRIX, pm); -}; - -// stupid function to cater for stupid ATI linux drivers that return incorrect depth values - -float depthcorrect(float d) -{ - return (d<=1/256.0f) ? d*256 : d; -}; - -// find out the 3d target of the crosshair in the world easily and very acurately. -// sadly many very old cards and drivers appear to fuck up on glReadPixels() and give false -// coordinates, making shooting and such impossible. -// also hits map entities which is unwanted. -// could be replaced by a more acurate version of monster.cpp los() if needed - -void readdepth(int w, int h) -{ - glReadPixels(w/2, h/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth); - double worldx = 0, worldy = 0, worldz = 0; - gluUnProject(w/2, h/2, depthcorrect(cursordepth), mm, pm, viewport, &worldx, &worldz, &worldy); - worldpos.x = (float)worldx; - worldpos.y = (float)worldy; - worldpos.z = (float)worldz; - vec r = { (float)mm[0], (float)mm[4], (float)mm[8] }; - vec u = { (float)mm[1], (float)mm[5], (float)mm[9] }; - setorient(r, u); -}; - -void drawicon(float tx, float ty, int x, int y, int tex = 5, float scaling = 1/3.0f) // EDIT: AH -{ - glBindTexture(GL_TEXTURE_2D, tex); - glBegin(GL_QUADS); - tx /= 192; - ty /= 192; - float o = scaling; - int s = 120; - glTexCoord2f(tx, ty); glVertex2i(x, y); - glTexCoord2f(tx+o, ty); glVertex2i(x+s, y); - glTexCoord2f(tx+o, ty+o); glVertex2i(x+s, y+s); - glTexCoord2f(tx, ty+o); glVertex2i(x, y+s); - glEnd(); - xtraverts += 4; -}; - -void invertperspective() -{ - // This only generates a valid inverse matrix for matrices generated by gluPerspective() - GLdouble inv[16]; - memset(inv, 0, sizeof(inv)); - - inv[0*4+0] = 1.0/pm[0*4+0]; - inv[1*4+1] = 1.0/pm[1*4+1]; - inv[2*4+3] = 1.0/pm[3*4+2]; - inv[3*4+2] = -1.0; - inv[3*4+3] = pm[2*4+2]/pm[3*4+2]; - - glLoadMatrixd(inv); -}; - -VAR(crosshairsize, 0, 15, 50); - -int dblend = 0; -void damageblend(int n) { dblend += n; }; - -VAR(hidestats, 0, 0, 1); -VAR(crosshairfx, 0, 1, 1); - -bool scoped = false; -int oldfov = 100; - -void togglescope() -{ - if(player1->gunselect!=GUN_SNIPER) return; - if(!scoped) oldfov = getvar("fov"); - scoped = !scoped; - setvar("fov", scoped ? 50 : oldfov); -} -COMMAND(togglescope, ARG_NONE); - -void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater) -{ - - readmatrices(); - if(editmode) - { - if(cursordepth==1.0f) worldpos = player1->o; - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - cursorupdate(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - }; - - glDisable(GL_DEPTH_TEST); - invertperspective(); - glPushMatrix(); - glOrtho(0, VIRTW, VIRTH, 0, -1, 1); - glEnable(GL_BLEND); - - if(dblend || underwater) - { - glDepthMask(GL_FALSE); - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - glBegin(GL_QUADS); - if(dblend) glColor3d(0.0f, 0.9f, 0.9f); - else glColor3d(0.9f, 0.5f, 0.0f); - glVertex2i(0, 0); - glVertex2i(VIRTW, 0); - glVertex2i(VIRTW, VIRTH); - glVertex2i(0, VIRTH); - glEnd(); - glDepthMask(GL_TRUE); - dblend -= curtime/3; - if(dblend<0) dblend = 0; - }; - - glEnable(GL_TEXTURE_2D); - - char *command = getcurcommand(); - dynent *player = playerincrosshair(); - if(command) draw_textf("> %s_", 20, 1570, 2, (int)command); - else if(closeent[0]) draw_text(closeent, 20, 1570, 2); - else if(player) - { - string plinfo; - if(isteam(player->team, player1->team)) sprintf_s(plinfo)("%s (health:%i armour:%i)", player->name, player->health, player->armour); - else sprintf_s(plinfo)("%s", player->name); - draw_text(plinfo, 20, 1570, 2); - }; - - renderscores(); - if(!rendermenu()) - { - if(scoped && player1->gunselect == GUN_SNIPER) - { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.9f); - glBindTexture(GL_TEXTURE_2D, 10); - glBegin(GL_QUADS); - glColor3ub(255,255,255); - float chsize = (float)crosshairsize; - glTexCoord2d(0.0, 0.0); glVertex2f(0, 0); - glTexCoord2d(1.0, 0.0); glVertex2f(VIRTW, 0); - glTexCoord2d(1.0, 1.0); glVertex2f(VIRTW, VIRTH); - glTexCoord2d(0.0, 1.0); glVertex2f(0, VIRTH); - glEnd(); - glDisable(GL_ALPHA_TEST); - } - else if (player1->gunselect != GUN_SNIPER) - { - glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, 1); - glBegin(GL_QUADS); - glColor3ub(255,255,255); - if(crosshairfx) - { - if(player1->gunwait) glColor3ub(128,128,128); - else if(player1->health<=25) glColor3ub(255,0,0); - else if(player1->health<=50) glColor3ub(255,128,0); - }; - float chsize = (float)crosshairsize; - glTexCoord2d(0.0, 0.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 - chsize); - glTexCoord2d(1.0, 0.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 - chsize); - glTexCoord2d(1.0, 1.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 + chsize); - glTexCoord2d(0.0, 1.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 + chsize); - glEnd(); - } - }; - - - - glPopMatrix(); - - glPushMatrix(); - glOrtho(0, VIRTW*4/3, VIRTH*4/3, 0, -1, 1); - renderconsole(); - - if(!hidestats) - { - glPopMatrix(); - glPushMatrix(); - glOrtho(0, VIRTW*3/2, VIRTH*3/2, 0, -1, 1); - draw_textf("fps %d", 3200, 2320, 2, curfps); - draw_textf("lod %d", 3200, 2390, 2, lod_factor()); - draw_textf("wqd %d", 3200, 2460, 2, nquads); - draw_textf("wvt %d", 3200, 2530, 2, curvert); - draw_textf("evt %d", 3200, 2600, 2, xtraverts); - }; - - glPopMatrix(); - - if(player1->state==CS_ALIVE) - { - glPushMatrix(); - glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); - draw_textf("%d", 90, 827, 2, player1->health); - if(player1->armour) draw_textf("%d", 390, 827, 2, player1->armour); - //draw_textf("%d", 690, 827, 2, player1->mag[player1->gunselect]); - if (player1->gunselect!=GUN_KNIFE) - { - //draw_textf("%d", 690, 827, 2, player1->mag[player1->gunselect]); - char gunstats [64]; - if (player1->gunselect!=GUN_GRENADE) - sprintf(gunstats,"%i/%i",player1->mag[player1->gunselect],player1->ammo[player1->gunselect]); - else sprintf(gunstats,"%i",player1->mag[player1->gunselect]); - draw_text(gunstats, 690, 827, 2); - //clean up pointer? - //delete gunstats; - } - - glPopMatrix(); - - // EDIT: AH - ctf - #define ctf_scl 0.75 - if(m_ctf) - { - glPushMatrix(); - glOrtho(0, VIRTW, VIRTH, 0, -1, 1); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glColor4f(1.0f, 1.0f, 1.0f, 0.2f); - if(flaginfos[TEAM_CLA].state == CTFF_INBASE) glDisable(GL_BLEND); else glEnable(GL_BLEND); - drawicon(64*ctf_scl, 0, 1940, 1650, 11, 1.0f/4.0f); - if(flaginfos[TEAM_RVSF].state == CTFF_INBASE) glDisable(GL_BLEND); else glEnable(GL_BLEND); - drawicon(0, 0, 1820, 1650, 11, 1.0f/4.0f); - glDisable(GL_BLEND); - drawicon(rb_team_int(player1->team)*64*ctf_scl, 64*ctf_scl, VIRTW-128-10, 10, 11, 1.0f/4.0f); // shows which team you are - glPopMatrix(); - - // big flag icon - flaginfo &f = flaginfos[rb_opposite(rb_team_int(player1->team))]; - if(f.state==CTFF_STOLEN && f.thief == player1 && f.pick_ack ) - { - glPushMatrix(); - glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glColor4f(1.0f, 1.0f, 1.0f, (float)(sin(lastmillis / 100.0) + 1) / 2); - glEnable(GL_BLEND); - drawicon(rb_opposite(rb_team_int(player1->team))*128*ctf_scl, 128*ctf_scl, 1065, VIRTH/2/3*2, 11, 1.0f/2.0f); - glPopMatrix(); - }; - }; - - glPushMatrix(); - glOrtho(0, VIRTW, VIRTH, 0, -1, 1); - glDisable(GL_BLEND); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(1.0f, 1.0f, 1.0f, (float) 0.2f+(sin(lastmillis/100)+1)/2); - - if(player1->health <= 20) - { - glEnable(GL_BLEND); - drawicon(64, 0, 20, 1650); //drawicon(128, 128, 20, 1650); - glDisable(GL_BLEND); - } - else drawicon(64, 0, 20, 1650); //drawicon(128, 128, 20, 1650); - if(player1->armour) drawicon((float)(128), 0, 620, 1650); //if(player1->armour) drawicon((float)(player1->armourtype*64), 0, 620, 1650); - int g = player1->gunselect; - int r = 64; - if(g>2) { g -= 3; r = 128; }; - - if(!player1->mag[player1->gunselect] && player1->gunselect != GUN_KNIFE && player1->gunselect != GUN_GRENADE) - { - glEnable(GL_BLEND); - drawicon((float)(g*64), (float)r, 1220, 1650); - glDisable(GL_BLEND); - } - else - { - if (player1->gunselect==GUN_GRENADE) drawicon(0, 0, 1220, 1650); - else drawicon((float)(g*64), (float)r, 1220, 1650); - } - glPopMatrix(); - }; - - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); - glEnable(GL_DEPTH_TEST); -}; - -#define MAXSHOTLINES 100 -struct shotline -{ - vec from, to; - bool inuse; - int millis; -}; -shotline shotlines[MAXSHOTLINES]; - -void shotlinereset() { loopi(MAXSHOTLINES) shotlines[i].inuse = false; }; - -void addshotline(dynent *d, vec &from, vec &to) -{ - if(d == player1) return; - if(rnd(3) != 0) return; - loopi(MAXSHOTLINES) - { - shotline *s = &shotlines[i]; - if(s->inuse) continue; - int start = 10; - vdist(_dist, _unitv, player1->o, to); - if(_dist <= 10.0f) start = 8; - else start = 5; - - vdist(dist, unitv, from, to); - vdiv(unitv, dist); - s->inuse = true; - s->from = unitv; - vmul(s->from, dist/10*start); - vadd(s->from, from); - s->to = unitv; - vmul(s->to, dist/10*-(10-start-2)); - vadd(s->to, to); - s->millis = lastmillis; - return; - }; -}; - -VAR(shotline_duration, 0, 75, 10000); - -void rendershotlines() -{ - loopi(MAXSHOTLINES) - { - shotline *s = &shotlines[i]; - if(!s->inuse) continue; - if(lastmillis-s->millis > shotline_duration) { s->inuse = false; continue; } - glPushMatrix(); - glDisable(GL_TEXTURE_2D); - - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); - glDisable(GL_FOG); - - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glBegin(GL_LINES); - glColor4f(1.0f, 1.0f, 0.7f, 0.5f); - glVertex3f(s->from.x, s->from.z, s->from.y); - glVertex3f(s->to.x, s->to.z, s->to.y); - glEnd(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - glEnable(GL_FOG); - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); - - glEnable(GL_TEXTURE_2D); - glPopMatrix(); - }; -}; - -void renderphysents() -{ - loopv(physents) - { - physent *d = physents[i]; - if(d && d->state==NADE_THROWED) rendermodel("weapons/grenade/static", 0, 1, 0, 0, d->o.x, d->o.z, d->o.y, d->yaw, d->pitch, false, 2.0f, 100.0f, 0, 0); - }; -}; - +// renderextras.cpp: misc gl render code and the HUD + +#include "cube.h" + +void line(int x1, int y1, float z1, int x2, int y2, float z2) +{ + glBegin(GL_POLYGON); + glVertex3f((float)x1, z1, (float)y1); + glVertex3f((float)x1, z1, y1+0.01f); + glVertex3f((float)x2, z2, y2+0.01f); + glVertex3f((float)x2, z2, (float)y2); + glEnd(); + xtraverts += 4; +}; + +void linestyle(float width, int r, int g, int b) +{ + glLineWidth(width); + glColor3ub(r,g,b); +}; + +void box(block &b, float z1, float z2, float z3, float z4) +{ + glBegin(GL_POLYGON); + glVertex3f((float)b.x, z1, (float)b.y); + glVertex3f((float)b.x+b.xs, z2, (float)b.y); + glVertex3f((float)b.x+b.xs, z3, (float)b.y+b.ys); + glVertex3f((float)b.x, z4, (float)b.y+b.ys); + glEnd(); + xtraverts += 4; +}; + +void dot(int x, int y, float z) +{ + const float DOF = 0.1f; + glBegin(GL_POLYGON); + glVertex3f(x-DOF, (float)z, y-DOF); + glVertex3f(x+DOF, (float)z, y-DOF); + glVertex3f(x+DOF, (float)z, y+DOF); + glVertex3f(x-DOF, (float)z, y+DOF); + glEnd(); + xtraverts += 4; +}; + +void blendbox(int x1, int y1, int x2, int y2, bool border, int tex) +{ + glDepthMask(GL_FALSE); + glDisable(GL_TEXTURE_2D); + if(tex>=0) + { + glBindTexture(GL_TEXTURE_2D, tex); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + else + { + if(border) glColor3d(0.7, 0.7, 0.7); //glColor3d(0.5, 0.3, 0.4); + else glColor3d(1.0, 1.0, 1.0); + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + }; + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2i(x1, y1); + glTexCoord2f(1, 0); glVertex2i(x2, y1); + glTexCoord2f(1, 1); glVertex2i(x2, y2); + glTexCoord2f(0, 1); glVertex2i(x1, y2); + glEnd(); + glDisable(GL_BLEND); + if(tex>=0) glDisable(GL_TEXTURE_2D); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glBegin(GL_POLYGON); + glColor3d(0.6, 0.6, 0.6); //glColor3d(0.2, 0.7, 0.4); + glVertex2i(x1, y1); + glVertex2i(x2, y1); + glVertex2i(x2, y2); + glVertex2i(x1, y2); + glEnd(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + xtraverts += 8; + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glDepthMask(GL_TRUE); +}; + +const int MAXSPHERES = 50; +struct sphere { vec o; float size, max; int type; sphere *next; }; +sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL; +bool sinit = false; + +void newsphere(vec &o, float max, int type) +{ + if(!sinit) + { + loopi(MAXSPHERES) + { + spheres[i].next = sempty; + sempty = &spheres[i]; + }; + sinit = true; + }; + if(sempty) + { + sphere *p = sempty; + sempty = p->next; + p->o = o; + p->max = max; + p->size = 1; + p->type = type; + p->next = slist; + slist = p; + }; +}; + +void renderspheres(int time) +{ + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glBindTexture(GL_TEXTURE_2D, 4); + + for(sphere *p, **pp = &slist; p = *pp;) + { + glPushMatrix(); + float size = p->size/p->max; + glColor4f(1.0f, 1.0f, 1.0f, 1.0f-size); + glTranslatef(p->o.x, p->o.z, p->o.y); + glRotatef(lastmillis/5.0f, 1, 1, 1); + glScalef(p->size, p->size, p->size); + glCallList(1); + glScalef(0.8f, 0.8f, 0.8f); + glCallList(1); + glPopMatrix(); + xtraverts += 12*6*2; + + if(p->size>p->max) + { + *pp = p->next; + p->next = sempty; + sempty = p; + } + else + { + p->size += time/100.0f; + pp = &p->next; + }; + }; + + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); +}; + +string closeent; +char *entnames[] = +{ + "none?", "light", "playerstart", + "clips", "ammobox","grenades", + "health", "armour", "akimbo", + "mapmodel", "trigger", + "ladder", "ctf-flag", "?", "?", "?", +}; + +void renderents() // show sparkly thingies for map entities in edit mode +{ + closeent[0] = 0; + if(!editmode) return; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==NOTUSED) continue; + vec v = { e.x, e.y, e.z }; + particle_splash(2, 2, 40, v); + }; + int e = closestent(); + if(e>=0) + { + entity &c = ents[e]; + sprintf_s(closeent)("closest entity = %s (%d, %d, %d, %d), selection = (%d, %d)", entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, getvar("selxs"), getvar("selys")); + }; +}; + +void loadsky(char *basename) +{ + static string lastsky = ""; + if(strcmp(lastsky, basename)==0) return; + char *side[] = { "ft", "bk", "lf", "rt", "dn", "up" }; + int texnum = 14; + loopi(6) + { + sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]); + int xs, ys; + if(!installtex(texnum+i, path(name), xs, ys, true)) conoutf("could not load sky textures"); + }; + strcpy_s(lastsky, basename); +}; + +COMMAND(loadsky, ARG_1STR); + +float cursordepth = 0.9f; +GLint viewport[4]; +GLdouble mm[16], pm[16]; +vec worldpos; + +void readmatrices() +{ + glGetIntegerv(GL_VIEWPORT, viewport); + glGetDoublev(GL_MODELVIEW_MATRIX, mm); + glGetDoublev(GL_PROJECTION_MATRIX, pm); +}; + +// stupid function to cater for stupid ATI linux drivers that return incorrect depth values + +float depthcorrect(float d) +{ + return (d<=1/256.0f) ? d*256 : d; +}; + +// find out the 3d target of the crosshair in the world easily and very acurately. +// sadly many very old cards and drivers appear to fuck up on glReadPixels() and give false +// coordinates, making shooting and such impossible. +// also hits map entities which is unwanted. +// could be replaced by a more acurate version of monster.cpp los() if needed + +void readdepth(int w, int h) +{ + glReadPixels(w/2, h/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth); + double worldx = 0, worldy = 0, worldz = 0; + gluUnProject(w/2, h/2, depthcorrect(cursordepth), mm, pm, viewport, &worldx, &worldz, &worldy); + worldpos.x = (float)worldx; + worldpos.y = (float)worldy; + worldpos.z = (float)worldz; + vec r = { (float)mm[0], (float)mm[4], (float)mm[8] }; + vec u = { (float)mm[1], (float)mm[5], (float)mm[9] }; + setorient(r, u); +}; + +void drawicon(float tx, float ty, int x, int y, int tex = 5, float scaling = 1/3.0f) // EDIT: AH +{ + glBindTexture(GL_TEXTURE_2D, tex); + glBegin(GL_QUADS); + tx /= 192; + ty /= 192; + float o = scaling; + int s = 120; + glTexCoord2f(tx, ty); glVertex2i(x, y); + glTexCoord2f(tx+o, ty); glVertex2i(x+s, y); + glTexCoord2f(tx+o, ty+o); glVertex2i(x+s, y+s); + glTexCoord2f(tx, ty+o); glVertex2i(x, y+s); + glEnd(); + xtraverts += 4; +}; + +void invertperspective() +{ + // This only generates a valid inverse matrix for matrices generated by gluPerspective() + GLdouble inv[16]; + memset(inv, 0, sizeof(inv)); + + inv[0*4+0] = 1.0/pm[0*4+0]; + inv[1*4+1] = 1.0/pm[1*4+1]; + inv[2*4+3] = 1.0/pm[3*4+2]; + inv[3*4+2] = -1.0; + inv[3*4+3] = pm[2*4+2]/pm[3*4+2]; + + glLoadMatrixd(inv); +}; + +VAR(crosshairsize, 0, 15, 50); + +int dblend = 0; +void damageblend(int n) { dblend += n; }; + +VAR(hidestats, 0, 0, 1); +VAR(crosshairfx, 0, 1, 1); + +VAR(scopefov, 5, 50, 50); +bool scoped = false; +int oldfov = 100; + +void togglescope() +{ + if(player1->gunselect!=GUN_SNIPER) return; + if(!scoped) oldfov = getvar("fov"); + scoped = !scoped; + setvar("fov", scoped ? scopefov : oldfov); +} +COMMAND(togglescope, ARG_NONE); + +void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater) +{ + + readmatrices(); + if(editmode) + { + if(cursordepth==1.0f) worldpos = player1->o; + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + cursorupdate(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + }; + + glDisable(GL_DEPTH_TEST); + invertperspective(); + glPushMatrix(); + glOrtho(0, VIRTW, VIRTH, 0, -1, 1); + glEnable(GL_BLEND); + + if(dblend || underwater) + { + glDepthMask(GL_FALSE); + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glBegin(GL_QUADS); + if(dblend) glColor3d(0.0f, 0.9f, 0.9f); + else glColor3d(0.9f, 0.5f, 0.0f); + glVertex2i(0, 0); + glVertex2i(VIRTW, 0); + glVertex2i(VIRTW, VIRTH); + glVertex2i(0, VIRTH); + glEnd(); + glDepthMask(GL_TRUE); + dblend -= curtime/3; + if(dblend<0) dblend = 0; + }; + + glEnable(GL_TEXTURE_2D); + + char *command = getcurcommand(); + dynent *player = playerincrosshair(); + if(command) draw_textf("> %s_", 20, 1570, 2, (int)command); + else if(closeent[0]) draw_text(closeent, 20, 1570, 2); + else if(player) + { + string plinfo; + if(isteam(player->team, player1->team)) sprintf_s(plinfo)("%s (health:%i armour:%i)", player->name, player->health, player->armour); + else sprintf_s(plinfo)("%s", player->name); + draw_text(plinfo, 20, 1570, 2); + }; + + renderscores(); + if(!rendermenu()) + { + if(scoped && player1->gunselect == GUN_SNIPER) + { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.9f); + glBindTexture(GL_TEXTURE_2D, 10); + glBegin(GL_QUADS); + glColor3ub(255,255,255); + float chsize = (float)crosshairsize; + glTexCoord2d(0.0, 0.0); glVertex2f(0, 0); + glTexCoord2d(1.0, 0.0); glVertex2f(VIRTW, 0); + glTexCoord2d(1.0, 1.0); glVertex2f(VIRTW, VIRTH); + glTexCoord2d(0.0, 1.0); glVertex2f(0, VIRTH); + glEnd(); + glDisable(GL_ALPHA_TEST); + } + else if (player1->gunselect != GUN_SNIPER) + { + glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, 1); + glBegin(GL_QUADS); + glColor3ub(255,255,255); + if(crosshairfx) + { + if(player1->gunwait) glColor3ub(128,128,128); + else if(player1->health<=25) glColor3ub(255,0,0); + else if(player1->health<=50) glColor3ub(255,128,0); + }; + float chsize = (float)crosshairsize; + glTexCoord2d(0.0, 0.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 - chsize); + glTexCoord2d(1.0, 0.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 - chsize); + glTexCoord2d(1.0, 1.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 + chsize); + glTexCoord2d(0.0, 1.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 + chsize); + glEnd(); + } + }; + + + + glPopMatrix(); + + glPushMatrix(); + glOrtho(0, VIRTW*4/3, VIRTH*4/3, 0, -1, 1); + renderconsole(); + + if(!hidestats) + { + glPopMatrix(); + glPushMatrix(); + glOrtho(0, VIRTW*3/2, VIRTH*3/2, 0, -1, 1); + draw_textf("fps %d", 3200, 2320, 2, curfps); + draw_textf("lod %d", 3200, 2390, 2, lod_factor()); + draw_textf("wqd %d", 3200, 2460, 2, nquads); + draw_textf("wvt %d", 3200, 2530, 2, curvert); + draw_textf("evt %d", 3200, 2600, 2, xtraverts); + }; + + glPopMatrix(); + + if(player1->state==CS_ALIVE) + { + glPushMatrix(); + glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); + draw_textf("%d", 90, 827, 2, player1->health); + if(player1->armour) draw_textf("%d", 390, 827, 2, player1->armour); + //draw_textf("%d", 690, 827, 2, player1->mag[player1->gunselect]); + if (player1->gunselect!=GUN_KNIFE) + { + //draw_textf("%d", 690, 827, 2, player1->mag[player1->gunselect]); + char gunstats [64]; + if (player1->gunselect!=GUN_GRENADE) + sprintf(gunstats,"%i/%i",player1->mag[player1->gunselect],player1->ammo[player1->gunselect]); + else sprintf(gunstats,"%i",player1->mag[player1->gunselect]); + draw_text(gunstats, 690, 827, 2); + //clean up pointer? + //delete gunstats; + } + + glPopMatrix(); + + // EDIT: AH - ctf + #define ctf_scl 0.75 + if(m_ctf) + { + glPushMatrix(); + glOrtho(0, VIRTW, VIRTH, 0, -1, 1); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glColor4f(1.0f, 1.0f, 1.0f, 0.2f); + if(flaginfos[TEAM_CLA].state == CTFF_INBASE) glDisable(GL_BLEND); else glEnable(GL_BLEND); + drawicon(64*ctf_scl, 0, 1940, 1650, 11, 1.0f/4.0f); + if(flaginfos[TEAM_RVSF].state == CTFF_INBASE) glDisable(GL_BLEND); else glEnable(GL_BLEND); + drawicon(0, 0, 1820, 1650, 11, 1.0f/4.0f); + glDisable(GL_BLEND); + drawicon(rb_team_int(player1->team)*64*ctf_scl, 64*ctf_scl, VIRTW-128-10, 10, 11, 1.0f/4.0f); // shows which team you are + glPopMatrix(); + + // big flag icon + flaginfo &f = flaginfos[rb_opposite(rb_team_int(player1->team))]; + if(f.state==CTFF_STOLEN && f.thief == player1 && f.pick_ack ) + { + glPushMatrix(); + glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glColor4f(1.0f, 1.0f, 1.0f, (float)(sin(lastmillis / 100.0) + 1) / 2); + glEnable(GL_BLEND); + drawicon(rb_opposite(rb_team_int(player1->team))*128*ctf_scl, 128*ctf_scl, 1065, VIRTH/2/3*2, 11, 1.0f/2.0f); + glPopMatrix(); + }; + }; + + glPushMatrix(); + glOrtho(0, VIRTW, VIRTH, 0, -1, 1); + glDisable(GL_BLEND); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1.0f, 1.0f, 1.0f, (float) 0.2f+(sin(lastmillis/100)+1)/2); + + if(player1->health <= 20) + { + glEnable(GL_BLEND); + drawicon(64, 0, 20, 1650); //drawicon(128, 128, 20, 1650); + glDisable(GL_BLEND); + } + else drawicon(64, 0, 20, 1650); //drawicon(128, 128, 20, 1650); + if(player1->armour) drawicon((float)(128), 0, 620, 1650); //if(player1->armour) drawicon((float)(player1->armourtype*64), 0, 620, 1650); + int g = player1->gunselect; + int r = 64; + if(g>2) { g -= 3; r = 128; }; + + if(!player1->mag[player1->gunselect] && player1->gunselect != GUN_KNIFE && player1->gunselect != GUN_GRENADE) + { + glEnable(GL_BLEND); + drawicon((float)(g*64), (float)r, 1220, 1650); + glDisable(GL_BLEND); + } + else + { + if (player1->gunselect==GUN_GRENADE) drawicon(0, 0, 1220, 1650); + else drawicon((float)(g*64), (float)r, 1220, 1650); + } + glPopMatrix(); + }; + + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +}; + +#define MAXSHOTLINES 100 +struct shotline +{ + vec from, to; + bool inuse; + int millis; +}; +shotline shotlines[MAXSHOTLINES]; + +void shotlinereset() { loopi(MAXSHOTLINES) shotlines[i].inuse = false; }; + +void addshotline(dynent *d, vec &from, vec &to) +{ + if(d == player1) return; + if(rnd(3) != 0) return; + loopi(MAXSHOTLINES) + { + shotline *s = &shotlines[i]; + if(s->inuse) continue; + int start = 10; + vdist(_dist, _unitv, player1->o, to); + if(_dist <= 10.0f) start = 8; + else start = 5; + + vdist(dist, unitv, from, to); + vdiv(unitv, dist); + s->inuse = true; + s->from = unitv; + vmul(s->from, dist/10*start); + vadd(s->from, from); + s->to = unitv; + vmul(s->to, dist/10*-(10-start-2)); + vadd(s->to, to); + s->millis = lastmillis; + return; + }; +}; + +VAR(shotline_duration, 0, 75, 10000); + +void rendershotlines() +{ + loopi(MAXSHOTLINES) + { + shotline *s = &shotlines[i]; + if(!s->inuse) continue; + if(lastmillis-s->millis > shotline_duration) { s->inuse = false; continue; } + glPushMatrix(); + glDisable(GL_TEXTURE_2D); + + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); + glDisable(GL_FOG); + + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glBegin(GL_LINES); + glColor4f(1.0f, 1.0f, 0.7f, 0.5f); + glVertex3f(s->from.x, s->from.z, s->from.y); + glVertex3f(s->to.x, s->to.z, s->to.y); + glEnd(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + glEnable(GL_FOG); + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + + glEnable(GL_TEXTURE_2D); + glPopMatrix(); + }; +}; + +void renderphysents() +{ + loopv(physents) + { + physent *d = physents[i]; + if(d && d->state==NADE_THROWED) rendermodel("weapons/grenade/static", 0, 1, 0, 0, d->o.x, d->o.z, d->o.y, d->yaw, d->pitch, false, 2.0f, 100.0f, 0, 0); + }; +}; + diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index 52e0d425..5d7864cb 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -283,7 +283,7 @@ void drawhudmodel(int start, int end, float speed, int base) { sprintf_sd(mdl)("hudguns/%s", hudgunnames[player1->gunselect]); rendermodel(mdl, start, end, 0, 1.0f, player1->o.x, player1->o.z, player1->o.y, player1->yaw+90, player1->pitch, false, 1.0f, speed, 0, base); -}; +}; void drawhudgun(int w, int h, float aspect, int farplane) @@ -377,6 +377,8 @@ void gl_drawframe(int w, int h, float changelod, float curfps) xtraverts = 0; renderclients(); + //monsterrender(); + BotManager.RenderBots(); // Added by Rick renderentities(); @@ -389,6 +391,12 @@ void gl_drawframe(int w, int h, float changelod, float curfps) glDisable(GL_CULL_FACE); + // Added by Rick: Need todo here because of drawing the waypoints + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + WaypointClass.Think(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + // end add + drawhudgun(w, h, aspect, farplane); overbright(1); diff --git a/source/src/rendermd3.cpp b/source/src/rendermd3.cpp index 9105f749..f7140225 100644 --- a/source/src/rendermd3.cpp +++ b/source/src/rendermd3.cpp @@ -79,9 +79,9 @@ struct md3model md3tag *tags; int numframes, numtags; bool loaded; - vec scale; + vec scale; bool mirrored; - void setanim(int anim); + void setanim(int anim); void setanimstate(md3state &as); bool link(md3model *link, char *tag); bool load(char *path, bool mirror); @@ -92,12 +92,12 @@ struct md3model }; md3model::md3model() -{ +{ mirrored = false; loaded = false; scale.x = scale.y = scale.z = MD3_DEFAULT_SCALE; -// animstate = NULL; - animstate = new md3state(); +// animstate = NULL; + animstate = new md3state(); animstate->lastTime = lastmillis; }; @@ -111,13 +111,13 @@ md3model::~md3model() if(links) free(links); if(tags) delete [] tags; }; - -void md3model::setanimstate(md3state &as) -{ - animstate->anim = as.anim; - animstate->frm = as.frm; - animstate->lastTime = as.lastTime; -}; + +void md3model::setanimstate(md3state &as) +{ + animstate->anim = as.anim; + animstate->frm = as.frm; + animstate->lastTime = as.lastTime; +}; void md3model::setanim(int anim) { @@ -137,7 +137,7 @@ bool md3model::link(md3model *link, char *tag) }; bool md3model::load(char *path, bool mirror=false) -{ +{ mirrored = mirror; if(!path) return false; FILE *f = fopen(path, "rb"); @@ -268,7 +268,7 @@ void md3model::render() if(animdebug) printf("interp. from frame %i to %i, %f percent done\n", a->frm, nextfrm, t*100.0f); - #define interpolate(p1,p2) ((p1) + t * ((p2) - (p1))) + #define interpolate(p1,p2) ((p1) + t * ((p2) - (p1))) loopv(meshes) { @@ -295,7 +295,7 @@ void md3model::render() }; glEnd(); }; - + loopi(numtags) // render the linked models - interpolate rotation and position of the 'link-tags' { @@ -329,11 +329,11 @@ void md3model::draw(float x, float y, float z, float yaw, float pitch, float rad glRotatef(-90, 1, 0, 0); glScalef( scale.x, scale.y, scale.z); - - if(mirrored) glCullFace(GL_BACK); - render(); - + if(mirrored) glCullFace(GL_BACK); + + render(); + if(mirrored) glCullFace(GL_FRONT); glPopMatrix(); @@ -396,7 +396,7 @@ void loadweapons() { firstweapon = models.length(); loopi(NUMGUNS+1) - { + { bool akimbo = i==NUMGUNS; sprintf(basedir, "packages/models/weapons/%s", akimbo ? hudgunnames[GUN_PISTOL] : hudgunnames[i]); md3model *mdl = new md3model(); @@ -420,71 +420,71 @@ VAR(swayupmovediv, 1, 200, 1000); VAR(x, 0, 0, 1000); VAR(y, 0, 0, 1000); -VAR(z, 0, 0, 1000); - -struct weaponmove -{ - float k_rot, kick; - vec pos; - int anim; - - weaponmove(vec base, int basetime) - { - bool akimbo = player1->akimbo && player1->gunselect==GUN_PISTOL; - bool throwingnade = player1->gunselect==GUN_GRENADE && player1->thrownademillis; - int timediff = throwingnade ? (lastmillis-player1->thrownademillis) : lastmillis-basetime; - int animtime = attackdelay(player1->gunselect); - int rtime = reloadtime(player1->gunselect); - kick = k_rot = 0.0f; - pos = player1->o; - anim = MDL_GUN_IDLE; - bool attacking = player1->lastaction && player1->lastattackgun==player1->gunselect /*&& lastmillis-player1->lastactionlastmillis) // akimbo gun queued for reloading - { - - } - else if(player1->reloading) - { - anim = MDL_GUN_RELOAD; +VAR(z, 0, 0, 1000); + +struct weaponmove +{ + float k_rot, kick; + vec pos; + int anim; + + weaponmove(vec base, int basetime) + { + bool akimbo = player1->akimbo && player1->gunselect==GUN_PISTOL; + bool throwingnade = player1->gunselect==GUN_GRENADE && player1->thrownademillis; + int timediff = throwingnade ? (lastmillis-player1->thrownademillis) : lastmillis-basetime; + int animtime = attackdelay(player1->gunselect); + int rtime = reloadtime(player1->gunselect); + kick = k_rot = 0.0f; + pos = player1->o; + anim = MDL_GUN_IDLE; + bool attacking = player1->lastaction && player1->lastattackgun==player1->gunselect /*&& lastmillis-player1->lastactionlastmillis) // akimbo gun queued for reloading + { + + } + else if(player1->reloading) + { + anim = MDL_GUN_RELOAD; float percent_done = (float)(timediff)*100.0f/rtime; - if(percent_done >= 100) percent_done = 100; - k_rot = -(sin((float)(percent_done*2/100.0f*90.0f)*PI/180.0f)*90); - } - else if(throwingnade && timediff>animtime-rtime/2 && timediffgunselect==player1->lastattackgun) - { + if(percent_done >= 100) percent_done = 100; + k_rot = -(sin((float)(percent_done*2/100.0f*90.0f)*PI/180.0f)*90); + } + else if(throwingnade && timediff>animtime-rtime/2 && timediffgunselect==player1->lastattackgun) + { percent_done = timediff*100.0f/(float)animtime; if(percent_done > 100.0f) percent_done = 100.0f; // f(x) = -sin(x-1.5)^3 - kick = -sin(pow((1.5f/100.0f*percent_done)-1.5f,3)); - }; - - if(attacking && timediffgunselect!=GUN_GRENADE && player1->gunselect!=GUN_KNIFE) - { + kick = -sin(pow((1.5f/100.0f*percent_done)-1.5f,3)); + }; + + if(attacking && timediffgunselect!=GUN_GRENADE && player1->gunselect!=GUN_KNIFE) + { k_rot = kick_rot(player1->gunselect)*kick; - k_back = kick_back(player1->gunselect)*kick/10; - }; + k_back = kick_back(player1->gunselect)*kick/10; + }; int swayt = (lastmillis+1)/10 % 100; float swayspeed = (float) (sin((float)lastmillis/swayspeeddiv))/(swaymovediv/10.0f); @@ -505,14 +505,14 @@ struct weaponmove sway.x *= swayspeed; sway.y *= swayspeed; - sway.z *= swayupspeed; - - pos.x = player1->o.x-base.x*k_back+sway.x; - pos.y = player1->o.y-base.y*k_back+sway.y; - pos.z = player1->o.z-base.z*k_back+sway.z; - }; - }; -}; + sway.z *= swayupspeed; + + pos.x = player1->o.x-base.x*k_back+sway.x; + pos.y = player1->o.y-base.y*k_back+sway.y; + pos.z = player1->o.z-base.z*k_back+sway.z; + }; + }; +}; void rendermd3gun() { @@ -533,25 +533,25 @@ void rendermd3gun() }; vdist(dist, unitv, player1->o, worldpos); - vdiv(unitv, dist); - - md3model *weapon = models[firstweapon + player1->gunselect]; - md3model *akimbo = player1->akimbo && player1->gunselect==GUN_PISTOL ? models[firstweapon+NUMGUNS] : NULL; - - weaponmove *w1, *w2; - - if(akimbo) - { - w1 = new weaponmove(unitv, akimbolastaction[0]); - w2 = new weaponmove(unitv, akimbolastaction[1]); - akimbo->setanim(w2->anim); - } - else w1 = new weaponmove(unitv, player1->lastaction); - - if(!w1) return; - weapon->setanim(w1->anim); - weapon->draw( w1->pos.x, w1->pos.z, w1->pos.y, player1->yaw + 90, player1->pitch+w1->k_rot, 1.0f, light); - + vdiv(unitv, dist); + + md3model *weapon = models[firstweapon + player1->gunselect]; + md3model *akimbo = player1->akimbo && player1->gunselect==GUN_PISTOL ? models[firstweapon+NUMGUNS] : NULL; + + weaponmove *w1, *w2; + + if(akimbo) + { + w1 = new weaponmove(unitv, akimbolastaction[0]); + w2 = new weaponmove(unitv, akimbolastaction[1]); + akimbo->setanim(w2->anim); + } + else w1 = new weaponmove(unitv, player1->lastaction); + + if(!w1) return; + weapon->setanim(w1->anim); + weapon->draw( w1->pos.x, w1->pos.z, w1->pos.y, player1->yaw + 90, player1->pitch+w1->k_rot, 1.0f, light); + if(akimbo && w2) akimbo->draw( w2->pos.x, w2->pos.z, w2->pos.y, player1->yaw + 90, player1->pitch+w2->k_rot, 1.0f, light); }; }; diff --git a/source/src/savegamedemo.cpp b/source/src/savegamedemo.cpp index 172d4d37..57e4e858 100644 --- a/source/src/savegamedemo.cpp +++ b/source/src/savegamedemo.cpp @@ -35,6 +35,15 @@ void stop() demoloading = false; loopv(playerhistory) zapdynent(playerhistory[i]); playerhistory.setsize(0); + // Added by Rick: Remove bots + loopv(bots) + { + if (!bots[i]) continue; + delete bots[i]->pBot; + bots[i]->pBot = NULL; + zapdynent(bots[i]); + } + bots.setsize(0); }; void stopifrecording() { if(demorecording) stop(); }; diff --git a/source/src/server.cpp b/source/src/server.cpp index c9129c8b..c6d0cd94 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -15,6 +15,13 @@ struct client // server side version of "dynent" type string mapvote; string name; int modevote; + // Added by Rick: For bot voting + bool addbot, kickbot, changebotskill; + short botcount; + string botteam, botname; + short botskill; + bool kickallbots; + // End add }; vector clients; @@ -29,17 +36,18 @@ int numclients() return num; }; -struct server_entity // server side version of "entity" type -{ - bool spawned; - int spawnsecs; -}; - vector sents; bool notgotitems = true; // true when map has changed and waiting for clients to send item int mode = 0; +// Added by Rick +#ifndef STANDALONE +extern void kickbot(const char *szName); +extern void kickallbots(void); +#endif +// End add + void restoreserverstate(vector &ents) // hack: called from savegame code, only works in SP { loopv(sents) @@ -286,6 +294,207 @@ bool vote(char *map, int reqmode, int sender) return true; }; +// Added by Rick: Vote for bot commands + +void resetbotvotes() +{ + loopv(clients) + { + clients[i].addbot = false; + clients[i].kickbot = false; + clients[i].changebotskill = false; + clients[i].kickallbots = false; + clients[i].botcount = 0; + clients[i].botteam[0] = 0; + clients[i].botname[0] = 0; + clients[i].botskill = -1; + } +}; + +bool addbotvote(int count, char *team, int skill, char *name, int sender) +{ +#ifndef STANDALONE + clients[sender].addbot = true; + clients[sender].kickbot = false; + clients[sender].changebotskill = false; + clients[sender].botcount = (count>=0) ? count : 0; + if (team && team[0]) strcpy_s(clients[sender].botteam, team); + else clients[sender].botteam[0] = 0; + clients[sender].botskill = skill; + if (name && name[0]) strcpy_s(clients[sender].botname, name); + else clients[sender].botname[0] = 0; + int yes = 0, no = 0; + loopv(clients) if(clients[i].type!=ST_EMPTY) + { + if (clients[i].addbot) + { + if (!strcmp(clients[i].botteam, team) && !strcmp(clients[i].botname, name) && + (clients[i].botskill == skill) && + (clients[i].botcount == clients[sender].botcount)) + yes++; + else + no++; + } + else no++; + } + if(yes==1 && no==0) return true; // single player + sprintf_sd(msg)("%s suggests to add a bot", clients[sender].name); + if (team && team[0]) + { + strcat(msg, " on team "); + strcat(msg, team); + } + + if (skill!=-1) + { + strcat(msg, ", with skill "); + strcat(msg, SkillNrToSkillName(skill)); + } + + if (name && name[0]) + { + strcat(msg, " named "); + strcat(msg, name); + } + + sendservmsg(msg); + if(yes/(float)(yes+no) <= 0.5f) return false; + sendservmsg("vote passed"); + resetbotvotes(); +#endif + return true; +}; + +bool kickbotvote(int specific, char *name, int sender) +{ + clients[sender].addbot = false; + clients[sender].kickbot = true; + clients[sender].changebotskill = false; + clients[sender].kickallbots = !specific; + if (name && name[0]) strcpy_s(clients[sender].botname, name); + else clients[sender].botname[0] = 0; + int yes = 0, no = 0; + loopv(clients) if(clients[i].type!=ST_EMPTY) + { + if (clients[i].kickbot) + { + if (clients[sender].kickallbots) + { + if (clients[i].kickallbots) yes++; + else no++; + } + else + { + if (!clients[i].kickallbots && !strcmp(clients[i].botname, name)) + yes++; + else + no++; + } + } + else no++; + } + if(yes==1 && no==0) return true; // single player + + char msg[256]; + if (clients[sender].kickallbots) + sprintf(msg, "%s suggests to kick all bots", clients[sender].name); + else + sprintf(msg, "%s suggests to kick bot %s", clients[sender].name, + clients[sender].botname); + + sendservmsg(msg); + if(yes/(float)(yes+no) <= 0.5f) return false; + sendservmsg("vote passed"); + resetbotvotes(); + return true; +}; + +bool botskillvote(int skill, int sender) +{ +#ifndef STANDALONE + clients[sender].addbot = false; + clients[sender].kickbot = false; + clients[sender].changebotskill = true; + clients[sender].botskill = skill; + int yes = 0, no = 0; + loopv(clients) if(clients[i].type!=ST_EMPTY) + { + if (clients[i].changebotskill) + { + if (clients[sender].botskill == clients[i].botskill) yes++; + else no++; + } + else no++; + } + if(yes==1 && no==0) return true; // single player + + sprintf_sd(msg)("%s suggests to change the skill of all bots to: %s", clients[sender].name, + SkillNrToSkillName(clients[sender].botskill)); + sendservmsg(msg); + if(yes/(float)(yes+no) <= 0.5f) return false; + sendservmsg("vote passed"); + resetbotvotes(); +#endif + return true; +}; + +void botcommand(uchar *&p, char *text, int sender) +{ + int type = getint(p); + switch(EBotCommands(type)) + { + case COMMAND_ADDBOT: + { + string name, team; + int count = getint(p); + int skill = getint(p); + sgetstr(); + strcpy(team, text); + sgetstr(); + strcpy(name, text); +#ifndef STANDALONE + if (addbotvote(count, team, skill, name, sender)) + { + while(count>0) + { + BotManager.CreateBot(team, SkillNrToSkillName(skill), name); + count--; + } + } +#endif + break; + } + case COMMAND_KICKBOT: + { + int specific = getint(p); + + if (specific) + sgetstr(); +#ifndef STANDALONE + if (kickbotvote(specific, text, sender)) + { + if (specific) + kickbot(text); + else + kickallbots(); + } +#endif + } + case COMMAND_BOTSKILL: + { + int skill = getint(p); +#ifndef STANDALONE + if (botskillvote(skill, sender)) + { + BotManager.ChangeBotSkill(skill, NULL); + } +#endif + } + } +} + +// End add + // force map change, EDIT: AH void changemap() { @@ -378,6 +587,12 @@ void process(ENetPacket * packet, int sender) // sender may be -1 int n = getint(p); pickup(n, getint(p), sender); break; + }; + + case SV_BOTITEMPICKUP: + { + loopi(2) getint(p); + break; }; case SV_PING: @@ -411,6 +626,21 @@ void process(ENetPacket * packet, int sender) // sender may be -1 send(sender, recvmap(sender)); return; + // Added by Rick: Bot specifc messages + case SV_ADDBOT: + getint(p); + sgetstr(); + //strcpy_s(clients[cn].name, text); + sgetstr(); + getint(p); + break; + case SV_BOTCOMMAND: // Client asked server for a bot command + { + botcommand(p, text, sender); + break; + } + // End add by Rick + // EDIT: AH case SV_FLAGPICKUP: { @@ -559,6 +789,7 @@ void resetserverifempty() clients.setsize(0); smapname[0] = 0; resetvotes(); + resetbotvotes(); // Added by Rick resetitems(); mode = 0; mapreload = false; diff --git a/source/src/serverutil.cpp b/source/src/serverutil.cpp index 92caa68c..381daf20 100644 --- a/source/src/serverutil.cpp +++ b/source/src/serverutil.cpp @@ -35,6 +35,20 @@ const char *modenames[] = //const char *modestr(int n) { return (n>=-2 && n<12) ? modenames[n+2] : "unknown"; }; const char *modestr(int n) { return (n>=0 && n<6) ? modenames[n] : "unknown"; }; +/* Modified by Rick: New messages and different sizes for existing ones +char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes +{ + SV_INITS2C, 4, SV_INITC2S, 0, SV_POS, 12, SV_TEXT, 0, SV_SOUND, 2, SV_CDIS, 2, + SV_DIED, 2, SV_DAMAGE, 4, SV_SHOsT, 8, SV_FRAGS, 2, + SV_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_ITEMACC, 2, + SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 3, SV_DENIED, 2, + SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2, + SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, + SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, + SV_EXT, 0, + -1 +}; +*/ char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes { @@ -47,6 +61,11 @@ char msgsizesl[] = // size inclusive message token, 0 for variable SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_WEAPCHANGE, 2, SV_MODELSKIN, 2, SV_FLAGPICKUP, 2, SV_FLAGDROP, 2, SV_FLAGRETURN, 2, SV_FLAGSCORE, 2, SV_FLAGINFO, 0, SV_FLAGS, 2, // EDIT: AH + // Added by Rick: Bot specific messages + SV_BOTSOUND, 3, SV_BOTDIS, 2, SV_BOTDIED, 4, SV_CLIENT2BOTDMG, 4, SV_BOT2BOTDMG, 2, + SV_BOTFRAGS, 3, SV_ADDBOT, 5, SV_BOTUPDATE, 12, SV_BOTCOMMAND, 0, + // End add + SV_BOTITEMPICKUP, 3, SV_BOT2CLIENTDMG, 5, SV_DIEDBYBOT, 2, SV_EXT, 0, -1 }; diff --git a/source/src/sound.cpp b/source/src/sound.cpp index 377c7427..be8ae857 100644 --- a/source/src/sound.cpp +++ b/source/src/sound.cpp @@ -1,227 +1,234 @@ -// sound.cpp: uses fmod on windows and sdl_mixer on unix (both had problems on the other platform) - -#include "cube.h" - -//#ifndef WIN32 // NOTE: fmod not being supported for the moment as it does not allow stereo pan/vol updating during playback -#define USE_MIXER -//#endif - -VARP(soundvol, 0, 255, 255); -VARP(musicvol, 0, 128, 255); -bool nosound = false; - -#define MAXCHAN 32 -#define SOUNDFREQ 22050 - -struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN]; - -#ifdef USE_MIXER - #include "SDL_mixer.h" - #define MAXVOL MIX_MAX_VOLUME - Mix_Music *mod = NULL; - void *stream = NULL; -#else - #include "fmod.h" - #define MAXVOL 255 - FMUSIC_MODULE *mod = NULL; - FSOUND_STREAM *stream = NULL; -#endif - -void stopsound() -{ - if(nosound) return; - if(mod) - { - #ifdef USE_MIXER - Mix_HaltMusic(); - Mix_FreeMusic(mod); - #else - FMUSIC_FreeSong(mod); - #endif - mod = NULL; - }; - if(stream) - { - #ifndef USE_MIXER - FSOUND_Stream_Close(stream); - #endif - stream = NULL; - }; -}; - -VAR(soundbufferlen, 128, 1024, 4096); - -void initsound() -{ - memset(soundlocs, 0, sizeof(soundloc)*MAXCHAN); - #ifdef USE_MIXER - if(Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0) - { - conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError()); - nosound = true; - }; - Mix_AllocateChannels(MAXCHAN); - #else - if(FSOUND_GetVersion()=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); }; - } - else - { - conoutf("could not play music: %s", sn); - }; - #endif - }; -}; - -COMMAND(music, ARG_1STR); - -#ifdef USE_MIXER -vector samples; -#else -vector samples; -#endif - -cvector snames; - -int registersound(char *name) -{ - loopv(snames) if(strcmp(snames[i], name)==0) return i; - snames.add(newstring(name)); - samples.add(NULL); - return samples.length()-1; -}; - -COMMAND(registersound, ARG_1EST); - -void cleansound() -{ - if(nosound) return; - stopsound(); - #ifdef USE_MIXER - Mix_CloseAudio(); - #else - FSOUND_Close(); - #endif -}; - -VAR(stereo, 0, 1, 1); - -void updatechanvol(int chan, vec *loc) -{ - int vol = soundvol, pan = 255/2; - if(loc) - { - vdist(dist, v, *loc, player1->o); - vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation - if(vol<0) vol = 0; - if(stereo && (v.x != 0 || v.y != 0)) - { - float yaw = -atan2(v.x, v.y) - player1->yaw*(PI / 180.0f); // relative angle of sound along X-Y axis - pan = int(255.9f*(0.5*sin(yaw)+0.5f)); // range is from 0 (left) to 255 (right) - }; - }; - vol = (vol*MAXVOL)/255; - #ifdef USE_MIXER - Mix_Volume(chan, vol); - Mix_SetPanning(chan, 255-pan, pan); - #else - FSOUND_SetVolume(chan, vol); - FSOUND_SetPan(chan, pan); - #endif -}; - -void newsoundloc(int chan, vec *loc) -{ - assert(chan>=0 && chan5) return; // avoid bursts of sounds with heavy packetloss and in sp - if(n<0 || n>=samples.length()) { conoutf("unregistered sound: %d", n); return; }; - - if(!samples[n]) - { - sprintf_sd(buf)("packages/audio/sounds/%s.wav", snames[n]); - - #ifdef USE_MIXER - samples[n] = Mix_LoadWAV(path(buf)); - #else - samples[n] = FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0); - #endif - - if(!samples[n]) { conoutf("failed to load sample: %s", (int)buf); return; }; - }; - - #ifdef USE_MIXER - int chan = Mix_PlayChannel(-1, samples[n], 0); - #else - int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true); - #endif - if(chan<0) return; - if(loc) newsoundloc(chan, loc); - updatechanvol(chan, loc); - #ifndef USE_MIXER - FSOUND_SetPaused(chan, false); - #endif -}; - -void sound(int n) { playsound(n, NULL); }; -COMMAND(sound, ARG_1INT); +// sound.cpp: uses fmod on windows and sdl_mixer on unix (both had problems on the other platform) + +#include "cube.h" + +//#ifndef WIN32 // NOTE: fmod not being supported for the moment as it does not allow stereo pan/vol updating during playback +#define USE_MIXER +//#endif + +VARP(soundvol, 0, 255, 255); +VARP(musicvol, 0, 128, 255); +bool nosound = false; + +#define MAXCHAN 32 +#define SOUNDFREQ 22050 + +struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN]; + +#ifdef USE_MIXER + #include "SDL_mixer.h" + #define MAXVOL MIX_MAX_VOLUME + Mix_Music *mod = NULL; + void *stream = NULL; +#else + #include "fmod.h" + #define MAXVOL 255 + FMUSIC_MODULE *mod = NULL; + FSOUND_STREAM *stream = NULL; +#endif + +void stopsound() +{ + if(nosound) return; + if(mod) + { + #ifdef USE_MIXER + Mix_HaltMusic(); + Mix_FreeMusic(mod); + #else + FMUSIC_FreeSong(mod); + #endif + mod = NULL; + }; + if(stream) + { + #ifndef USE_MIXER + FSOUND_Stream_Close(stream); + #endif + stream = NULL; + }; +}; + +VAR(soundbufferlen, 128, 1024, 4096); + +void initsound() +{ + memset(soundlocs, 0, sizeof(soundloc)*MAXCHAN); + #ifdef USE_MIXER + if(Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0) + { + conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError()); + nosound = true; + }; + Mix_AllocateChannels(MAXCHAN); + #else + if(FSOUND_GetVersion()=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); }; + } + else + { + conoutf("could not play music: %s", sn); + }; + #endif + }; +}; + +COMMAND(music, ARG_1STR); + +#ifdef USE_MIXER +vector samples; +#else +vector samples; +#endif + +cvector snames; + +int registersound(char *name) +{ + loopv(snames) if(strcmp(snames[i], name)==0) return i; + snames.add(newstring(name)); + samples.add(NULL); + return samples.length()-1; +}; + +COMMAND(registersound, ARG_1EST); + +void cleansound() +{ + if(nosound) return; + stopsound(); + #ifdef USE_MIXER + Mix_CloseAudio(); + #else + FSOUND_Close(); + #endif +}; + +VAR(stereo, 0, 1, 1); + +void updatechanvol(int chan, vec *loc) +{ + int vol = soundvol, pan = 255/2; + if(loc) + { + vdist(dist, v, *loc, player1->o); + vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation + if(vol<0) vol = 0; + if(stereo && (v.x != 0 || v.y != 0)) + { + float yaw = -atan2(v.x, v.y) - player1->yaw*(PI / 180.0f); // relative angle of sound along X-Y axis + pan = int(255.9f*(0.5*sin(yaw)+0.5f)); // range is from 0 (left) to 255 (right) + }; + }; + vol = (vol*MAXVOL)/255; + #ifdef USE_MIXER + Mix_Volume(chan, vol); + Mix_SetPanning(chan, 255-pan, pan); + #else + FSOUND_SetVolume(chan, vol); + FSOUND_SetPan(chan, pan); + #endif +}; + +void newsoundloc(int chan, vec *loc) +{ + assert(chan>=0 && chano); +} +// End add by Rick + +int soundsatonce = 0, lastsoundmillis = 0; + +void playsound(int n, vec *loc) +{ + if(nosound) return; + if(!soundvol) return; + if(lastmillis==lastsoundmillis) soundsatonce++; else soundsatonce = 1; + lastsoundmillis = lastmillis; + if(soundsatonce>5) return; // avoid bursts of sounds with heavy packetloss and in sp + if(n<0 || n>=samples.length()) { conoutf("unregistered sound: %d", n); return; }; + + if(!samples[n]) + { + sprintf_sd(buf)("packages/audio/sounds/%s.wav", snames[n]); + + #ifdef USE_MIXER + samples[n] = Mix_LoadWAV(path(buf)); + #else + samples[n] = FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0); + #endif + + if(!samples[n]) { conoutf("failed to load sample: %s", (int)buf); return; }; + }; + + #ifdef USE_MIXER + int chan = Mix_PlayChannel(-1, samples[n], 0); + #else + int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true); + #endif + if(chan<0) return; + if(loc) newsoundloc(chan, loc); + updatechanvol(chan, loc); + #ifndef USE_MIXER + FSOUND_SetPaused(chan, false); + #endif +}; + +void sound(int n) { playsound(n, NULL); }; +COMMAND(sound, ARG_1INT); diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index 0db6ff43..2f332bdd 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -11,92 +11,95 @@ vec sg[SGRAYS]; //change the particales guninfo guns[NUMGUNS] = { - { S_KNIFE, S_NULL, 0, 500, 50, 0, 0, 1, 1, 1, 0, 0, "knife" }, + { S_KNIFE, S_NULL, 0, 500, 50, 0, 0, 1, 1, 1, 0, 0, "knife" }, { S_PISTOL, S_RPISTOL, 1400, 170, 20, 0, 0, 80, 10, 8, 6, 5, "pistol" }, // *SGRAYS { S_SHOTGUN, S_RSHOTGUN, 2400, 1000, 6, 0, 0, 1, 35, 7, 9, 9, "shotgun" }, //reload time is for 1 shell from 7 too powerful to 6 { S_SUBGUN, S_RSUBGUN, 1650, 80, 17, 0, 0, 70, 15, 30, 1, 2, "subgun" }, { S_SNIPER, S_RSNIPER, 1950, 1500, 72, 0, 0, 60, 50, 5, 4, 4, "sniper" }, { S_ASSULT, S_RASSULT, 2000, 130, 20, 0, 0, 20, 40, 20, 0, 2, "assult" }, //recoil was 44 - { S_GRENADE, S_NULL, 1000, 2500, 150, 20, 6, 1, 1, 1, 3, 1, "grenade" }, + { S_GRENADE, S_NULL, 1000, 2500, 150, 20, 6, 1, 1, 1, 3, 1, "grenade" }, }; -bool gun_changed = false; - -void weapon(int gun) -{ - if(gun>=0) gun %= G_NUM; - else gun = G_NUM-(gun % G_NUM); - gun_changed = true; - switch(gun) - { - case G_PRIMARY: player1->gunselect=player1->primary; break; - case G_SECONDARY: player1->gunselect=GUN_PISTOL; break; - case G_MELEE: player1->gunselect=GUN_KNIFE; break; - case G_GRENADE: player1->gunselect=GUN_GRENADE; break; - }; -}; - -void shiftweapon(int i) -{ - int gun; - switch(player1->gunselect) - { - case GUN_KNIFE: gun=G_MELEE; break; - case GUN_PISTOL: gun=G_SECONDARY; break; - case GUN_GRENADE: gun=G_GRENADE; break; - default: gun=G_PRIMARY; break; - }; - gun += i; - weapon(gun); +bool gun_changed = false; + +void weapon(int gun) +{ + if(gun>=0) gun %= G_NUM; + else gun = G_NUM-(gun % G_NUM); + gun_changed = true; + switch(gun) + { + case G_PRIMARY: player1->gunselect=player1->primary; break; + case G_SECONDARY: player1->gunselect=GUN_PISTOL; break; + case G_MELEE: player1->gunselect=GUN_KNIFE; break; + case G_GRENADE: player1->gunselect=GUN_GRENADE; break; + }; +}; + +void shiftweapon(int i) +{ + int gun; + switch(player1->gunselect) + { + case GUN_KNIFE: gun=G_MELEE; break; + case GUN_PISTOL: gun=G_SECONDARY; break; + case GUN_GRENADE: gun=G_GRENADE; break; + default: gun=G_PRIMARY; break; + }; + gun += i; + weapon(gun); } -COMMAND(weapon, ARG_1INT); +COMMAND(weapon, ARG_1INT); COMMAND(shiftweapon, ARG_1INT); -void reload() +void reload(dynent *d) { - bool akimbo = player1->gunselect==GUN_PISTOL && player1->akimbo!=0; + if(!d) return; - if(player1->gunselect==GUN_KNIFE || player1->gunselect==GUN_GRENADE) return; - if(akimbo && player1->mag[player1->gunselect]>=(guns[player1->gunselect].magsize * 2)) return; - else if(player1->mag[player1->gunselect]>=guns[player1->gunselect].magsize && player1->akimbo==0) return; - if(player1->ammo[player1->gunselect]<=0) return; - if(player1->reloading) return; + bool akimbo = d->gunselect==GUN_PISTOL && d->akimbo!=0; + + if(d->gunselect==GUN_KNIFE || d->gunselect==GUN_GRENADE) return; + if(akimbo && d->mag[d->gunselect]>=(guns[d->gunselect].magsize * 2)) return; + else if(d->mag[d->gunselect]>=guns[d->gunselect].magsize && d->akimbo==0) return; + if(d->ammo[d->gunselect]<=0) return; + if(d->reloading) return; - player1->reloading = true; - player1->lastaction = lastmillis; - - if(akimbo) - { - akimbolastaction[akimboside?1:0] = lastmillis; - akimbolastaction[akimboside?0:1] = lastmillis;// + (reloadtime(GUN_PISTOL)/2); + d->reloading = true; + d->lastaction = lastmillis; + + if(akimbo) + { + akimbolastaction[akimboside?1:0] = lastmillis; + akimbolastaction[akimboside?0:1] = lastmillis;// + (reloadtime(GUN_PISTOL)/2); }; - player1->gunwait = guns[player1->gunselect].reloadtime; + d->gunwait = guns[d->gunselect].reloadtime; //temp hack in drawhudgun() so no guns drawn while reloading, needs fixed - int a = guns[player1->gunselect].magsize - player1->mag[player1->gunselect]; - if(player1->gunselect==GUN_PISTOL && player1->akimbo!=0) - a = guns[player1->gunselect].magsize * 2 - player1->mag[player1->gunselect]; + int a = guns[d->gunselect].magsize - d->mag[d->gunselect]; + if(d->gunselect==GUN_PISTOL && d->akimbo!=0) + a = guns[d->gunselect].magsize * 2 - d->mag[d->gunselect]; - if (a >= player1->ammo[player1->gunselect]) + if (a >= d->ammo[d->gunselect]) { - player1->mag[player1->gunselect] += player1->ammo[player1->gunselect]; - player1->ammo[player1->gunselect] = 0; + d->mag[d->gunselect] += d->ammo[d->gunselect]; + d->ammo[d->gunselect] = 0; } else { - player1->mag[player1->gunselect] += a; - player1->ammo[player1->gunselect] -= a; + d->mag[d->gunselect] += a; + d->ammo[d->gunselect] -= a; } - if(akimbo) playsoundc(S_RAKIMBO); - else playsoundc(guns[player1->gunselect].reload); + if(akimbo) playsoundc(S_RAKIMBO); + else playsoundc(guns[d->gunselect].reload); }; - -COMMAND(reload,ARG_NONE); + +void selfreload() { reload(player1); }; +COMMANDN(reload, selfreload, ARG_NONE); int reloadtime(int gun) { return guns[gun].reloadtime; }; int attackdelay(int gun) { return guns[gun].attackdelay; }; @@ -118,7 +121,8 @@ void createrays(vec &from, vec &to) // create random spread of rays }; }; -bool intersect(dynent *d, vec &from, vec &to) // if lineseg hits entity bounding box +//bool intersect(dynent *d, vec &from, vec &to) // if lineseg hits entity bounding box +bool intersect(dynent *d, vec &from, vec &to, vec *end) // if lineseg hits entity bounding box { vec v = to, w = d->o, *p; vsub(v, from); @@ -158,6 +162,19 @@ dynent *playerincrosshair() }; return NULL; }; +// Added by Rick +char *botincrosshair() +{ + if(demoplayback) return NULL; + loopv(bots) + { + dynent *o = bots[i]; + if(!o) continue; + if(intersect(o, player1->o, worldpos)) return o->name; + }; + return NULL; +}; +// End add by Rick const int MAXPROJ = 100; struct projectile { vec o, to; float speed; dynent *owner; int gun; bool inuse, local; }; @@ -185,7 +202,58 @@ void newprojectile(vec &from, vec &to, float speed, bool local, dynent *owner, i void hit(int target, int damage, dynent *d, dynent *at) { if(d==player1) selfdamage(damage, at==player1 ? -1 : -2, at); - else { addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); playsound(S_PAIN1+rnd(5), &d->o); }; + //else if(d->monsterstate) monsterpain(d, damage, at); + // Added by Rick: Let bots take damage + else if (d->pBot) d->pBot->BotPain(damage, at); + else if (d->bIsBot) + { + int PlayerIndex = -1; + if (at->bIsBot) PlayerIndex = BotManager.GetBotIndex(at); + else + { + loopv(players) + { + if (!players[i]) continue; + + if (players[i] == at) + { + PlayerIndex = i; + break; + } + } + } + + int msgtype = (at->bIsBot) ? SV_BOT2BOTDMG : SV_CLIENT2BOTDMG; + addmsg(1, 4, msgtype, BotManager.GetBotIndex(d), damage, PlayerIndex); + playsound(S_PAIN1+rnd(5), &d->o); + } + // End add by Rick + //else { addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); playsound(S_PAIN1+rnd(5), &d->o); }; Modified by Rick: Added IsBot and PlayerIndex to message + else + { + // Modified by Rick: Added IsBot and PlayerIndex to message + int PlayerIndex = -1; + if (at->bIsBot) + PlayerIndex = BotManager.GetBotIndex(at); + else + { + loopv(players) + { + if (!players[i]) + continue; + + if (players[i] == at) + { + PlayerIndex = i; + break; + } + } + } + if(at->bIsBot) addmsg(1, 5, SV_BOT2CLIENTDMG, target, damage, d->lifesequence, PlayerIndex); + else addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); + playsound(S_PAIN1+rnd(5), &d->o); + }; + particle_splash(3, damage, 1000, d->o); demodamage(damage, d->o); }; @@ -208,7 +276,8 @@ void radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at) }; }; -void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster, int qdam) +// Modified by Rick: Added notthisbot +void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster, int notthisbot, int qdam) { particle_splash(0, 50, 300, v); p->inuse = false; @@ -231,15 +300,29 @@ void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismons if(!o) continue; radialeffect(o, v, i, qdam, p->owner); }; - }; + // Added by Rick + loopv(bots) + { + if(i==notthisbot) continue; + dynent *o = bots[i]; + if(!o) continue; + radialeffect(o, v, i, qdam, p->owner); + } + // End add by Rick + //dvector &mv = getmonsters(); + //loopv(mv) if(i!=notthismonster) radialeffect(mv[i], v, i, qdam, p->owner); + }; }; -inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam) +// Modified by Rick +// inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam) +inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int ib, int qdam) { if(o->state!=CS_ALIVE) return; if(intersect(o, p->o, v)) { - splash(p, v, p->o, i, im, qdam); + // splash(p, v, p->o, i, im, qdam); Modified by Rick + splash(p, v, p->o, i, im, ib, qdam); hit(i, qdam, o, p->owner); }; }; @@ -263,17 +346,36 @@ void moveprojectiles(float time) { dynent *o = players[i]; if(!o) continue; - projdamage(o, p, v, i, -1, qdam); + // projdamage(o, p, v, i, -1, qdam); Modified by Rick + projdamage(o, p, v, i, -1, -1, qdam); }; - if(p->owner!=player1) projdamage(player1, p, v, -1, -1, qdam); + // Added by Rick: Damage bots aswell + loopv(bots) + { + dynent *o = bots[i]; + if(!o || (o == p->owner)) continue; + projdamage(o, p, v, -1, -1, i, qdam); + }; + // End add by Rick + // if(p->owner!=player1) projdamage(player1, p, v, -1, -1, qdam); Modified by Rick + if(p->owner!=player1) projdamage(player1, p, v, -1, -1, -1, qdam); + //dvector &mv = getmonsters(); + //loopv(mv) if(!vreject(mv[i]->o, v, 10.0f) && mv[i]!=p->owner) projdamage(mv[i], p, v, -1, i, qdam); Modified by Rick + //loopv(mv) if(!vreject(mv[i]->o, v, 10.0f) && mv[i]!=p->owner) projdamage(mv[i], p, v, -1, i, -1, qdam); }; if(p->inuse) { - if(time==dtime) splash(p, v, p->o, -1, -1, qdam); + // if(time==dtime) splash(p, v, p->o, -1, -1, qdam); Modified by Rick + if(time==dtime) splash(p, v, p->o, -1, -1, -1, qdam); else { if(p->gun==GUN_GRENADE) { dodynlight(p->o, v, 0, 255, p->owner); particle_splash(5, 2, 200, v); } else { particle_splash(1, 1, 200, v); particle_splash(guns[p->gun].part, 1, 1, v); }; + // Added by Rick + traceresult_s tr; + TraceLine(p->o, v, p->owner, true, &tr); + if (tr.collided) splash(p, v, p->o, -1, -1, -1, qdam); + // End add }; }; p->o = v; @@ -288,7 +390,7 @@ void throw_nade(dynent *d, vec &to, physent *p) { if(!p || !d) return; printf("thrownade\n"); - playsound(S_GRENADETHROW, &d->o); + playsound(S_GRENADETHROW, &d->o); p->isphysent = true; p->gravity = 20; @@ -378,7 +480,7 @@ physent *new_nade(dynent *d, int millis = 0) { curnade = p; d->thrownademillis = 0; - }; + }; playsound(S_GRENADEPULL, &d->o); return p; }; @@ -394,7 +496,7 @@ void explode_nade(physent *i) vec dist = { 0.1f, 0.1f, 0.1f }; vadd(o, dist); throw_nade(i->owner, o, i); - }; + }; playsound(S_FEXPLODE, &i->o); newprojectile(i->o, i->o, 1, i->owner==player1, i->owner, GUN_GRENADE); }; @@ -422,7 +524,7 @@ void shootv(int gun, vec &from, vec &to, dynent *d, bool local, int nademillis) //particle_trail(1, 10, from, to); break; - case GUN_GRENADE: + case GUN_GRENADE: { if(d!=player1) { @@ -460,7 +562,7 @@ void raydamage(dynent *o, vec &from, vec &to, dynent *d, int i) if(damage) hitpush(i, damage, o, d, from, to); } else if(intersect(o, from, to)) hitpush(i, qdam, o, d, from, to); -}; +}; void spreadandrecoil(vec & from, vec & to, dynent * d) { @@ -503,13 +605,13 @@ void spreadandrecoil(vec & from, vec & to, dynent * d) if(d->pitch<80.0f) d->pitch += guns[d->gunselect].recoil*0.05f; }; - -//VAR(grenadepulltime, 0, 100, 10000); -const int grenadepulltime = 650; - -bool akimboside = false; -int akimbolastaction[2] = {0,0}; - + +//VAR(grenadepulltime, 0, 100, 10000); +const int grenadepulltime = 650; + +bool akimboside = false; +int akimbolastaction[2] = {0,0}; + void shoot(dynent *d, vec &targ) { @@ -517,10 +619,10 @@ void shoot(dynent *d, vec &targ) if(!d->attacking && d->gunselect==GUN_GRENADE && curnade) // throw { - if(attacktime>grenadepulltime) - { - d->thrownademillis = lastmillis; - throw_nade(d, targ, curnade); + if(attacktime>grenadepulltime) + { + d->thrownademillis = lastmillis; + throw_nade(d, targ, curnade); } return; }; @@ -538,17 +640,26 @@ void shoot(dynent *d, vec &targ) if(d->gunselect!=GUN_SUBGUN && d->gunselect!=GUN_ASSULT && d->gunselect!=GUN_GRENADE) d->attacking = false; //makes sub/assult autos else d->shots++; - if(d->gunselect==GUN_PISTOL && d->akimbo!=0) - { - d->attacking = true; //make akimbo auto - akimbolastaction[akimboside?1:0] = lastmillis; - akimboside = !akimboside; - } + if(d->gunselect==GUN_PISTOL && d->akimbo) + { + d->attacking = true; //make akimbo auto + akimbolastaction[akimboside?1:0] = lastmillis; + akimboside = !akimboside; + } d->lastaction = lastmillis; d->lastattackgun = d->gunselect; - if(!d->mag[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; - + //if(!d->mag[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; + if(!d->mag[d->gunselect]) + { + if (d->bIsBot) botplaysound(S_NOAMMO, d); + else playsoundc(S_NOAMMO); + d->gunwait = 250; + d->lastattackgun = -1; + return; + }; + // End mod + if(d->gunselect) d->mag[d->gunselect]--; vec from = d->o; @@ -567,18 +678,17 @@ void shoot(dynent *d, vec &targ) vadd(to, unitv); }; if(d->gunselect==GUN_SHOTGUN) createrays(from, to); - - //if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP); if(d->gunselect==GUN_PISTOL && d->akimbo) d->gunwait = guns[d->gunselect].attackdelay / 2; //make akimbo pistols shoot twice as fast as normal pistol else d->gunwait = guns[d->gunselect].attackdelay; if(d->gunselect==GUN_GRENADE) // activate { - if(!curnade) new_nade(player1); + if(!curnade) new_nade(d); return; } +//fixmebot shootv(d->gunselect, from, to, d, 0); addmsg(1, 9, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), 0 ); @@ -589,6 +699,16 @@ void shoot(dynent *d, vec &targ) dynent *o = players[i]; if(!o) continue; raydamage(o, from, to, d, i); - }; - + }; + + // Added by Rick: raydamage on bots too + loopv(bots) + { + dynent *o = bots[i]; + if(!o || (o == d)) continue; + raydamage(o, from, to, d, i); + }; + // End add by Rick + + if(d->bIsBot) raydamage(player1, from, to, d, -1); }; diff --git a/source/src/world.cpp b/source/src/world.cpp index d76519ed..0879fff1 100644 --- a/source/src/world.cpp +++ b/source/src/world.cpp @@ -37,7 +37,16 @@ void settag(int tag, int type) // set all cubes with "tag" to space, if }; void resettagareas() { settag(0, 0); }; // reset for editing or map saving -void settagareas() { settag(0, 1); loopv(ents) if(ents[i].type==CARROT) setspawn(i, true); }; // set for playing +// Modified by Rick +//void settagareas() { settag(0, 1); loopv(ents) if(ents[i].type==TRIGGER) setspawn(i, true); }; // set for playing + +void settagareas() // set for playing +{ + settag(0, 1); + loopv(ents) if(ents[i].type==CARROT) setspawn(i, true); + if(ishost()) BotManager.PickNextTrigger(); +}; +// End mod void trigger(int tag, int type, bool savegame) { diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 5f774b62..fe0fb661 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -205,11 +205,11 @@ void save_world(char *mname) conoutf("wrote map file %s", (int)cgzname); settagareas(); }; - -extern void preparectf(bool cleanonly = false); + +extern void preparectf(bool cleanonly = false); void load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas! -{ +{ preparectf(true); stopifrecording(); cleardlights();