diff --git a/CMakeLists.txt b/CMakeLists.txt index efb27d7..f172bdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ option(VRX_Q2PRO "Use map/gamemap differentiation (for q2pro)." TRUE) option(VRX_OLD_NOLAG "Use old NOLAG style. Not recommended." FALSE) option(VRX_OLD_VOTE "Use old vote system." FALSE) -set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED TRUE) # Source files diff --git a/sql/vrx-5.7.sql b/sql/vrx-5.7.sql index e8eb318..d1d52b6 100644 --- a/sql/vrx-5.7.sql +++ b/sql/vrx-5.7.sql @@ -2,72 +2,83 @@ create table if not exists abilities ( char_idx int not null, aindex int not null, - level int null, - max_level int null, - hard_max int null, - modifier int null, - disable int null, - general_skill int null, + level int not null, + max_level int not null, + hard_max int not null, + modifier int not null, + disable int not null, + general_skill int not null, primary key (char_idx, aindex) ); +create table if not exists prestige +( + char_idx int not null, + pindex int not null, + param int not null, + level int not null, + primary key (char_idx, pindex, param) +); + create table if not exists character_data ( char_idx int not null, - respawns int null, - health int null, - maxhealth int null, - armour int null, - maxarmour int null, - nerfme int null, - adminlevel int null, - bosslevel int null, + respawns int not null default 0, + health int not null default 100, + maxhealth int not null default 100, + armour int not null default 0, + maxarmour int not null default 0, + nerfme int not null default 0, + adminlevel int not null default 0, + bosslevel int not null default 0, + prestigelevel int not null default 0, + prestigepoints int not null default 0, primary key (char_idx) ); create table if not exists ctf_stats ( char_idx int not null, - flag_pickups int null, - flag_captures int null, - flag_returns int null, - flag_kills int null, - offense_kills int null, - defense_kills int null, - assists int null, + flag_pickups int not null default 0, + flag_captures int not null default 0, + flag_returns int not null default 0, + flag_kills int not null default 0, + offense_kills int not null default 0, + defense_kills int not null default 0, + assists int not null default 0, primary key (char_idx) ); create table if not exists game_stats ( char_idx int not null, - shots int null, - shots_hit int null, - frags int null, - fragged int null, - num_sprees int null, - max_streak int null, - spree_wars int null, - broken_sprees int null, - broken_spreewars int null, - suicides int null, - teleports int null, - num_2fers int null, + shots int not null default 0, + shots_hit int not null default 0, + frags int not null default 0, + fragged int not null default 0, + num_sprees int not null default 0, + max_streak int not null default 0, + spree_wars int not null default 0, + broken_sprees int not null default 0, + broken_spreewars int not null default 0, + suicides int not null default 0, + teleports int not null default 0, + num_2fers int not null default 0, primary key (char_idx) ); create table if not exists point_data ( char_idx int not null, - exp int null, - exptnl int null, - level int null, - classnum int null, - skillpoints int null, - credits int null, - weap_points int null, - resp_weapon int null, - tpoints int null, + exp int not null, + exptnl int not null, + level int not null default 0, + classnum int not null default 0, + skillpoints int not null default 0, + credits int not null default 0, + weap_points int not null default 0, + resp_weapon int not null default 0, + tpoints int not null default 0, primary key (char_idx) ); diff --git a/src/characters/io/v_characterio.c b/src/characters/io/v_characterio.c index b7e7545..8a27256 100644 --- a/src/characters/io/v_characterio.c +++ b/src/characters/io/v_characterio.c @@ -116,7 +116,8 @@ void vrx_setup_sqlite_io() { .handle_status = NULL, .character_exists = &vrx_sqlite_character_exists, .is_loading = &vrx_sqlite_isloading, - .set_owner = &cdb_set_owner + .set_owner = &cdb_set_owner, + .type = SAVEMETHOD_SQLITE }; cdb_start_connection(); @@ -170,7 +171,8 @@ void vrx_setup_mysql_io() { .load_player = &vrx_mysql_load_character, .is_loading = &vrx_mysql_isloading, .handle_status = &gds_handle_status, - .set_owner = &gds_queue_add_setowner + .set_owner = &gds_queue_add_setowner, + .type = SAVEMETHOD_MYSQL }; gds_connect(); diff --git a/src/characters/io/v_characterio.h b/src/characters/io/v_characterio.h index 30096a4..e8bfc73 100644 --- a/src/characters/io/v_characterio.h +++ b/src/characters/io/v_characterio.h @@ -13,6 +13,7 @@ typedef struct { qboolean (*is_loading) (edict_t* ent); // if it's already loading... don't bother calling load_player again. void (*handle_status)(edict_t* ent); // if not null, this is a multithreaded io system. void (*set_owner)(edict_t* ent, char* charname, char* mpw); + int type; } char_io_t; extern char_io_t vrx_char_io; diff --git a/src/characters/io/v_sqlite_unidb.c b/src/characters/io/v_sqlite_unidb.c index eacd128..93b595d 100644 --- a/src/characters/io/v_sqlite_unidb.c +++ b/src/characters/io/v_sqlite_unidb.c @@ -14,9 +14,9 @@ // ********************************* // Definitions // ********************************* -#define TOTAL_TABLES 14 +#define TOTAL_TABLES 15 #define TOTAL_INSERTONCE 5 -#define TOTAL_RESETTABLES 6 +#define TOTAL_RESETTABLES 7 void cdb_start_connection(); @@ -34,7 +34,8 @@ const char* VSFU_CREATEDBQUERY[TOTAL_TABLES] = "CREATE TABLE [userdata] ( [char_idx] INTEGER,[title] CHAR(24), [playername] CHAR(64), [password] CHAR(24), [email] CHAR(64), [owner] CHAR(24), [member_since] CHAR(30), [last_played] CHAR(30), [playtime_total] INTEGER,[playingtime] INTEGER)", "CREATE TABLE [weapon_meta] ([char_idx] INTEGER,[index] INTEGER, [disable] INTEGER)", "CREATE TABLE [weapon_mods] ([char_idx] INTEGER,[weapon_index] INTEGER, [modindex] INTEGER, [level] INTEGER, [soft_max] INTEGER, [hard_max] INTEGER)", - "CREATE TABLE [character_data] ([char_idx] INTEGER, [respawns] INTEGER, [health] INTEGER, [maxhealth] INTEGER, [armour] INTEGER, [maxarmour] INTEGER, [nerfme] INTEGER, [adminlevel] INTEGER, [bosslevel] INTEGER)", + "CREATE TABLE [character_data] ([char_idx] INTEGER, [respawns] INTEGER, [health] INTEGER, [maxhealth] INTEGER, [armour] INTEGER, [maxarmour] INTEGER, [nerfme] INTEGER, [adminlevel] INTEGER, [bosslevel] INTEGER, [prestigelevel] INTEGER, [prestigepoints] INTEGER)", + "create table [prestige](char_idx int not null, pindex int not null, param int not null, level int not null, primary key (char_idx, pindex, param))" "create table stash( char_idx int not null, lock_char_id int null, primary key (char_idx));", "create table stash_runes_meta( char_idx int not null, stash_index int not null, itemtype int null, itemlevel int null, quantity int null, untradeable int null, id char(16) null, name char(24) null, nummods int null, setcode int null, classnum int null, primary key (char_idx, stash_index));", "create table stash_runes_mods( char_idx int not null, stash_index int not null, rune_mod_index int not null, type int null, mod_index int null, value int null, [set] int null, primary key (char_idx, stash_index, rune_mod_index));" @@ -42,7 +43,7 @@ const char* VSFU_CREATEDBQUERY[TOTAL_TABLES] = // SAVING -const char* CreateCharacterData = "INSERT INTO character_data VALUES (%d,0,0,0,0,0,0,0,0)"; +const char* CreateCharacterData = "INSERT INTO character_data VALUES (%d,0,0,0,0,0,0,0,0,0,0)"; const char* CreateCtfStats = "INSERT INTO ctf_stats VALUES (%d,0,0,0,0,0,0,0)"; const char* CreateGameStats = "INSERT INTO game_stats VALUES (%d,0,0,0,0,0,0,0,0,0,0,0,0)"; const char* CreatePointData = "INSERT INTO point_data VALUES (%d,0,0,0,0,0,0,0,0,0)"; @@ -53,6 +54,7 @@ const char* VSFU_RESETTABLES[TOTAL_RESETTABLES] = { "DELETE FROM abilities WHERE char_idx=%d;", "DELETE FROM talents WHERE char_idx=%d;", + "DELETE FROM prestige WHERE char_idx=%d;", "DELETE FROM runes_meta WHERE char_idx=%d;", "DELETE FROM runes_mods WHERE char_idx=%d;", "DELETE FROM weapon_meta WHERE char_idx=%d;", @@ -80,7 +82,9 @@ const char* VSFU_INSERTRMOD = "INSERT INTO runes_mods VALUES (%d,%d,%d,%d,%d,%d) const char* VSFU_INSERTRMODEX = "INSERT INTO runes_mods VALUES (%d,%d,0,%d,%d,%d,%d);"; -const char* VSFU_UPDATECDATA = "UPDATE character_data SET respawns=%d, health=%d, maxhealth=%d, armour=%d, maxarmour=%d, nerfme=%d, adminlevel=%d, bosslevel=%d WHERE char_idx=%d;"; +const char* VSFU_UPDATECDATA = "UPDATE character_data SET respawns=%d, health=%d, maxhealth=%d, armour=%d, " + "maxarmour=%d, nerfme=%d, adminlevel=%d, bosslevel=%d, prestigelevel=%d, prestigepoints=%d" + " WHERE char_idx=%d;"; const char* VSFU_UPDATESTATS = "UPDATE game_stats SET shots=%d, shots_hit=%d, frags=%d, fragged=%d, num_sprees=%d, max_streak=%d, spree_wars=%d, broken_sprees=%d, broken_spreewars=%d, suicides=%d, teleports=%d, num_2fers=%d WHERE char_idx=%d;"; @@ -326,7 +330,10 @@ qboolean cdb_save_player(edict_t* player) MAX_ARMOR(player), player->myskills.nerfme, player->myskills.administrator, // flags - player->myskills.boss, id)); + player->myskills.boss, + player->myskills.prestige.total, + player->myskills.prestige.points, + id)); //***************************** @@ -437,6 +444,34 @@ qboolean cdb_save_player(edict_t* player) player->myskills.defense_kills, player->myskills.assists, id)); + // prestige + QUERY(va("INSERT INTO prestige(char_idx, pindex, param, level) VALUES (%d, %d, %d, %d);", + id, PRESTIGE_CREDITS, 0, player->myskills.prestige.creditLevel)); + + QUERY(va("INSERT INTO prestige(char_idx, pindex, param, level) VALUES (%d, %d, %d, %d);", + id, PRESTIGE_ABILITY_POINTS, 0, player->myskills.prestige.abilityPoints)); + + QUERY(va("INSERT INTO prestige(char_idx, pindex, param, level) VALUES (%d, %d, %d, %d);", + id, PRESTIGE_WEAPON_POINTS, 0, player->myskills.prestige.weaponPoints)); + + // softmax bump points - param is the ab index, level is the bump + for (i = 0; i < MAX_ABILITIES; i++) + { + if (player->myskills.prestige.softmaxBump[i] > 0) { + QUERY(va("INSERT INTO prestige(char_idx, pindex, param, level) VALUES (%d, %d, %d, %d);", + id, PRESTIGE_SOFTMAX_BUMP, i, player->myskills.prestige.softmaxBump[i])); + } + } + + // class skills - param is the ab index, level is always 0 + for (i = 0; i < MAX_ABILITIES; i++) + { + if (player->myskills.prestige.classSkill[i / 32] & (1 << (i % 32))) + { + QUERY(va("INSERT INTO prestige(char_idx, pindex, param, level) VALUES (%d, %d, %d, %d);", + id, PRESTIGE_CLASS_SKILL, i, 0)); + } + } } // end saving @@ -749,6 +784,10 @@ qboolean cdb_load_player(edict_t* player) //boss flag player->myskills.boss = sqlite3_column_int(statement, 8); + // prestige + player->myskills.prestige.total = sqlite3_column_int(statement, 9); + player->myskills.prestige.points = sqlite3_column_int(statement, 10); + //***************************** //stats //***************************** @@ -804,10 +843,42 @@ qboolean cdb_load_player(edict_t* player) sqlite3_finalize(statement); + // prestige + format = va("SELECT * FROM prestige WHERE char_idx=%d", id); + + r = sqlite3_prepare_v2(db, format, strlen(format), &statement, NULL); + while(sqlite3_step(statement) == SQLITE_ROW) { + int pindex = sqlite3_column_int(statement, 1); + int param = sqlite3_column_int(statement, 2); + int level = sqlite3_column_int(statement, 3); + + switch (pindex) + { + case PRESTIGE_CREDITS: + player->myskills.prestige.creditLevel = level; + break; + case PRESTIGE_ABILITY_POINTS: + player->myskills.prestige.abilityPoints = level; + break; + case PRESTIGE_WEAPON_POINTS: + player->myskills.prestige.weaponPoints = level; + break; + case PRESTIGE_SOFTMAX_BUMP: + player->myskills.prestige.softmaxBump[param] = level; + break; + case PRESTIGE_CLASS_SKILL: + player->myskills.prestige.classSkill[param / 32] |= (1 << (param % 32)); + break; + default: break; + } + } + + + //Apply runes - V_ResetAllStats(player); + vrx_runes_unapply(player); for (i = 0; i < 4; ++i) - V_ApplyRune(player, &player->myskills.items[i]); + vrx_runes_apply(player, &player->myskills.items[i]); //Apply health if (player->myskills.current_health > MAX_HEALTH(player)) diff --git a/src/characters/player_points.c b/src/characters/player_points.c index 20dbba8..f46b44c 100644 --- a/src/characters/player_points.c +++ b/src/characters/player_points.c @@ -208,15 +208,14 @@ int vrx_get_credits(const edict_t *ent, float level_diff, int bonus, qboolean cl else add_credits = level_diff * (vrx_creditmult->value * vrx_pvmcreditmult->value * (CREDITS_OTHER_BASE)); - // vrxchile v1.5 no credit cap - //if (add_credits > 250) - // add_credits = 250; - add_credits += bonus; + add_credits = (int) (add_credits * PRESTIGE_CREDIT_BUFF_MULTIPLIER); return add_credits; } void vrx_add_credits(edict_t *ent, int add_credits) { + if (add_credits < 0) + return; //FIXME: remove this after allocating more space if (ent->myskills.credits + add_credits > MAX_CREDITS) diff --git a/src/characters/prestige.c b/src/characters/prestige.c new file mode 100644 index 0000000..e21c9a4 --- /dev/null +++ b/src/characters/prestige.c @@ -0,0 +1,266 @@ +// +// Created by dahum on 04-09-2024. +// + +#include "g_local.h" +#include "prestige.h" + +struct prestigedef_t { + int id; + const char *name; +}; + +struct prestigedef_t prestige[] = { + {PRESTIGE_CLASS_SKILL, "New Class Skill"}, + {PRESTIGE_CREDITS, "Credit Earning Buff"}, + {PRESTIGE_SOFTMAX_BUMP, "Softmax Bump"}, + {PRESTIGE_ABILITY_POINTS, "Additional Ability Point"}, + {PRESTIGE_WEAPON_POINTS, "Additional Weapon Points"}, +}; + +uint32_t PRESTIGE_THRESHOLD; + +void vrx_prestige_global_init() { + PRESTIGE_THRESHOLD = 0; + for (int i = 0; i < 15; i++) { + PRESTIGE_THRESHOLD += (int) vrx_get_points_tnl(i); + } +} + +void vrx_prestige_init(edict_t *pUser) { + memset(&pUser->myskills.prestige, 0, sizeof(prestige)); +} + +uint32_t vrx_prestige_get_upgrade_points(uint32_t exp) { + return exp / PRESTIGE_THRESHOLD; +} + +qboolean vrx_prestige_filter_class_skill(const abilitydef_t *pAbility, void *user) { + edict_t *pUser = user; + + // general skills cannot become class skills + if (pAbility->general) + return false; + + // non-scaleable abilities cannot become class skills + if (pAbility->softmax != DEFAULT_SOFTMAX) + return false; + + // already a class skill + if (!pUser->myskills.abilities[pAbility->index].general_skill) + return false; + + return true; +} + +qboolean vrx_prestige_filter_softmax_bump(const abilitydef_t *pAbility, void *user) { + edict_t *pUser = user; + + if (pAbility->softmax != DEFAULT_SOFTMAX) + return false; + + // Don't show the skill if it has reached the maximum softmax bump. + if (pUser->myskills.prestige.softmaxBump[pAbility->index] >= MAX_SOFTMAX_BUMP) + return false; + + return true; +} + +void vrx_prestige_handle_class_skill(edict_t *ent, int option) { + if (option >= 10000) { + vrx_ability_open_select_menu( + ent, + "Select a new class skill", + vrx_prestige_filter_class_skill, + option - 10000, + ent, + vrx_prestige_handle_class_skill + ); + return; + } + + if (option >= 1000) + option -= 1000; + + if (option < 0 || option >= MAX_ABILITIES) { + menu_close(ent, false); + return; + } + + ent->myskills.prestige.classSkill[option / 32] |= 1 << (option % 32); + vrx_add_ability(ent, option); + ent->myskills.prestige.points -= 1; + + gi.cprintf(ent, PRINT_HIGH, "You have selected %s as a class skill.\n", GetAbilityString(option)); +} + +void vrx_prestige_handle_softmax_bump(edict_t *ent, int option) { + if (option >= 10000) { + vrx_ability_open_select_menu( + ent, + "Increase softmax", + vrx_prestige_filter_softmax_bump, + option - 10000, + ent, + vrx_prestige_handle_softmax_bump + ); + + return; + } + + if (option >= 1000) + option -= 1000; + + if (option < 0 || option >= MAX_ABILITIES) { + menu_close(ent, false); + return; + } + + if (ent->myskills.prestige.softmaxBump[option] >= MAX_SOFTMAX_BUMP) { + gi.cprintf(ent, PRINT_HIGH, "You have reached the maximum softmax bump for %s.\n", GetAbilityString(option)); + return; + } + + ent->myskills.prestige.softmaxBump[option] += 1; + ent->myskills.prestige.points -= 1; + ent->myskills.abilities[option].max_level += 1; + ent->myskills.abilities[option].hard_max = vrx_get_hard_max(option, 0, vrx_get_ability_class(option)); + + gi.cprintf(ent, PRINT_HIGH, "You have increased the softmax of %s by 1.\n", GetAbilityString(option)); +} + +void vrx_prestige_ascend(edict_t *self) { + // check if the player has enough experience to ascend + if (self->myskills.experience < PRESTIGE_THRESHOLD) + return; + + // check how many levels the player can ascend + int upgradePoints = vrx_prestige_get_upgrade_points(self->myskills.experience); + + // add the upgrade points to the player's prestige total and current + self->myskills.prestige.total += upgradePoints; + self->myskills.prestige.points += upgradePoints; + + // reset them to start level + vrx_change_class(self->myskills.player_name, self->myskills.class_num, 3); +} + +void vrx_prestige_menu_handler(edict_t *self, int option) { + if (option >= PRESTIGE_CREDITS && option <= PRESTIGE_SOFTMAX_BUMP) { + if (self->myskills.prestige.points == 0) { + safe_cprintf(self, PRINT_HIGH, "You do not have enough points to purchase this upgrade.\n"); + return; + } + } + + switch (option) { + case PRESTIGE_CLASS_SKILL: + vrx_ability_open_select_menu( + self, + "Select a new class skill", + vrx_prestige_filter_class_skill, + 0, + self, + vrx_prestige_handle_class_skill + ); + break; + case PRESTIGE_CREDITS: + self->myskills.prestige.creditLevel += 1; + self->myskills.prestige.points -= 1; + break; + case PRESTIGE_SOFTMAX_BUMP: + vrx_ability_open_select_menu( + self, + "Increase softmax", + vrx_prestige_filter_softmax_bump, + 0, + self, + vrx_prestige_handle_softmax_bump + ); + break; + case PRESTIGE_ABILITY_POINTS: + self->myskills.speciality_points += 1; + self->myskills.prestige.points -= 1; + break; + case PRESTIGE_WEAPON_POINTS: + self->myskills.weapon_points += 1; + self->myskills.prestige.points -= 1; + break; + case PRESTIGE_ASCEND: + vrx_prestige_ascend(self); + break; + default: + menu_close(self, false); + } +} + +void vrx_prestige_open_menu(edict_t *self) { + if (!menu_can_show(self)) + return; + + menu_clear(self); + menu_add_line(self, va("Prestige %d", self->myskills.prestige.total), MENU_GREEN_CENTERED); + menu_add_line(self, va("You have %d points available", self->myskills.prestige.points), MENU_WHITE_CENTERED); + + int rem = self->myskills.experience % PRESTIGE_THRESHOLD; + menu_add_line(self, va("%d xp until next point", PRESTIGE_THRESHOLD - rem), MENU_WHITE_CENTERED); + + if (self->myskills.prestige.creditLevel) { + menu_add_line(self, " ", 0); + menu_add_line(self, va("Credit Earning Buff: %d%%", + self->myskills.prestige.creditLevel * PRESTIGE_CREDIT_BUFF_PERCENT), 0); + } + + menu_add_line(self, " ", 0); + + for (int i = 0; i < sizeof(prestige) / sizeof(prestige[0]); i++) { + menu_add_line(self, prestige[i].name, prestige[i].id); + } + + int upgradePoints = self->myskills.experience / PRESTIGE_THRESHOLD; + + menu_add_line(self, " ", 0); + if (upgradePoints) { + menu_add_line(self, va("Ascend (%d)", upgradePoints), PRESTIGE_ASCEND); + } + + menu_add_line(self, "Exit", 666); + + self->client->menustorage.currentline += self->client->menustorage.num_of_lines; + menu_set_handler(self, vrx_prestige_menu_handler); + menu_show(self); +} + +void vrx_prestige_reapply_abilities(edict_t* self) { + struct prestigelist_s *pre = &self->myskills.prestige; + for (uint32_t abIndex = 0; abIndex < MAX_ABILITIES; abIndex++) { + uint32_t arrIdx = abIndex / 32; + uint32_t mask = 1 << (abIndex % 32); + qboolean isPrestigeAbility = (pre->classSkill[arrIdx] & mask) != 0; + + if (isPrestigeAbility) { + vrx_add_ability(self, abIndex); + } + + self->myskills.abilities[abIndex].max_level += pre->softmaxBump[arrIdx]; + } +} + +// must be applied after abilities +void vrx_prestige_reapply_all(edict_t *self) { + struct prestigelist_s *pre = &self->myskills.prestige; + self->myskills.speciality_points += pre->abilityPoints; + self->myskills.weapon_points += pre->weaponPoints; + + vrx_prestige_reapply_abilities(self); +} + +qboolean vrx_prestige_has_class_skills(edict_t *self) { + int size = sizeof(self->myskills.prestige.classSkill) / sizeof(self->myskills.prestige.classSkill[0]); + for (uint32_t i = 0; i < size; i++) { + if (self->myskills.prestige.classSkill[i]) + return true; + } + + return false; +} diff --git a/src/characters/prestige.h b/src/characters/prestige.h new file mode 100644 index 0000000..d18e84d --- /dev/null +++ b/src/characters/prestige.h @@ -0,0 +1,40 @@ +#ifndef PRESTIGE_H +#define PRESTIGE_H + +#include +#include + +#define MAX_SOFTMAX_BUMP 10 + +enum PrestigeType { + PRESTIGE_CREDITS = 1, + PRESTIGE_ABILITY_POINTS = 2, + PRESTIGE_CLASS_SKILL = 3, + PRESTIGE_WEAPON_POINTS = 4, + PRESTIGE_SOFTMAX_BUMP = 5, + PRESTIGE_ASCEND = 999 +}; + +typedef struct prestigelist_s { + uint32_t points; + uint32_t total; + uint8_t softmaxBump[MAX_ABILITIES]; + uint32_t creditLevel; + uint32_t abilityPoints; + uint32_t weaponPoints; + + abilitybitmap_t classSkill; +} prestigelist_t; + +#define PRESTIGE_CREDIT_BUFF_PERCENT 30 +#define PRESTIGE_CREDIT_BUFF_MULTIPLIER (1.0 + (PRESTIGE_CREDIT_BUFF_PERCENT / 100.0)) + +void vrx_prestige_global_init(); +void vrx_prestige_init(edict_t *pUser); +void vrx_prestige_reapply_all(edict_t *self); +void vrx_prestige_reapply_abilities(edict_t *self); +void vrx_prestige_open_menu(edict_t *self); +uint32_t vrx_prestige_get_upgrade_points(uint32_t exp); +qboolean vrx_prestige_has_class_skills(edict_t *self); + +#endif //PRESTIGE_H diff --git a/src/characters/v_abilitylist.c b/src/characters/v_abilitylist.c index 4eba4b6..00fdce4 100644 --- a/src/characters/v_abilitylist.c +++ b/src/characters/v_abilitylist.c @@ -309,6 +309,24 @@ int vrx_get_hard_max(int index, qboolean general, int class) { return abilities_by_index[index]->softmax; } +void vrx_add_ability(edict_t* ent, int index) { + if (index < 0 || index >= MAX_ABILITIES) + return; + + int class = vrx_get_ability_class(index); + if (class == CLASS_NULL) + return; + + const abilitydef_t *ability = abilities_by_index[index]; + int hardmax = vrx_get_hard_max(index, 0, class); + vrx_enable_ability( + ent, index, + ability->start, + ability->softmax + ent->myskills.prestige.softmaxBump[index], + 0 + ); +} + void vrx_enable_ability(edict_t *ent, int index, int level, int max_level, int general) { ent->myskills.abilities[index].disable = false; @@ -437,7 +455,8 @@ void vrx_init_ability_list() { // iterate through class' ability list while (first->index != -1) { if (abilities_by_index[first->index]) { - // get the one with the highest softmax + // get the one with the highest softmax, since we want to track the abilities + // as they exist on their class. if (abilities_by_index[first->index]->softmax < first->softmax) { abilities_by_index[first->index] = first; } @@ -499,7 +518,7 @@ void vrx_update_free_abilities(edict_t *ent) { //FIXME: this doesn't work with 2-4 point abilities -void V_UpdatePlayerAbilities(edict_t *ent) { +void vrx_normalize_abilities(edict_t *ent) { int i, refunded = 0; upgrade_t old_abilities[MAX_ABILITIES]; qboolean points_refunded = false; @@ -514,6 +533,7 @@ void V_UpdatePlayerAbilities(edict_t *ent) { memset(ent->myskills.abilities, 0, sizeof(upgrade_t) * MAX_ABILITIES); vrx_assign_abilities(ent); + vrx_prestige_reapply_abilities(ent); for (i = 0; i < MAX_ABILITIES; ++i) { // if this ability was previously enabled, restore the upgrade level @@ -540,9 +560,9 @@ void V_UpdatePlayerAbilities(edict_t *ent) { } // re-apply equipment - V_ResetAllStats(ent); + vrx_runes_unapply(ent); for (i = 0; i < 3; ++i) - V_ApplyRune(ent, &ent->myskills.items[i]); + vrx_runes_apply(ent, &ent->myskills.items[i]); /*safe_cprintf(ent, PRINT_HIGH, "Your abilities have been updated.\n"); */ @@ -551,4 +571,87 @@ void V_UpdatePlayerAbilities(edict_t *ent) { ent->myskills.speciality_points += old_abilities[i].level; safe_cprintf(ent, PRINT_HIGH, "%d ability points have been refunded.\n", refunded); } +} + +int vrx_get_ability_class(int abil) { + if (abil < 0 || abil >= MAX_ABILITIES) + return 0; + + for (int i = 1; i < CLASS_MAX; i++) { + if (ability_class[abil][i]) + return i; + } + + return 0; +} + +void vrx_ability_open_select_menu( + edict_t* self, + const char* title, + qboolean (*filter)(const abilitydef_t*, void*), + int page, + void* userdata, + void (*handler)(edict_t *ent,int option)) { + if (!menu_can_show(self)) + return; + + abilitybitmap_t skills; + + memset(skills, 0, sizeof(abilitybitmap_t)); + + for (int i = 0; i < MAX_ABILITIES; i++) { + const abilitydef_t *ability = vrx_get_ability_by_index(i); + if (ability && filter(ability, userdata)) { + skills[i / 32] |= 1 << (i % 32); + } + } + + int ab_count = 0; + int start_index = -1; + // find the first ability to display + for (int i = 0; i < MAX_ABILITIES; i++) { + if (skills[i / 32] & (1 << (i % 32))) { + ab_count++; + } + + if (ab_count > 10 * page && start_index == -1) { + start_index = i; + } + } + + menu_clear(self); + menu_add_line(self, "Ability Selection", MENU_GREEN_CENTERED); + if (title) + menu_add_line(self, title, MENU_WHITE_CENTERED); + + menu_add_line(self, " ", 0); + + int ab_count_on_page = 0; + int limit = 10; + for (int i = 0; i < limit && ab_count_on_page <= 10 && i < MAX_ABILITIES; i++) { + int index = start_index + i; + // if the ability is enabled by the filter + if (skills[index / 32] & (1 << (index % 32))) { + const abilitydef_t *ability = vrx_get_ability_by_index(index); + if (ability) { + menu_add_line(self, va("%s", GetAbilityString(index)), index + 1000); + ab_count_on_page++; + } + } else { + limit++; + } + } + + menu_add_line(self, " ", 0); + + if (page > 0) + menu_add_line(self, "Previous Page", 10000 + page - 1); + if (ab_count > 10 * (page + 1)) + menu_add_line(self, "Next Page", 10000 + page + 1); + + menu_add_line(self, "Exit", 666); + + self->client->menustorage.currentline += self->client->menustorage.num_of_lines - 1; + menu_set_handler(self, handler); + menu_show(self); } \ No newline at end of file diff --git a/src/characters/v_utils.c b/src/characters/v_utils.c index ba2be0c..7f8a1ba 100644 --- a/src/characters/v_utils.c +++ b/src/characters/v_utils.c @@ -4,6 +4,7 @@ #include "class_limits.h" qboolean IsAllowedPregameSkills(); + //************************************************************************************************ // Indexing functions //************************************************************************************************ @@ -162,7 +163,7 @@ char *GetArmoryItemString(int purchase_number) { return "Reset Abilities/Weapons"; #ifndef REMOVE_RESPAWNS case 30: - return "Respawns"; + return "Respawns"; #endif default: return ""; @@ -353,17 +354,17 @@ char *GetModString(int weapon_number, int mod_number) { //************************************************************************************************ char *classnames[] = { - "Soldier", - "Poltergeist", - "Vampire", - "Mage", - "Engineer", - "Knight", - "Cleric", - "Necromancer", - "Shaman", - "Alien", - "Weapon Master" + "Soldier", + "Poltergeist", + "Vampire", + "Mage", + "Engineer", + "Knight", + "Cleric", + "Necromancer", + "Shaman", + "Alien", + "Weapon Master" }; char *vrx_get_class_string(int class_num) { @@ -392,7 +393,7 @@ char *GetTalentString(int talent_ID) { return "Bombardier"; case TALENT_MONSTER_MASTERY: return "Mastery"; - //Poltergeist Talents + //Poltergeist Talents case TALENT_MORPHING: return "Morphing"; case TALENT_MORE_AMMO: @@ -403,7 +404,7 @@ char *GetTalentString(int talent_ID) { return "Retaliation"; case TALENT_PACK_ANIMAL: return "Pack Animal"; - //Vampire Talents + //Vampire Talents case TALENT_IMP_CLOAK: return "Imp. Cloak"; case TALENT_ARMOR_VAMP: @@ -416,7 +417,7 @@ char *GetTalentString(int talent_ID) { return "Cannibalism"; case TALENT_FATAL_WOUND: return "Fatal Wound"; - //Mage Talents + //Mage Talents case TALENT_ICE_BOLT: return "Ice Bolt"; case TALENT_MEDITATION: @@ -429,8 +430,8 @@ char *GetTalentString(int talent_ID) { return "Mana Shield"; case TALENT_OVERLOAD: return "Overload"; - //Engineer Talents - //case TALENT_DEFENSIVE_CRATE: return "Defensive Crate"; + //Engineer Talents + //case TALENT_DEFENSIVE_CRATE: return "Defensive Crate"; case TALENT_LASER_PLATFORM: return "Laser Platform"; case TALENT_ALARM: @@ -443,7 +444,7 @@ char *GetTalentString(int talent_ID) { return "Precision Tune"; case TALENT_STORAGE_UPGRADE: return "Storage Upgrade"; - //Knight Talents + //Knight Talents case TALENT_REPEL: return "Repel"; case TALENT_MAG_BOOTS: @@ -454,7 +455,7 @@ char *GetTalentString(int talent_ID) { return "Mobility"; case TALENT_DURABILITY: return "Durability"; - //Cleric Talents + //Cleric Talents case TALENT_BALANCESPIRIT: return "Balance Spirit"; case TALENT_HOLY_GROUND: @@ -465,7 +466,7 @@ char *GetTalentString(int talent_ID) { return "Purge"; case TALENT_BOOMERANG: return "Boomerang"; - //Weapon Master Talents + //Weapon Master Talents case TALENT_BASIC_AMMO_REGEN: return "Ammo Regen"; case TALENT_COMBAT_EXP: @@ -474,7 +475,7 @@ char *GetTalentString(int talent_ID) { return "Tactics"; case TALENT_SIDEARMS: return "Sidearms"; - //Necromancer Talents + //Necromancer Talents case TALENT_EVIL_CURSE: return "Evil Curse"; case TALENT_CHEAPER_CURSES: @@ -487,7 +488,7 @@ char *GetTalentString(int talent_ID) { return "Dim Vision"; case TALENT_FLIGHT: return "Flight"; - //Shaman Talents + //Shaman Talents case TALENT_ICE: return "Ice"; case TALENT_WIND: @@ -502,7 +503,7 @@ char *GetTalentString(int talent_ID) { return "Volcanic"; case TALENT_TOTEM: return "Totemic Focus"; - //Alien Talents + //Alien Talents case TALENT_PHANTOM_OBSTACLE: return "Hidden Obstacle"; case TALENT_SUPER_HEALER: @@ -515,8 +516,8 @@ char *GetTalentString(int talent_ID) { return "Swarming"; case TALENT_EXPLODING_BODIES: return "Exploding Body"; - //case TALENT_FASTMOVE: return "Run!"; - // Kamikaze talents + //case TALENT_FASTMOVE: return "Run!"; + // Kamikaze talents case TALENT_MARTYR: return "Martyr"; case TALENT_INSTANTPROXYS: @@ -772,113 +773,113 @@ char *GetAbilityString(int ability_number) { typedef const char *class_rune_string_t[7]; class_rune_string_t soldier_rune_val = { - "Newbie's", - "Greenhorn's", - "Sergent's", - "Soldier's", - "Warrior's", - "Veteran's", - "Master's" + "Newbie's", + "Greenhorn's", + "Sergent's", + "Soldier's", + "Warrior's", + "Veteran's", + "Master's" }; class_rune_string_t mage_rune_val = { - "Apprentice's", - "Illusionist's", - "Sage's", - "Mage's", - "Wizard's", - "Sorcerer's", - "Archimage's", + "Apprentice's", + "Illusionist's", + "Sage's", + "Mage's", + "Wizard's", + "Sorcerer's", + "Archimage's", }; class_rune_string_t necromancer_rune_val = { - "Exorcist's", - "Theurgist's", - "Shaman's", - "Necromancer's", - "Warlock's", - "Demi-Lich's", - "Lich's" + "Exorcist's", + "Theurgist's", + "Shaman's", + "Necromancer's", + "Warlock's", + "Demi-Lich's", + "Lich's" }; class_rune_string_t engineer_rune_val = { - "Student's", - "Assistant's", - "Technician's", - "Mechanic's", - "Scientist's", - "Physicist's", - "Engineer's" + "Student's", + "Assistant's", + "Technician's", + "Mechanic's", + "Scientist's", + "Physicist's", + "Engineer's" }; class_rune_string_t shaman_rune_val = { - "Dabbler's", - "Divinist's", - "Mystic's", - "Animist's", - "Gnostic's", - "Spiritualist's", - "Spirits'" + "Dabbler's", + "Divinist's", + "Mystic's", + "Animist's", + "Gnostic's", + "Spiritualist's", + "Spirits'" }; class_rune_string_t vampire_rune_val = { - "Ghoul's", - "Geist's", - "Wraith's", - "Vampire's", - "Revenant's", - "Nosferatu's", - "Dracula's" + "Ghoul's", + "Geist's", + "Wraith's", + "Vampire's", + "Revenant's", + "Nosferatu's", + "Dracula's" }; class_rune_string_t alien_rune_val = { - "Egg's", - "Hatchling's", - "Worm's", - "Pupa's", - "Insect's", - "Guard's", - "Royal's" + "Egg's", + "Hatchling's", + "Worm's", + "Pupa's", + "Insect's", + "Guard's", + "Royal's" }; class_rune_string_t poltergeist_rune_val = { - "Spook's", - "Spirit's", - "Phantom's", - "Poltergeist's", - "Apparition's", - "Ghost's", - "Monster's" + "Spook's", + "Spirit's", + "Phantom's", + "Poltergeist's", + "Apparition's", + "Ghost's", + "Monster's" }; class_rune_string_t knight_rune_val = { - "Commoner's", - "Squire's", - "Guard's", - "Knight's", - "Baron's", - "Lord's", - "King's" + "Commoner's", + "Squire's", + "Guard's", + "Knight's", + "Baron's", + "Lord's", + "King's" }; class_rune_string_t cleric_rune_val = { - "Follower's", - "Acolyte's", - "Preacher's", - "Cleric's", - "Pastor's", - "Bishop's", - "Pope's" + "Follower's", + "Acolyte's", + "Preacher's", + "Cleric's", + "Pastor's", + "Bishop's", + "Pope's" }; class_rune_string_t weaponmaster_rune_val = { - "Amateur's", - "Neophyte's", - "Novice's", - "Weapon Master's", - "Guru's", - "Expert's", - "Elite's" + "Amateur's", + "Neophyte's", + "Novice's", + "Weapon Master's", + "Guru's", + "Expert's", + "Elite's" }; const char *GetRuneValString(item_t *rune) { @@ -944,8 +945,8 @@ const char *GetRuneValString(item_t *rune) { return ""; } break; - default: - return "Strange"; + default: + return "Strange"; } } } @@ -1051,7 +1052,7 @@ qboolean V_CanUseAbilities(edict_t *ent, int ability_index, int ability_cost, qb return false; if (level.time < pregame_time->value && !trading->value) { - if (!IsAllowedPregameSkills()) // allow use of abilities in pvm modes. + if (!IsAllowedPregameSkills()) // allow use of abilities in pvm modes. { if (print_msg) safe_cprintf(ent, PRINT_HIGH, "You can't use abilities during pre-game.\n"); @@ -1171,25 +1172,25 @@ qboolean V_GiveAmmoClip(edict_t *ent, float qty, int ammotype) { //Returns an ammo type based on the player's respawn weapon. int V_GetRespawnAmmoType(edict_t *ent) { switch (ent->myskills.respawn_weapon) { - case 2: //sg - case 3: //ssg - case 12: //20mm + case 2: //sg + case 3: //ssg + case 12: //20mm return AMMO_SHELLS; - case 4: //mg - case 5: //cg + case 4: //mg + case 5: //cg return AMMO_BULLETS; - case 6: //gl - case 11: //hg + case 6: //gl + case 11: //hg return AMMO_GRENADES; - case 7: //rl + case 7: //rl return AMMO_ROCKETS; - case 9: //rg + case 9: //rg return AMMO_SLUGS; - case 8: //hb - case 10: //bfg + case 8: //hb + case 10: //bfg return AMMO_CELLS; - default: //blaster/sword - return 0; //nothing + default: //blaster/sword + return 0; //nothing } } @@ -1214,7 +1215,7 @@ int GetClientNumber(edict_t *ent) if (strlen(temp->myskills.player_name) < 1) continue; - if (Q_stricmp(ent->myskills.player_name, temp->myskills.player_name) == 0) //same name + if (Q_stricmp(ent->myskills.player_name, temp->myskills.player_name) == 0) //same name return i + 1; } return 0; @@ -1383,7 +1384,7 @@ int V_tFileCountLines(FILE *fptr, long size) { } while (++i < size); rewind(fptr); - return count - 1; //Last line is always empty + return count - 1; //Last line is always empty } //************************************************************************************************ @@ -1408,6 +1409,7 @@ void V_tFileGotoLine(FILE *fptr, int linenumber, long size) { #define CHANGECLASS_MSG_CHANGE 1 #define CHANGECLASS_MSG_RESET 2 +#define CHANGECLASS_MSG_ASCEND 3 void vrx_change_class(char *playername, int newclass, int msgtype) { int i; @@ -1436,11 +1438,12 @@ void vrx_change_class(char *playername, int newclass, int msgtype) { vrx_assign_abilities(player); vrx_reset_weapon_maximums(player); vrx_set_talents(player); + vrx_prestige_reapply_all(player); //Re-apply equipment - V_ResetAllStats(player); + vrx_runes_unapply(player); for (i = 0; i < 3; ++i) - V_ApplyRune(player, &player->myskills.items[i]); + vrx_runes_apply(player, &player->myskills.items[i]); if (msgtype == CHANGECLASS_MSG_CHANGE) { //Notify everyone @@ -1452,10 +1455,24 @@ void vrx_change_class(char *playername, int newclass, int msgtype) { safe_cprintf(player, PRINT_HIGH, "Your ability and weapon upgrades have been reset!\n"); vrx_write_to_logfile(player, "Character data was reset.\n"); gi.dprintf("%s's character data was reset.\n", playername); + } else if (msgtype == CHANGECLASS_MSG_ASCEND) { + char* msg = HiPrint(va("*** %s has ascended to level %d! ***", playername, player->myskills.prestige.total)); + safe_bprintf(PRINT_HIGH, "%s\n", msg); + vrx_write_to_logfile(player, va("Ascended to level %d.\n", player->myskills.prestige.total)); + gi.dprintf("%s has ascended to level %d!\n", playername, player->myskills.prestige.total); + safe_centerprintf(player, "You have ascended to level %d.", player->myskills.prestige.total); + gi.sound(player, CHAN_AUTO, gi.soundindex("misc/keyuse.wav"), 1, ATTN_NORM, 0); + V_Free(msg); } player->myskills.level = 0; player->myskills.next_level = vrx_get_points_tnl(0); + player->myskills.experience = 0; + + for (int j = 1; j <= start_level->value; ++j) { + player->myskills.experience += vrx_get_points_tnl(j - 1); + } + player->myskills.speciality_points = 0; player->myskills.weapon_points = 0; vrx_check_for_levelup(player, false); @@ -1550,7 +1567,7 @@ void V_ModifyMorphedHealth(edict_t *ent, int type, qboolean morph) { if (mult > 1.5) mult = 1.5; break; - // nothing to modify + // nothing to modify default: return; } @@ -1562,7 +1579,7 @@ void V_ModifyMorphedHealth(edict_t *ent, int type, qboolean morph) { ent->max_health = floattoint(ent->max_health * mult); //gi.dprintf("after = %d/%d\n", ent->health, ent->max_health); } - // remove multiplier + // remove multiplier else { ent->health = floattoint(ent->health / mult); ent->max_health = MAX_HEALTH(ent); @@ -1570,48 +1587,41 @@ void V_ModifyMorphedHealth(edict_t *ent, int type, qboolean morph) { } // attempts to push (move) away from nearby wall(s) by dist -void V_PushBackWalls(edict_t* self, float dist) -{ - vec3_t start, end, v = { 0,0,0 }; - trace_t tr; - qboolean move = false; +void V_PushBackWalls(edict_t *self, float dist) { + vec3_t start, end, v = {0, 0, 0}; + trace_t tr; + qboolean move = false; VectorCopy(self->s.origin, start); // gather plane normal vector(s) - for (int i = 0; i <= 3; i++) - { + for (int i = 0; i <= 3; i++) { VectorCopy(start, end); // trace in a circle around us to find nearby walls - switch (i) - { - case 0: - end[0] += dist; - break; - case 1: - end[1] += dist; - break; - case 2: - end[0] -= dist; - break; - case 3: - end[1] -= dist; - break; + switch (i) { + case 0: + end[0] += dist; + break; + case 1: + end[1] += dist; + break; + case 2: + end[0] -= dist; + break; + case 3: + end[1] -= dist; + break; } tr = gi.trace(start, self->mins, self->maxs, end, self, MASK_SOLID); // did we hit something? - if (tr.fraction < 1) - { + if (tr.fraction < 1) { move = true; - if (VectorEmpty(v)) - { + if (VectorEmpty(v)) { // first intersecting plane normal--this is our escape vector from one wall VectorCopy(tr.plane.normal, v); - } - else - { + } else { // second intersecting plane normal--this is another wall roughly perpendicular to the first // now add the two plane normals together to get our final escape vector VectorAdd(tr.plane.normal, v, v); @@ -1620,15 +1630,13 @@ void V_PushBackWalls(edict_t* self, float dist) } } // we are adjacent to one or more walls, so try to push/move away - if (move) - { + if (move) { // ending position VectorMA(start, dist, v, end); tr = gi.trace(start, self->mins, self->maxs, end, self, MASK_SOLID); // nothing detected - if (!(tr.contents & MASK_SOLID) && tr.fraction == 1) - { + if (!(tr.contents & MASK_SOLID) && tr.fraction == 1) { // try to move target 'e' to new position VectorCopy(end, self->s.origin); gi.linkentity(self); @@ -1637,16 +1645,14 @@ void V_PushBackWalls(edict_t* self, float dist) } // attempts to push/knock-away nearby entities by dist -void V_PushBackEnts(edict_t* self, float dist) -{ - vec3_t start, end, v; - trace_t tr; - edict_t* e = NULL; +void V_PushBackEnts(edict_t *self, float dist) { + vec3_t start, end, v; + trace_t tr; + edict_t *e = NULL; //gi.dprintf("V_PushBackEnts()\n"); - while ((e = findradius(e, self->s.origin, dist)) != NULL) - { + while ((e = findradius(e, self->s.origin, dist)) != NULL) { if (!G_EntExists(e)) continue; // ignore non-moving entities (e.g. forcewall) @@ -1680,14 +1686,11 @@ void V_PushBackEnts(edict_t* self, float dist) tr = gi.trace(start, e->mins, e->maxs, end, e, MASK_SOLID); // nothing detected - if (!(tr.contents & MASK_SOLID) && tr.fraction == 1) - { + if (!(tr.contents & MASK_SOLID) && tr.fraction == 1) { // try to move target 'e' to new position VectorCopy(end, e->s.origin); gi.linkentity(e); - } - else - { + } else { //gi.dprintf("collided with solid\n"); } } @@ -1733,7 +1736,7 @@ void V_RestoreMorphed(edict_t *ent, int refund) { // az: restore v_flags we've set ent->v_flags &= ~SFLG_NO_BOB; - ent->client->lock_frames = 0;//4.2 reset smart-rocket lock-on counter + ent->client->lock_frames = 0; //4.2 reset smart-rocket lock-on counter } void mutant_checkattack(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf); @@ -1786,7 +1789,7 @@ char *V_GetMonsterKind(int mtype) { case P_TANK: return "tank"; case M_SUPERTANK: - return "supertank"; + return "supertank"; case M_SHAMBLER: return "shambler"; case M_JORG: @@ -2120,7 +2123,7 @@ void V_TouchSolids(edict_t *ent) { // list removed before we get to it (killtriggered) for (i = 0; i < num; i++) { hit = touch[i]; - if (!hit->inuse || !hit->touch/* || !hit->takedamage */|| hit == ent) + if (!hit->inuse || !hit->touch/* || !hit->takedamage */ || hit == ent) continue; //gi.dprintf("V_TouchSolids called by %s hit %s\n", ent->classname, hit->classname); hit->touch(hit, ent, NULL, NULL); @@ -2148,25 +2151,24 @@ void V_ShellNonAbilityEffects(edict_t *ent) { if ((ent->monsterinfo.selected_time > level.time) && (sf2qf(level.framenum) & 6)) { ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= RF_SHELL_GREEN; - return;// stop processing effects + return; // stop processing effects } - + // spree war if (SPREE_WAR == true) { // spree dude and his summons glow white if (G_GetSummoner(ent) == SPREE_DUDE) { - ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE); } - // players and player-summoned monsters in FFA/PvP mode glow red + // players and player-summoned monsters in FFA/PvP mode glow red else if (cl_ent) { ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= (RF_SHELL_RED); } - return;// stop processing effects + return; // stop processing effects } // these effects apply to players and player-spawned monsters @@ -2178,7 +2180,7 @@ void V_ShellNonAbilityEffects(edict_t *ent) { // red team shell if (cl_ent->teamnum == RED_TEAM) ent->s.renderfx |= RF_SHELL_RED; - // default blue shell (blue team and PvM aura or morphed/monster) + // default blue shell (blue team and PvM aura or morphed/monster) else { if (!V_IsPVP()) ent->s.renderfx |= RF_SHELL_BLUE; @@ -2187,7 +2189,6 @@ void V_ShellNonAbilityEffects(edict_t *ent) { } - // CTF/Domination/PtR/PvM mode effects if (ctf->value || domination->value || ptr->value || pvm->value || hw->value || tbi->value) { // flag effects for clients @@ -2198,13 +2199,13 @@ void V_ShellNonAbilityEffects(edict_t *ent) { ent->s.effects |= EF_FLAG1; ent->s.renderfx |= RF_SHELL_RED; } - // blue flag + // blue flag else if (ent->client->pers.inventory[blue_flag_index]) { ent->s.effects |= EF_COLOR_SHELL; ent->s.effects |= EF_FLAG2; ent->s.renderfx |= RF_SHELL_BLUE; } - // domination + // domination else if (ent->client->pers.inventory[flag_index]) { ent->s.effects |= EF_COLOR_SHELL; // red team @@ -2212,7 +2213,7 @@ void V_ShellNonAbilityEffects(edict_t *ent) { ent->s.effects |= EF_FLAG1; ent->s.renderfx |= RF_SHELL_RED; } - // blue team + // blue team else { ent->s.effects |= EF_FLAG2; ent->s.renderfx |= RF_SHELL_BLUE; @@ -2230,7 +2231,7 @@ void V_ShellNonAbilityEffects(edict_t *ent) { if (hw->value && !ent->client->pers.inventory[ITEM_INDEX(FindItem("Halo"))]) { ent->s.effects |= EF_SPHERETRANS; - }*/ + }*/ } // these effects only apply to FFA/PvP modes @@ -2269,7 +2270,6 @@ void V_ShellNonAbilityEffects(edict_t *ent) { ent->s.effects |= EF_HALF_DAMAGE; ent->s.renderfx |= RF_SHELL_GREEN | RF_SHELL_RED; } - } void V_ShellAbilityEffects(edict_t *ent) { @@ -2285,12 +2285,12 @@ void V_ShellAbilityEffects(edict_t *ent) { // off-white color shell ent->s.effects |= EF_HALF_DAMAGE; } - // detector ability + // detector ability else if (ent->flags & FL_DETECTED && ent->detected_time > level.time) { ent->s.effects |= EF_COLOR_SHELL | EF_TAGTRAIL; ent->s.renderfx |= (RF_SHELL_YELLOW); } - // an active aura makes you glow cyan + // an active aura makes you glow cyan else if (que_typeexists(ent->auras, 0)) { ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= (RF_SHELL_CYAN); @@ -2304,7 +2304,7 @@ void V_ShellAbilityEffects(edict_t *ent) { remaining = sf2qf(ent->client->quad_framenum - level.framenum); if (remaining > 30 || (remaining & 4)) ent->s.effects |= EF_QUAD; - if (remaining == 30 && (ent->svflags & SVF_MONSTER)) // beginning to fade + if (remaining == 30 && (ent->svflags & SVF_MONSTER)) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0); } @@ -2312,7 +2312,7 @@ void V_ShellAbilityEffects(edict_t *ent) { remaining = sf2qf(ent->client->quadfire_framenum - level.framenum); if (remaining > 30 || (remaining & 4)) ent->s.effects |= EF_QUAD; - if (remaining == 30 && (ent->svflags & SVF_MONSTER)) // beginning to fade + if (remaining == 30 && (ent->svflags & SVF_MONSTER)) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("items/quadfire2.wav"), 1, ATTN_NORM, 0); } @@ -2320,7 +2320,7 @@ void V_ShellAbilityEffects(edict_t *ent) { remaining = sf2qf(ent->client->invincible_framenum - level.framenum); if (remaining > 30 || (remaining & 4)) ent->s.effects |= EF_PENT; - if (remaining == 30 && (ent->svflags & SVF_MONSTER)) // beginning to fade + if (remaining == 30 && (ent->svflags & SVF_MONSTER)) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0); } //END QUAD EFFECTS @@ -2357,7 +2357,7 @@ void V_NonShellEffects(edict_t *ent) { // only non-shell effects are added here, so power shield effect is intentionally omitted } - // ghost effect applies to all classes except Poltergeist (who gets it for free) + // ghost effect applies to all classes except Poltergeist (who gets it for free) else if (ent->myskills.abilities[GHOST].current_level > 0 || vrx_is_morphing_polt(ent)) ent->s.effects |= EF_PLASMA; @@ -2389,7 +2389,7 @@ void V_NonShellEffects(edict_t *ent) { // ********** NON-CLIENT SPECIFIC EFFECTS BELOW ********** // barrel is transparent if it has an owner - if (ent->mtype == M_BARREL && ent->owner && ent->owner->inuse)// && ent->owner->client) + if (ent->mtype == M_BARREL && ent->owner && ent->owner->inuse) // && ent->owner->client) ent->s.effects |= EF_SPHERETRANS; // obstacle becomes transparent before it cloaks @@ -2490,4 +2490,4 @@ qboolean vrx_is_morphing_polt(edict_t *ent) { return ent->myskills.class_num == CLASS_POLTERGEIST && ent->myskills.abilities[MORPH_MASTERY].current_level && !ent->myskills.abilities[MORPH_MASTERY].disable; -} \ No newline at end of file +} diff --git a/src/characters/v_utils.h b/src/characters/v_utils.h index 54dd49e..2339dd9 100644 --- a/src/characters/v_utils.h +++ b/src/characters/v_utils.h @@ -11,7 +11,7 @@ void V_PushBackWalls(edict_t* self, float dist); void V_ModifyMorphedHealth (edict_t *ent, int type, qboolean morph); void V_RegenAbilityAmmo (edict_t *ent, int ability_index, int regen_frames, int regen_delay); void V_Touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf); -void V_UpdatePlayerAbilities (edict_t *ent); +void vrx_normalize_abilities (edict_t *ent); qboolean V_HealthCache (edict_t *ent, int max_per_second, int update_frequency_svframes); qboolean V_ArmorCache (edict_t *ent, int max_per_second, int update_frequency); void vrx_reset_player_state (edict_t *ent); diff --git a/src/combat/abilities/ability_def.h b/src/combat/abilities/ability_def.h index 54c73ec..a49ddf6 100644 --- a/src/combat/abilities/ability_def.h +++ b/src/combat/abilities/ability_def.h @@ -114,3 +114,7 @@ #define EXPLODING_BARREL 110 #define MAX_ABILITIES 160 +#include + + +typedef uint32_t abilitybitmap_t[MAX_ABILITIES / 32 + 1]; diff --git a/src/combat/abilities/g_abilities.h b/src/combat/abilities/g_abilities.h index c51558c..e26cb85 100644 --- a/src/combat/abilities/g_abilities.h +++ b/src/combat/abilities/g_abilities.h @@ -93,6 +93,7 @@ typedef struct skills_s upgrade_t abilities[MAX_ABILITIES]; talentlist_t talents; + prestigelist_t prestige; }skills_t; #endif \ No newline at end of file diff --git a/src/entities/boss_general.c b/src/entities/boss_general.c index 2840f25..4c2052c 100644 --- a/src/entities/boss_general.c +++ b/src/entities/boss_general.c @@ -547,7 +547,7 @@ qboolean SkipFrame (int frame, int *skip_frames) return false; } -void G_RunFrames1 (edict_t *ent, int start_frame, int end_frame, int *skip_frames, qboolean reverse) +void G_RunFrames1 (edict_t *ent, int start_frame, int end_frame, int *skip_frames, qboolean reverse) { int next_frame=ent->s.frame; diff --git a/src/entities/drone/drone_ai.c b/src/entities/drone/drone_ai.c index 1e3d8b9..65a1e02 100644 --- a/src/entities/drone/drone_ai.c +++ b/src/entities/drone/drone_ai.c @@ -54,7 +54,7 @@ void ai_eval_targets() { from->monsterinfo.last_target_scanner = NULL; // forget who last looked at this ent } - /* + /* the older findclosestradius sort of takes the center between the origin and the bounding box. this seems a little unneccesary. i give that up for a little more efficiency by not calculating distance vectors @@ -156,7 +156,7 @@ qboolean G_ValidTarget_Lite(const edict_t *self, const edict_t *target, qboolean { if (trading->value && !(target->flags & FL_NO_TRADING_PROTECT)) return false; - + // check for targets that require medic healing if (self && self->mtype == M_MEDIC) { @@ -229,7 +229,7 @@ void ai_charge (edict_t *self, float dist) // trigger sight function, if available if (self->monsterinfo.sight) self->monsterinfo.sight(self, self->enemy); - + // alert nearby monsters and make them chase our enemy drone_wakeallies(self); } @@ -266,10 +266,10 @@ void ai_run_slide(edict_t *self, float distance) ofs = 90; else ofs = -90; - + if (M_walkmove (self, self->ideal_yaw + ofs, distance)) return; - + self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; M_walkmove (self, self->ideal_yaw - ofs, distance); @@ -370,7 +370,7 @@ qboolean M_ValidMedicTarget(const edict_t *self, const edict_t *target) { if (!strcmp(target->classname, "drone")) { // if we are player-owned, don't resurrect more than maximum limit - if (self->activator && self->activator->client + if (self->activator && self->activator->client && (self->activator->num_monsters + target->monsterinfo.control_cost > MAX_MONSTERS)) return false; return true; @@ -380,7 +380,7 @@ qboolean M_ValidMedicTarget(const edict_t *self, const edict_t *target) { if (!strcmp(target->classname, "bodyque") || !strcmp(target->classname, "player")) { // if we are player-owned, don't resurrect more than maximum limit - if (self->activator && self->activator->client + if (self->activator && self->activator->client && (self->activator->num_monsters + 1 > MAX_MONSTERS)) return false; return true; @@ -390,7 +390,7 @@ qboolean M_ValidMedicTarget(const edict_t *self, const edict_t *target) { if (!strcmp(target->classname, "spiker")) { // if we are player-owned, don't resurrect more than maximum limit - if (self->activator && self->activator->client + if (self->activator && self->activator->client && (self->activator->num_spikers + 1 > SPIKER_MAX_COUNT)) return false; return true; @@ -400,7 +400,7 @@ qboolean M_ValidMedicTarget(const edict_t *self, const edict_t *target) { if (!strcmp(target->classname, "obstacle")) { // if we are player-owned, don't resurrect more than maximum limit - if (self->activator && self->activator->client + if (self->activator && self->activator->client && (self->activator->num_obstacle + 1 > OBSTACLE_MAX_COUNT)) return false; return true; @@ -410,7 +410,7 @@ qboolean M_ValidMedicTarget(const edict_t *self, const edict_t *target) { if (!strcmp(target->classname, "gasser")) { // if we are player-owned, don't resurrect more than maximum limit - if (self->activator && self->activator->client + if (self->activator && self->activator->client && (self->activator->num_gasser + 1 > GASSER_MAX_COUNT)) return false; return true; @@ -470,7 +470,7 @@ qboolean drone_heartarget (edict_t *target) } qboolean M_IgnoreInferiorTarget (edict_t *self, edict_t *target) -{ +{ // doesn't work in invasion mode if (invasion->value) return false; @@ -549,7 +549,7 @@ edict_t *drone_get_enemy (edict_t *self) edict_t *drone_findnavi (edict_t *self); -edict_t *drone_get_target (edict_t *self, +edict_t *drone_get_target (edict_t *self, qboolean get_medic_target, qboolean get_enemy, qboolean get_navi) { edict_t *target = NULL; @@ -576,7 +576,7 @@ edict_t *drone_get_target (edict_t *self, ============= drone_findtarget -Searches a spherical area for enemies and +Searches a spherical area for enemies and returns true if one is found within range ============= */ @@ -754,7 +754,7 @@ qboolean drone_ai_findgoal (edict_t *self) self->monsterinfo.run(self); return true; } - + // no longer valid, so forget about him self->oldenemy = NULL; } @@ -791,7 +791,7 @@ qboolean drone_ai_findgoal (edict_t *self) // the leader is no longer valid, so forget about him self->monsterinfo.leader = NULL; } - + // didn't find anything to chase return false; @@ -886,7 +886,7 @@ void drone_ai_stand (edict_t *self, float dist) drone_ai_checkattack(self); return; } - + self->enemy = NULL; return; } @@ -916,7 +916,7 @@ FindPlat returns true if a nearby platform/elevator is found self the entity searching for the platform -plat_pos the position of the platform (if found) +plat_pos the position of the platform (if found) ============= */ qboolean FindPlat (edict_t *self, vec3_t plat_pos) @@ -928,7 +928,7 @@ qboolean FindPlat (edict_t *self, vec3_t plat_pos) if (!self->enemy) return false; // what are we doing here? - while((e = G_Find(e, FOFS(classname), "func_plat")) != NULL) + while((e = G_Find(e, FOFS(classname), "func_plat")) != NULL) { // this is an ugly hack, but it's the only way to test the distance // or visiblity of a plat, since the origin and bbox yield no useful @@ -1046,7 +1046,7 @@ void FindHigherGoal (edict_t *self, float dist) VectorCopy(tr.endpos, best); // we may find more than one position at the same height // so take it if it's farther from us - else if ((tr.endpos[2] == best[2]) && (distance(self->absmin, + else if ((tr.endpos[2] == best[2]) && (distance(self->absmin, tr.endpos) > distance(self->absmin, best))) VectorCopy(tr.endpos, best); } @@ -1068,12 +1068,12 @@ void FindHigherGoal (edict_t *self, float dist) } if (DRONE_DEBUG) gi.dprintf("found higher goal at %d, current %d\n", (int)best[2], (int)self->absmin[2]); - + self->goalentity = SpawnGoalEntity(self, best); VectorSubtract(self->goalentity->s.origin, self->s.origin, forward); self->ideal_yaw = vectoyaw(forward); M_MoveToGoal(self, dist); - + } /* @@ -1130,7 +1130,7 @@ void FindLowerGoal (edict_t *self, float dist) VectorCopy(tr.endpos, best); // we may find more than one position at the same height // so take it if it's farther from us - else if ((tr.endpos[2] == best[2]) && (distance(self->absmin, + else if ((tr.endpos[2] == best[2]) && (distance(self->absmin, tr.endpos) > distance(self->absmin, best))) VectorCopy(tr.endpos, best); } @@ -1155,7 +1155,7 @@ void FindLowerGoal (edict_t *self, float dist) VectorSubtract(self->goalentity->s.origin, self->s.origin, forward); self->ideal_yaw = vectoyaw(forward); M_MoveToGoal(self, dist); - + } @@ -1171,7 +1171,7 @@ qboolean FollowWall (edict_t *self, vec3_t endpos, vec3_t wall_normal, float dis for (i=90; i<180; i*=2) { - + vectoangles(wall_normal, angles); angles[YAW] += i; if (angles[YAW] > 360) @@ -1203,7 +1203,7 @@ qboolean FollowWall (edict_t *self, vec3_t endpos, vec3_t wall_normal, float dis } gi.dprintf("*** FAILED at %d degrees ***\n", i); return false; - + } */ @@ -1279,7 +1279,7 @@ void drone_unstuck (edict_t *self) forward[0] = cos(DEG2RAD(yaw)); forward[1] = sin(DEG2RAD(yaw)); forward[2] = 0; - + // trace from current position VectorMA(self->s.origin, 64, forward, start); tr = gi.trace(self->s.origin, self->mins, self->maxs, start, self, MASK_SHOT); @@ -1385,7 +1385,7 @@ void drone_pursue_goal (edict_t *self, float dist) if (tr.fraction < 1) { M_MoveToGoal(self, dist); - return; + return; } VectorSubtract(goal_origin, self->s.origin, v); @@ -1415,7 +1415,7 @@ void drone_ai_run_slide (edict_t *self, float dist) { float ofs, range; vec3_t v; - + VectorSubtract(self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); @@ -1437,15 +1437,15 @@ void drone_ai_run_slide (edict_t *self, float dist) ofs = 90; else ofs = -90; - + if (M_walkmove (self, self->ideal_yaw + ofs, dist)) return; - + // change direction and try strafing in the opposite direction self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; M_walkmove (self, self->ideal_yaw - ofs, dist); } - + void TeleportForward (edict_t *ent, vec3_t vec, float dist); void drone_cleargoal (edict_t *self) @@ -1492,10 +1492,10 @@ void drone_ai_giveup (edict_t *self) // if we have no melee function, then make sure we can circle strafe if (!self->monsterinfo.melee) self->monsterinfo.aiflags &= ~AI_NO_CIRCLE_STRAFE; - + // we're not lost anymore, but just idle self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; - + // if we can walk, walk if (self->monsterinfo.walk) self->monsterinfo.walk(self); @@ -1562,7 +1562,7 @@ void M_FindPath (edict_t *self, vec3_t goalpos, qboolean compute_path_now) if (DRONE_DEBUG) gi.dprintf("M_FindPath() trying to recalc path\n"); - // + // // get node location nearest to us and our goal if (!(NearestNodeLocation(self->s.origin, v1, 0, true)) ||!(NearestNodeLocation(goalpos, v2, 0, true))) @@ -1571,7 +1571,7 @@ void M_FindPath (edict_t *self, vec3_t goalpos, qboolean compute_path_now) M_ClearPath(self); return; } - + // try to find a path between these two nodes if (self->flags & FL_FLY) searchType = SEARCHTYPE_FLY; @@ -1581,7 +1581,7 @@ void M_FindPath (edict_t *self, vec3_t goalpos, qboolean compute_path_now) if (FindPath(searchType, v1, v2)) { if (DRONE_DEBUG) - gi.dprintf("%s (%d) is recalculating path at %d\n", + gi.dprintf("%s (%d) is recalculating path at %d\n", GetMonsterKindString(self->mtype), G_GetEntityIndex(self), level.framenum); // copy waypoints to monster @@ -1597,7 +1597,7 @@ void M_FindPath (edict_t *self, vec3_t goalpos, qboolean compute_path_now) else { if (DRONE_DEBUG) - gi.dprintf("%s (%d) failed to recalculate path at %d\n", + gi.dprintf("%s (%d) failed to recalculate path at %d\n", GetMonsterKindString(self->mtype), G_GetEntityIndex(self), level.framenum); // couldn't find a path to our goal @@ -1726,7 +1726,7 @@ void drone_ai_run1 (edict_t *self, float dist) self->monsterinfo.idle_frames = 0; // decoys make step sounds - if ((self->mtype == M_DECOY) && (level.time > self->wait)) + if ((self->mtype == M_DECOY) && (level.time > self->wait)) { gi.sound (self, CHAN_BODY, gi.soundindex(va("player/step%i.wav", (randomMT()%4)+1)), 1, ATTN_NORM, 0); self->wait = level.time + 0.3; @@ -1794,7 +1794,7 @@ void drone_ai_run1 (edict_t *self, float dist) drone_ai_run_slide(self, dist); return; } - + // don't let a search time-out as long as we can see the enemy self->monsterinfo.search_frames = 0; } @@ -1857,10 +1857,10 @@ void drone_ai_run1 (edict_t *self, float dist) // we've reached the temporary entity used for movement commands if (self->monsterinfo.aiflags & AI_COMBAT_POINT) { - + if (DRONE_DEBUG) gi.dprintf("reached combat point\n"); - + // az: follow the chain if (!G_GetClient(self)) // non-player spawned monster, i.e. owned by worldspawn { @@ -1874,7 +1874,7 @@ void drone_ai_run1 (edict_t *self, float dist) gi.dprintf("reached end\n"); } } - + } if (invasion->value) { @@ -1965,7 +1965,7 @@ void drone_ai_run1 (edict_t *self, float dist) if (goalChanged) { if (DRONE_DEBUG) - gi.dprintf("%s (%d) goal changed at %d\n", + gi.dprintf("%s (%d) goal changed at %d\n", GetMonsterKindString(self->mtype), G_GetEntityIndex(self), level.framenum); // calculate path immediately M_FindPath(self, self->monsterinfo.last_sighting, true); @@ -1978,7 +1978,7 @@ void drone_ai_run1 (edict_t *self, float dist) if (!VectorCompare(self->monsterinfo.prevGoalPos, self->monsterinfo.last_sighting)) { if (DRONE_DEBUG) - gi.dprintf("%s (%d) goal position changed at %d\n", + gi.dprintf("%s (%d) goal position changed at %d\n", GetMonsterKindString(self->mtype), G_GetEntityIndex(self), level.framenum); // compute path after a delay self->monsterinfo.updatePath = true; @@ -2001,7 +2001,7 @@ void drone_ai_run1 (edict_t *self, float dist) { if (DRONE_DEBUG) gi.dprintf("drone has waypoints\n"); - // + // // have we reached the end of the path we are searching? if (self->monsterinfo.nextWaypoint < self->monsterinfo.numWaypoints) { @@ -2037,7 +2037,7 @@ void drone_ai_run1 (edict_t *self, float dist) //FIXME: code above looks for a clear path to goalent, and follows it regardless of waypoints; either we force waypoints to be followed if present OR // we change the code below to find the nearest waypoint, and then find the next waypoint from that one - + // get the position of the next waypoint GetNodePosition(self->monsterinfo.waypoint[self->monsterinfo.nextWaypoint], dest); @@ -2128,7 +2128,7 @@ void drone_ai_run1 (edict_t *self, float dist) if (DRONE_DEBUG) gi.dprintf("couldnt find valid path\n"); // we couldn't determine a valid path - drone_ai_lost(self, goal, dist); + drone_ai_lost(self, goal, dist); } } else @@ -2168,7 +2168,7 @@ void drone_ai_run (edict_t *self, float dist) // decoys make step sounds - if ((self->mtype == M_DECOY) && (level.time > self->wait)) + if ((self->mtype == M_DECOY) && (level.time > self->wait)) { gi.sound (self, CHAN_BODY, gi.soundindex(va("player/step%i.wav", (randomMT()%4)+1)), 1, ATTN_NORM, 0); self->wait = level.time + 0.3; @@ -2241,7 +2241,7 @@ void drone_ai_run (edict_t *self, float dist) // circle-strafe our target if we are close - if ((entdist(self, self->enemy) < 256) + if ((entdist(self, self->enemy) < 256) && !(self->monsterinfo.aiflags & AI_NO_CIRCLE_STRAFE)) { drone_ai_run_slide(self, dist); @@ -2314,8 +2314,8 @@ void drone_ai_run (edict_t *self, float dist) // if we are on a platform going up, wait around until we've reached the top if (self->groundentity && (self->monsterinfo.aiflags & AI_PURSUE_PLAT_GOAL) - && (self->groundentity->style == FUNC_PLAT) - && (self->groundentity->moveinfo.state == STATE_UP)) + && (self->groundentity->style == FUNC_PLAT) + && (self->groundentity->moveinfo.state == STATE_UP)) { // gi.dprintf("standing on plat!\n"); // divide by speed to get time to reach destination @@ -2367,7 +2367,7 @@ void drone_ai_run (edict_t *self, float dist) } } else - { + { // we don't already have a goal, make the enemy our goal self->goalentity = self->enemy; @@ -2415,13 +2415,13 @@ void drone_ai_run (edict_t *self, float dist) self->monsterinfo.stand(self); } #endif -} +} void drone_togglelight (edict_t *self) { if (self->health > 0) { - if (level.daytime && self->flashlight) + if (level.daytime && self->flashlight) { // turn light off G_FreeEdict(self->flashlight); @@ -2478,7 +2478,7 @@ qboolean drone_validposition (edict_t *self) { //if (self->activator && self->activator->inuse && !self->activator->client) // respawned = vrx_find_random_spawn_point(self, false); - + //if (!respawned) //{ WriteServerMsg("A drone was removed from a solid object.", "Info", true, false); @@ -2576,7 +2576,7 @@ void drone_return(edict_t* self) return; // teleport back to our leader if we can't reach him - if (!self->enemy && leader && leader->inuse && self->monsterinfo.search_frames > 300 + if (!self->enemy && leader && leader->inuse && self->monsterinfo.search_frames > 300 && level.time > self->monsterinfo.teleport_delay) { TeleportNearArea(self, self->activator->s.origin, 256, false); diff --git a/src/entities/v_items.c b/src/entities/v_items.c index 622471b..673666b 100644 --- a/src/entities/v_items.c +++ b/src/entities/v_items.c @@ -94,7 +94,7 @@ int V_GetRuneAbilityPts(edict_t *ent, item_t *rune) return points; } -void V_ApplyRune(edict_t *ent, item_t *rune) +void vrx_runes_apply(edict_t *ent, item_t *rune) { int i; @@ -1085,7 +1085,7 @@ void V_ItemSwap(item_t *item1, item_t *item2) //************************************************************************************************ -void V_ResetAllStats(edict_t *ent) +void vrx_runes_unapply(edict_t *ent) { int i; @@ -1179,11 +1179,11 @@ void V_EquipItem(edict_t *ent, int index) } //Reset all rune info - V_ResetAllStats(ent); + vrx_runes_unapply(ent); for (i = 0; i < 3; ++i) { if (ent->myskills.items[i].itemtype != TYPE_NONE) - V_ApplyRune(ent, &ent->myskills.items[i]); + vrx_runes_apply(ent, &ent->myskills.items[i]); } } diff --git a/src/g_local.h b/src/g_local.h index 2ec5268..1490454 100644 --- a/src/g_local.h +++ b/src/g_local.h @@ -130,9 +130,9 @@ typedef enum DAMAGE_AIM // auto targeting recognizes this } damage_t; -typedef enum +typedef enum { - WEAPON_READY, + WEAPON_READY, WEAPON_ACTIVATING, WEAPON_DROPPING, WEAPON_FIRING @@ -143,12 +143,12 @@ typedef enum AMMO_BULLETS = 1, AMMO_SHELLS = 2, AMMO_ROCKETS = 3, - AMMO_GRENADES = 4, - AMMO_CELLS = 5, - AMMO_SLUGS = 6, - // RAFAEL - AMMO_MAGSLUG = 7, - AMMO_TRAP = 8, + AMMO_GRENADES = 4, + AMMO_CELLS = 5, + AMMO_SLUGS = 6, + // RAFAEL + AMMO_MAGSLUG = 7, + AMMO_TRAP = 8, // 3.5 AMMO_GENERATOR = 9 } ammo_t; @@ -307,15 +307,15 @@ typedef struct #define IT_HEALTH 256 // gitem_t->weapmodel for weapons indicates model index -#define WEAP_BLASTER 1 -#define WEAP_SHOTGUN 2 -#define WEAP_SUPERSHOTGUN 3 -#define WEAP_MACHINEGUN 4 -#define WEAP_CHAINGUN 5 -#define WEAP_GRENADES 6 -#define WEAP_GRENADELAUNCHER 7 -#define WEAP_ROCKETLAUNCHER 8 -#define WEAP_HYPERBLASTER 9 +#define WEAP_BLASTER 1 +#define WEAP_SHOTGUN 2 +#define WEAP_SUPERSHOTGUN 3 +#define WEAP_MACHINEGUN 4 +#define WEAP_CHAINGUN 5 +#define WEAP_GRENADES 6 +#define WEAP_GRENADELAUNCHER 7 +#define WEAP_ROCKETLAUNCHER 8 +#define WEAP_HYPERBLASTER 9 #define WEAP_RAILGUN 10 #define WEAP_BFG 11 #define WEAP_PHALANX 12 @@ -470,7 +470,7 @@ typedef struct int level_bonus; float time_to_next_respawn; } pvm; - + /* gdsfiles_t gdsfiles[MAX_CLIENTS];*/ // experimental monster pathfinding @@ -663,11 +663,11 @@ extern spawn_temp_t st; extern int sm_meat_index; extern int snd_fry; -extern int jacket_armor_index; -extern int combat_armor_index; -extern int body_armor_index; -extern int power_cube_index; -extern int flag_index; +extern int jacket_armor_index; +extern int combat_armor_index; +extern int body_armor_index; +extern int power_cube_index; +extern int flag_index; extern int red_flag_index; extern int blue_flag_index; extern int halo_index; @@ -675,13 +675,13 @@ extern int resistance_index; extern int strength_index; extern int regeneration_index; extern int haste_index; - -//ammo -extern int bullet_index; -extern int shell_index; -extern int grenade_index; -extern int rocket_index; -extern int slug_index; + +//ammo +extern int bullet_index; +extern int shell_index; +extern int grenade_index; +extern int rocket_index; +extern int slug_index; extern int cell_index; //pre searched items @@ -1005,7 +1005,7 @@ extern cvar_t *vrx_pvmcreditmult; #define FFL_NOSPAWN 2 typedef enum { - F_INT, + F_INT, F_FLOAT, F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL F_GSTRING, // string on disk, pointer in memory, TAG_GAME @@ -1631,7 +1631,7 @@ struct gclient_s float quadfire_framenum; int burst_count; float trap_time; - + int silencer_shots; int weapon_sound; @@ -1742,7 +1742,7 @@ struct edict_s // FIXME: move these fields to a server private sv_entity_t link_t area; // linked to a division node or leaf - + int num_clusters; // if -1, use headnode instead int clusternums[MAX_ENT_CLUSTERS]; int headnode; // unused if num_clusters != -1 @@ -1768,7 +1768,7 @@ struct edict_s char *model; float freetime; // sv.time when the object was freed - + // // only used locally in game, not by server // @@ -1881,7 +1881,7 @@ struct edict_s monsterinfo_t monsterinfo; // jabot (vrxcl/newvrx) - ai_handle_t ai; + ai_handle_t ai; // RAFAEL int orders; @@ -1921,7 +1921,7 @@ struct edict_s float sentrydelay; edict_t *lasersight; float lasthbshot; - edict_t *decoy; + edict_t *decoy; edict_t *flashlight; int mtype; // Type of Monstersee M_* defines.. (M_HOVER, etc) int atype; //3.0 used for new curses @@ -2031,7 +2031,7 @@ struct edict_s // "connection" id, not database id. // kept around without NO_GDS to simplify preprocessor macros - volatile int gds_connection_id; + volatile int gds_connection_id; #ifndef NO_GDS #ifndef GDS_NOMULTITHREADING volatile int gds_thread_status; // vrxchile 3.0 diff --git a/src/menus/armory.c b/src/menus/armory.c index 33c6c9e..b396345 100644 --- a/src/menus/armory.c +++ b/src/menus/armory.c @@ -603,9 +603,9 @@ void SellConfirmMenu_handler(edict_t *ent, int option) safe_cprintf(ent, PRINT_HIGH, "Item Sold for %d credits.\n", value); //Re-apply equipment - V_ResetAllStats(ent); + vrx_runes_unapply(ent); for (i = 0; i < 3; ++i) - V_ApplyRune(ent, &ent->myskills.items[i]); + vrx_runes_apply(ent, &ent->myskills.items[i]); //refund some credits ent->myskills.credits += value; diff --git a/src/menus/item_menu.c b/src/menus/item_menu.c index 8e3dbc8..96554f3 100644 --- a/src/menus/item_menu.c +++ b/src/menus/item_menu.c @@ -17,9 +17,9 @@ void DeleteMenu_handler(edict_t *ent, int option) { memset(&ent->myskills.items[option - 778], 0, sizeof(item_t)); //Re-apply equipment - V_ResetAllStats(ent); + vrx_runes_unapply(ent); for (i = 0; i < 3; ++i) - V_ApplyRune(ent, &ent->myskills.items[i]); + vrx_runes_apply(ent, &ent->myskills.items[i]); safe_cprintf(ent, PRINT_HIGH, "Item deleted.\n"); } else if (option - 666 > 0) { diff --git a/src/menus/menu.c b/src/menus/menu.c index 7a0198d..bdadb38 100644 --- a/src/menus/menu.c +++ b/src/menus/menu.c @@ -316,7 +316,7 @@ menu_can_show returns false if the client has another menu open ============= */ -qboolean menu_can_show (edict_t *ent) +qboolean menu_can_show (edict_t *ent) { if (ent->client->showscores || ent->client->showinventory || ent->client->menustorage.menu_active || ent->client->pers.scanner_active) diff --git a/src/menus/upgrades.c b/src/menus/upgrades.c index 0eef766..d96df7a 100644 --- a/src/menus/upgrades.c +++ b/src/menus/upgrades.c @@ -142,7 +142,7 @@ void OpenUpgradeMenu (edict_t *ent) menu_add_line(ent, va("and you have %d points.", ent->myskills.speciality_points), 0); menu_add_line(ent, " ", 0); - if (ent->myskills.class_num != CLASS_WEAPONMASTER) // WMs don't get class specific skills. + if (ent->myskills.class_num != CLASS_WEAPONMASTER || vrx_prestige_has_class_skills(ent)) // WMs don't get class specific skills. menu_add_line(ent, "Class specific skills", 1); menu_add_line(ent, "General skills", 2); diff --git a/src/menus/v_menu.c b/src/menus/v_menu.c index f56e297..4ba6676 100644 --- a/src/menus/v_menu.c +++ b/src/menus/v_menu.c @@ -88,7 +88,7 @@ void vrx_start_reign(edict_t *ent) gi.bprintf( PRINT_HIGH, "%s starts %s reign.\n", ent->client->pers.netname, GetPossesiveAdjective(ent) ); } - V_UpdatePlayerAbilities(ent); + vrx_normalize_abilities(ent); V_UpdatePlayerTalents(ent); //Set the player's name @@ -483,6 +483,7 @@ void classmenu_handler (edict_t *ent, int option) ent->myskills.class_num = option; vrx_assign_abilities(ent); vrx_set_talents(ent); + vrx_prestige_init(ent); ent->myskills.weapon_respawns = 100; gi.dprintf("INFO: %s created a new %s!\n", @@ -494,7 +495,7 @@ void classmenu_handler (edict_t *ent, int option) ent->client->pers.netname, vrx_get_class_string(ent->myskills.class_num))); - vrx_check_for_levelup(ent, true); + vrx_check_for_levelup(ent, false); vrx_update_all_character_maximums(ent); vrx_add_respawn_weapon(ent, ent->myskills.respawn_weapon); vrx_add_respawn_items(ent); @@ -603,6 +604,7 @@ void generalmenu_handler (edict_t *ent, int option) case 11: ShowVoteModeMenu(ent); break; case 12: ShowHelpMenu(ent, 0); break; case 13: Cmd_Armory_f(ent, 31); break; + case 20: vrx_prestige_open_menu(ent); break; default: menu_close(ent, true); } } @@ -635,11 +637,24 @@ void OpenGeneralMenu (edict_t *ent) else menu_add_line(ent, va("Upgrade talents (%d)", ent->myskills.talents.talentPoints), 3); + // az: only supported on sqlite. i'm lazy. + if (vrx_char_io.type == SAVEMETHOD_SQLITE) { + int prestigePotential = vrx_prestige_get_upgrade_points(ent->myskills.experience); + if (prestigePotential) + menu_add_line(ent, va("Prestige %d (%d)", ent->myskills.prestige.total, prestigePotential), 20); + else + menu_add_line(ent, va("Prestige %d", ent->myskills.prestige.total), 20); + } + menu_add_line(ent, " ", 0); if (!vrx_is_morphing_polt(ent) && ent->myskills.class_num != CLASS_KNIGHT) menu_add_line(ent, "Set respawn weapon", 4); - menu_add_line(ent, "Set master password", 5); + + if (ent->myskills.email[0] == '\0') + menu_add_line(ent, "Set master password", 5); + + menu_add_line(ent, "Show character info", 6); menu_add_line(ent, "Access the armory", 7); menu_add_line(ent, "Access your items", 8); diff --git a/src/q_shared.h b/src/q_shared.h index 83ad653..51b69e5 100644 --- a/src/q_shared.h +++ b/src/q_shared.h @@ -43,7 +43,7 @@ typedef unsigned char byte; //k03 Begin #ifndef __cplusplus -typedef enum { false, true} qboolean; +typedef enum { false, true } qboolean; #else typedef bool qboolean; #endif diff --git a/src/quake2/g_save.c b/src/quake2/g_save.c index 82d0bdc..c4d0027 100644 --- a/src/quake2/g_save.c +++ b/src/quake2/g_save.c @@ -205,6 +205,7 @@ void InitGame(void) vrx_init_char_io(); vrx_init_stash_io(); + vrx_prestige_global_init(); #ifdef CMD_USEHASH InitHash(); diff --git a/src/quake2/g_utils.c b/src/quake2/g_utils.c index 50f2846..e8d7d1a 100644 --- a/src/quake2/g_utils.c +++ b/src/quake2/g_utils.c @@ -1056,7 +1056,7 @@ qboolean G_EntIsAlive(const edict_t *ent) } -void G_RunFrames (edict_t *ent, int start_frame, int end_frame, qboolean reverse) +void G_RunFrames (edict_t *ent, int start_frame, int end_frame, qboolean reverse) { if (reverse) { diff --git a/src/server/misc_stuff.c b/src/server/misc_stuff.c index 2cf31e6..020bda5 100644 --- a/src/server/misc_stuff.c +++ b/src/server/misc_stuff.c @@ -520,7 +520,7 @@ void vrx_write_to_logfile(edict_t *ent, char *s) gi.dprintf("ERROR: Failed to write to player log.\n"); } -void WriteServerMsg (char *s, char *error_string, qboolean print_msg, qboolean save_to_logfile) +void WriteServerMsg (char *s, char *error_string, qboolean print_msg, qboolean save_to_logfile) { cvar_t *port; char buf[512]; diff --git a/src/server/v_cmd.c b/src/server/v_cmd.c index f53030f..7e05958 100644 --- a/src/server/v_cmd.c +++ b/src/server/v_cmd.c @@ -138,7 +138,7 @@ const gameCommand_s commands[] = { "thrust", Cmd_Thrust_f }, { "vote", ShowVoteModeMenu }, { "wormhole", Cmd_WormHole_f }, - { "update", V_UpdatePlayerAbilities}, + { "update", vrx_normalize_abilities}, { "berserker", Cmd_PlayerToBerserk_f }, // { "medicpack", Cmd_fmedi_f }, //lepi { "caltrops", Cmd_Caltrops_f }, diff --git a/src/v_shared.h b/src/v_shared.h index 1b939fa..b28f1e7 100644 --- a/src/v_shared.h +++ b/src/v_shared.h @@ -11,6 +11,7 @@ #include "combat/abilities/shaman.h" #include "combat/abilities/auras.h" #include "characters/Talents.h" +#include "characters/prestige.h" #include "quake2/g_layout.h" /**************** v_abilitylist.c ***************/ @@ -25,6 +26,9 @@ int vrx_get_hard_max(int index, qboolean general, int class); void vrx_assign_abilities(edict_t *ent); +// get any class for which this ability is enabled +int vrx_get_ability_class(int ability); + #define DEFAULT_SOFTMAX 10 #define GENERAL_SOFTMAX 5 @@ -39,6 +43,15 @@ typedef struct { typedef const abilitydef_t *abilitylist_t; const abilitydef_t * vrx_get_ability_by_index(int index); +void vrx_add_ability(edict_t *ent, int index); +void vrx_ability_open_select_menu( + edict_t* self, + const char* title, + qboolean (*filter)(const abilitydef_t*, void*), + int page, + void* userdata, + void (*handler)(edict_t *ent,int option)); + /**************** v_abilitylist.c ***************/ //************ lasersight.c ************ @@ -133,11 +146,11 @@ void V_ItemClear(item_t *item); void V_PrintItemProperties(edict_t *player, item_t *item); int eqSetItems(edict_t *ent, item_t *rune); void ApplyRune(edict_t *ent, item_t *rune); -void V_ResetAllStats(edict_t *ent); +void vrx_runes_unapply(edict_t *ent); void PurchaseRandomRune(edict_t *ent, int runetype); -void V_ApplyRune(edict_t *ent, item_t *rune); +void vrx_runes_apply(edict_t *ent, item_t *rune); void V_ItemSwap(item_t *item1, item_t *item2);