.
diff --git a/GPL-2 b/COPYING.txt
similarity index 100%
rename from GPL-2
rename to COPYING.txt
diff --git a/Makefile b/Makefile
index a65e177..17a7884 100644
--- a/Makefile
+++ b/Makefile
@@ -60,9 +60,9 @@ ifneq ($(PLATFORM),$(COMPILE_PLATFORM))
else
CROSS_COMPILING=0
- ifneq ($(ARCH),$(COMPILE_ARCH))
- CROSS_COMPILING=1
- endif
+ifneq ($(ARCH),$(COMPILE_ARCH))
+ CROSS_COMPILING=1
+endif
endif
export CROSS_COMPILING
@@ -115,7 +115,7 @@ ifeq ($(PLATFORM),linux)
BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes -pipe
- OPTIMIZE = -O2 -fvisibility=hidden -fomit-frame-pointer -ffast-math
+ OPTIMIZE = -O3 -fvisibility=hidden -fomit-frame-pointer -ffast-math
SHLIBEXT=so
SHLIBCFLAGS=-fPIC -fvisibility=hidden
@@ -170,7 +170,7 @@ ifdef MINGW
BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes -pipe
- OPTIMIZE = -O2 -fvisibility=hidden -fomit-frame-pointer -ffast-math
+ OPTIMIZE = -O3 -fvisibility=hidden -fomit-frame-pointer -ffast-math
SHLIBEXT=dll
SHLIBCFLAGS=-fPIC -fvisibility=hidden
@@ -221,7 +221,7 @@ ifeq ($(PLATFORM),windows)
BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes -pipe
- OPTIMIZE = -O2 -fvisibility=hidden -fomit-frame-pointer -ffast-math
+ OPTIMIZE = -O3 -fvisibility=hidden -fomit-frame-pointer -ffast-math
SHLIBEXT=dll
SHLIBCFLAGS=-fPIC -fvisibility=hidden
@@ -237,10 +237,6 @@ endif # DLL
TARGETS =
-ifndef FULLBINEXT
- FULLBINEXT=.$(ARCH)$(BINEXT)
-endif
-
ifndef SHLIBNAME
SHLIBNAME=$(ARCH).$(SHLIBEXT)
endif
@@ -369,10 +365,32 @@ makedirs:
@if [ ! -d $(B)/game ];then $(MKDIR) $(B)/game;fi
@if [ ! -d $(B)/ui ];then $(MKDIR) $(B)/ui;fi
+
+install:
+ $(if $(DESTDIR), , $(error DESTDIR is not set! Use 'make install DESTDIR=/your/path'))
+ @$(MAKE) release
+ @echo "Installing in $(DESTDIR)..."
+ cp $(BR)/cgame$(SHLIBNAME) $(DESTDIR)/cgame$(SHLIBNAME)
+ cp $(BR)/qagame$(SHLIBNAME) $(DESTDIR)/qagame$(SHLIBNAME)
+ cp $(BR)/ui$(SHLIBNAME) $(DESTDIR)/ui$(SHLIBNAME)
+ @echo "*************************************************************************************"
+ @echo " Files compiled with release-$(PLATFORM)-$(SHLIBNAME) are successfully installed! "
+ @echo "*************************************************************************************"
+
+install_debug:
+ $(if $(DESTDIR), , $(error DESTDIR is not set! Use 'make install_debug DESTDIR=/your/path'))
+ @$(MAKE) debug
+ @echo "Installing in $(DESTDIR)..."
+ cp $(BD)/cgame$(SHLIBNAME) $(DESTDIR)/cgame$(SHLIBNAME)
+ cp $(BD)/qagame$(SHLIBNAME) $(DESTDIR)/qagame$(SHLIBNAME)
+ cp $(BD)/ui$(SHLIBNAME) $(DESTDIR)/ui$(SHLIBNAME)
+ @echo "*************************************************************************************"
+ @echo " Files compiled with debug-$(PLATFORM)-$(SHLIBNAME) are successfully installed! "
+ @echo "*************************************************************************************"
+
#############################################################################
## BASEQ3 CGAME
#############################################################################
-# $(B)/cgame/cg_particles.o \
CGOBJ_ = \
$(B)/cgame/cg_main.o \
@@ -389,12 +407,14 @@ CGOBJ_ = \
$(B)/cgame/cg_info.o \
$(B)/cgame/cg_localents.o \
$(B)/cgame/cg_marks.o \
+ $(B)/cgame/cg_particles.o \
$(B)/cgame/cg_players.o \
$(B)/cgame/cg_playerstate.o \
$(B)/cgame/cg_predict.o \
$(B)/cgame/cg_scoreboard.o \
$(B)/cgame/cg_servercmds.o \
$(B)/cgame/cg_snapshot.o \
+ $(B)/cgame/cg_trails.o \
$(B)/cgame/cg_view.o \
$(B)/cgame/cg_weapons.o \
\
@@ -413,6 +433,7 @@ $(B)/cgame$(SHLIBNAME): $(CGOBJ)
QAOBJ_ = \
$(B)/game/g_main.o \
+ $(B)/game/ai_bfp.o \
$(B)/game/ai_chat.o \
$(B)/game/ai_cmd.o \
$(B)/game/ai_dmnet.o \
diff --git a/MakefileQVM b/MakefileQVM
index 4599ad2..8078f99 100644
--- a/MakefileQVM
+++ b/MakefileQVM
@@ -31,7 +31,7 @@ ifeq ($(COMPILE_PLATFORM),cygwin)
endif
ifndef PLATFORM
-PLATFORM=$(COMPILE_PLATFORM)
+ PLATFORM=$(COMPILE_PLATFORM)
endif
export PLATFORM
@@ -53,7 +53,11 @@ endif
# SETUP AND BUILD
#############################################################################
-PK3 = pak9.pk3
+ifndef DESTDIR
+ DESTDIR = .
+endif
+
+PK3 = $(DESTDIR)/pak9.pk3
MOUNTDIR = source
QADIR = $(MOUNTDIR)/game
@@ -76,7 +80,7 @@ QA_SRC = \
g_main $(QADIR)/g_syscalls.asm \
bg_misc bg_lib bg_pmove bg_slidemove \
q_math q_shared \
- ai_dmnet ai_dmq3 ai_team ai_main ai_chat ai_cmd ai_vcmd \
+ ai_bfp ai_dmnet ai_dmq3 ai_team ai_main ai_chat ai_cmd ai_vcmd \
g_active g_arenas g_bot g_client g_cmds g_combat g_items g_mem g_misc \
g_missile g_mover g_session g_spawn g_svcmds g_target g_team \
g_trigger g_utils g_weapon \
@@ -88,8 +92,8 @@ QA_SRC = \
CG_SRC = \
cg_main $(CGDIR)/cg_syscalls.asm \
cg_consolecmds cg_draw cg_drawtools cg_effects cg_ents cg_event cg_info \
- cg_localents cg_marks cg_players cg_playerstate cg_predict cg_scoreboard \
- cg_servercmds cg_snapshot cg_view cg_weapons \
+ cg_localents cg_marks cg_particles cg_players cg_playerstate cg_predict cg_scoreboard \
+ cg_servercmds cg_snapshot cg_trails cg_view cg_weapons \
bg_slidemove bg_pmove bg_lib bg_misc \
q_math q_shared \
diff --git a/README.md b/README.md
index 7ae2b4e..50297ac 100644
--- a/README.md
+++ b/README.md
@@ -1,140 +1,260 @@
Bid For Power (BFP) source code
===============================
+[](https://github.com/LegendaryGuard/BFP/stargazers)
+[](https://github.com/LegendaryGuard/BFP/forks)
+[](#legal)
+[](https://github.com/LegendaryGuard/BFP/actions)
+
-A legendary 90s era Quake 3 Arena mod.
+
+A legendary Quake 3 Arena mod from the late 90s to early 2000s.
-
+
-
-## *WARNING!* UNDER CONSTRUCTION!
-
-### Table of contents
-> > 1. [TODO list](#todo-list)
-> > 2. [History](#history)
-> > 3. [About the repository](#about-the-repository)
-> > 4. [References and clues to know how should be the game](#references-and-clues-to-know-how-should-be-the-game)
-> > 5. [How to build](#how-to-build)
-> > > 5.1. [Introduction](#introduction)
-> > > 5.2. [Windows](#windows)
-> > > > 5.1.1. [Building QVM (using .bat)](#building-qvm-using-bat)
-> > > > 5.1.2. [Building QVM (mingw)](#building-qvm-mingw)
-> > > > 5.1.3. [MSYS2 (mingw) (Building dynamic libraries (.dll))](#msys2-mingw-building-dynamic-libraries-dll)
-> > > > 5.1.4. [Cygwin (mingw) (Building dynamic libraries (.dll))](#cygwin-mingw-building-dynamic-libraries-dll)
-> > >
-> > > 5.3. [Linux](#linux)
-> > > > 5.3.1. [Building QVM (make)](#building-qvm-make)
-> > > > 5.3.2. [Building QVM (using .bat)](#building-qvm-using-bat-1)
-> > > > 5.3.3. [Building shared libraries (.so)](#building-shared-libraries-so)
-> > >
-> > > 5.4. [Optional](#optional)
-> > 6. [Notes](#notes)
-> > 7. [License](#license)
-> > 8. [Credits](#credits)
-
-# TODO list:
-
-- [x] ~~Toggeable flight (bind key). Hint: FLIGHT POWERUP~~
-- [ ] Gauntlet logic must be replaced as a bind key, it will use fight animation (using kicks and fists)
-- [x] ~~Remove weapon visuals (models and stuff)~~
-- [x] ~~Animations as listed on the old docs~~
-- [x] ~~Bind key to recover ki energy~~
-- [x] ~~Bind key to toggle speed (ki boost). HINT: HASTE POWERUP~~
-- [x] ~~Replace ammo to ki energy stamina~~
-- [x] ~~Third person traceable crosshair~~
-- [ ] Make ki energy regeneration, ki use, attacks, charging balance indicated on old docs
-- [ ] Powerlevel and Power Tiers indicated on old docs
-- [ ] Hit Stun (makes player can't use ki, melee, block and charge)
-- [ ] Power Struggles (when two beam attacks collide)
-- [ ] Blocking (consumes ki energy, transfers all damage to ki instead of health, deflect missile attacks, more info on old docs)
-- [ ] Short-Range Teleport (when pressing 2 times left or right)
-- [ ] Transformations (related to Power Tiers)
-- [ ] Attacksets (configurable for cfgs)
-- [ ] Skin Config File (explosionModel, explosionShader, missileRotation, missileShader, ... look old docs about that. "Custom plugin models")
-- [x] ~~Playable third person mode and first person vis mode (add the options in the UI Setup menu)~~
-- [ ] Cvars as described on old docs
-- [ ] Survival gametype (`g_gametype 3`)
-- [ ] Oozaru gametype (`g_gametype 4`)
-- [ ] Last Man Standing gametype (`g_gametype 6`)
-- [ ] 6 different selectable characters, each with 5 attacks (can be referenced to some previous tasks)
-- [ ] 21 different ki attacks including controllable, homing, and chargeable attacks (no guns) (can be referenced to some previous tasks)
-
-### History
+# TODO list
+
+### Pending:
+
+- [ ] Attacksets ([bfp_attacksets.cfg](cfgs/bfp_attacksets.cfg))
+- [ ] Skin Config File (explosionModel, explosionShader, missileRotation, missileShader, ... look old docs and cfgs about that: Custom plugin models - default.cfg for each player model)
+- [ ] Weapon Config File ([bfp_weapon.cfg](cfgs/bfp_weapon.cfg) & [bfp_weapon2.cfg](cfgs/bfp_weapon2.cfg))
+
+### Done:
+
+- [x] Animations as listed on the old docs
+- [x] Auras
+- [x] Balanced player physics movements (different from the original BFP, but balanced for gameplay)
+- [x] Beam trails
+- [x] BFP config file ([bfp.cfg](cfgs/bfp.cfg)) (a sample of how the game saves general binding and BFP cvar stuff in a cfg file)
+- [x] bg_ weapon properties: weaponTime, randomWeaponTime, kiCostAsPct, kiPct, kiCost, chargeAttack, chargeAutoFire, minCharge, maxCharge, loopingAnim, noAttackAnim and movementPenalty
+- [x] Bind key to recover ki energy
+- [x] Bind key to toggle speed (ki boost)
+- [x] Blinding Flash (blinds opponents during 6 seconds)
+- [x] Blocking (consumes ki energy, transfers all damage to ki instead of health, deflect missile attacks, more info on old docs)
+- [x] Breakable map entities ("func_breakable")
+- [x] Bots behavior
+- [x] Bounce features (bounces, bounceFriction, noZBounce)
+- [x] Cone of fire feature (coneOfFireX and coneOfFireY)
+- [x] Cvars
+- [x] Damage calculated by attacker powerlevel, modified knockback and melee knockback (pushing opponents after receiving impact/damage, also loses altitude while flying/underwater/on slopes)
+- [x] Charge multipliers and maximum damage for weapons (maxDamage, chargeRadiusMult, chargeDamageMult, chargeExpRadiusMult)
+- [x] Extraknockback for weapons (extraKnockback)
+- [x] Forcefield feature
+- [x] Gametype: Survival (`g_gametype 3`)
+- [x] Gametype: Monster (`g_gametype 4`)
+- [x] Gametype: Team Last Man Standing (`g_gametype 6`)
+- [x] Homing weapon feature
+- [x] Hit Stun (makes player can't use ki, melee, block and charge)
+- [x] Illegal player model server handling
+- [x] Instant character model changing
+- [x] Ki shockwave (or Tornado Blast) weapon (a shockwave to push opponents)
+- [x] Ki trails
+- [x] Load player models only by prefix
+- [x] Multishot feature
+- [x] Melee feature
+- [x] Offset features (randXOffset, randYOffset and alternatingXOffset)
+- [x] Particles
+- [x] Power Struggles (when two beam attacks collide)
+- [x] Powerlevel and Power Tiers indicated on old docs
+- [x] Playable third person mode and first person vis mode
+- [x] Priority for projectiles
+- [x] Missile properties (missileSpeed, missileDuration, missileGravity and missileAcceleration)
+- [x] railTrail weapon property
+- [x] Radius feature (radius, maxRadius, maxExpRadius)
+- [x] Razor disk weapon (fires a cutting disk which can pierce beating low health opponents)
+- [x] Reflective feature
+- [x] Remove some items like powerups and health pickups
+- [x] Remove weapon visuals (models and stuff)
+- [x] Replace ammo to ki energy stamina
+- [x] Rocket jumping (applicable if jumping is pressed at the same time)
+- [x] sbeam attack type feature
+- [x] Short-Range Teleport - Zanzoken (when pressing 2 times left or right)
+- [x] Spectator can stop following the last player
+- [x] Splitting ki ball (rdmissile attack type) feature
+- [x] Third person traceable crosshair
+- [x] Toggeable flight (bind key)
+- [x] Transformations (related to Power Tiers)
+
+
+## Table of contents
+> 1. [History](#history)
+> 2. [About the repository](#about-the-repository)
+> 3. [References and clues to know how should be the game](#references-and-clues-to-know-how-should-be-the-game)
+> 4. [How to build](#how-to-build)
+> > 4.1. [Introduction](#introduction)
+> > 4.2. [Windows](#windows)
+> > > 4.2.1. [Building QVM (using .bat)](#building-qvm-using-bat)
+> > > 4.2.2. [Building QVM (mingw)](#building-qvm-mingw)
+> > > 4.2.3. [MSYS2 (mingw) (Building dynamic libraries (.dll))](#msys2-mingw-building-dynamic-libraries-dll)
+> > > 4.2.4. [Cygwin (mingw) (Building dynamic libraries (.dll))](#cygwin-mingw-building-dynamic-libraries-dll)
+> > > 4.2.5. [MSVC (.dll))](#msvc-dll)
+> >
+> > 4.3. [Linux](#linux)
+> > > 4.3.1. [Building QVM (make)](#building-qvm-make)
+> > > 4.3.2. [Building QVM (using .bat)](#building-qvm-using-bat-1)
+> > > 4.3.3. [Building shared libraries (.so)](#building-shared-libraries-so)
+> >
+> > 4.4. [Optional](#optional)
+> 5. [Notes](#notes)
+> 6. [Legal](#legal)
+> 7. [Credits](#credits)
+
+# History

-Started: 1999
+Started: [1998](https://goldenhammersoftware.blogspot.com/2010/07/big-mountain-snowboarding-history-and.html)
Ended: 2002
Bid For Power is a total conversion for QuakeIII that plays nothing like the original game. Players take control of Ki-powered superheros and battle it out in a mostly aerial fight. The game is highlighted by the work of a great art team and an original style, and the gameplay is extremely fast paced. It can be difficult to keep up with until you get the hang of it.
-The source code was lost, but the assets and some docs are in any place.
+The project was started on Quake 2, that's where they were getting organized playing Quake and learn some programming there.
-#### Old dev journals (1999 - 2002)
+The source code is said to have started from SDK 1.15c released on January 14, 2000. It continued to be updated, including the addition of `cg_particles.c` in 1.29, until 1.31.
+More info can be found in [Quake 3 Arena changelog version history](https://discourse.ioquake.org/t/quake-3-changelog-version-history/375).
-- Old Yrgol dev journal: https://web.archive.org/web/20020205150340/http://www.bidforpower.com/journals/yrgol.php
+The original source code appears to be lost, but the assets and some docs are available in various places. Nonetheless, not all sources are accessible.
-Click here to see the [Old Yrgol dev journal (Markdown edition)](docs/yrgol_dev_journal.md)
+### Old dev journals (1998 - 2002)
-- Old Rodney Olmos dev journal: https://web.archive.org/web/20011218204129/http://bidforpower.com/journals/rodney.php
+The original URLs can be slower to load, so it's recommended to use markdown edition ones.
-Click here to see the [Old Rodney Olmos dev journal (Markdown edition)](docs/rodney_dev_journal.md)
+NOTE: Some URLs and images within these contents may be broken or partially recovered. The final parts of Chris and Yrgol dev journals were found here:
+Chris: https://web.archive.org/web/20011202065630/http://bidforpower.com/
+Yrgol: https://web.archive.org/web/20020520060044/http://www.planetquake.com/Bidforpower/
+On markdown editions, the data log is complete.
-- Old PyroFragger dev journal: https://web.archive.org/web/20011218203246/http://bidforpower.com/journals/pyrofragger.php
+- Chris dev journal: https://web.archive.org/web/20020210145200/http://bidforpower.com/journals/chris.php
-Click here to see the [Old PyroFragger dev journal (Markdown edition)](docs/pyrofragger_dev_journal.md)
+ * [Chris dev journal (Markdown edition)](docs/old_dev_journals/chris_dev_journal.md)
-### About the repository
+- Yrgol dev journal: https://web.archive.org/web/20020205150340/http://www.bidforpower.com/journals/yrgol.php
-We're making a replica of the lost source code.
-The highest priority goal is copying and recreating completely BFP game logical structure.
-Any fixes, improvements and contributions are welcome. But we can't accept secondary things and other stuff that don't reach the goals.
+ * [Yrgol dev journal (Markdown edition)](docs/old_dev_journals/yrgol_dev_journal.md)
-### References and clues to know how should be the game
+- Ansel dev journal: https://web.archive.org/web/20011203063814/http://bidforpower.com/journals/ansel.php
-Documentations, references and extracted stuff will give us clues to reach the goals.
+ * [Ansel dev journal (Markdown edition)](docs/old_dev_journals/ansel_dev_journal.md)
-- Old documentations:
+- Anthony dev journal: https://web.archive.org/web/20020210151755/http://bidforpower.com/journals/anthony.php
- * [Guide](docs/Guide.md)
- * [Creating custom plugin models](docs/Create_Custom_Models.md)
+ * [Anthony dev journal (Markdown edition)](docs/old_dev_journals/anthony_dev_journal.md)
-
+- Dash dev journal: https://web.archive.org/web/20020223210411/http://www.bidforpower.com/journals/dash.php
-- Cvars, cmd and bind stuff about the old game:
+ * [Dash dev journal (Markdown edition)](docs/old_dev_journals/dash_dev_journal.md)
- * [Bindlist](docs/bind_bfp_list.txt)
- * [Cmdlist](docs/cmd_bfp_list.txt)
- * [Cvarlist](docs/cvar_bfp_list.txt)
+- Rodney Olmos dev journal: https://web.archive.org/web/20011218204129/http://bidforpower.com/journals/rodney.php
-We can see a cvar, e.g. `g_plKillBonusPct`, which means we need to find the function that rewards the player and do something with that. Something like this:
+ * [Rodney Olmos dev journal (Markdown edition)](docs/old_dev_journals/rodney_dev_journal.md)
-```c
-if ( killedSomeone ) {
- pl = currentPL + (currentPL * g_plKillBonusPct.value);
-}
-```
+- PyroFragger dev journal: https://web.archive.org/web/20011218203246/http://bidforpower.com/journals/pyrofragger.php
-
+ * [PyroFragger dev journal (Markdown edition)](docs/old_dev_journals/pyrofragger_dev_journal.md)
-_**Click on some image to see it complete**._
+- Remisser dev journal: https://web.archive.org/web/20020210152114/http://bidforpower.com/journals/remisser.php
-- How should the HUD be:
+ * [Remisser dev journal (Markdown edition)](docs/old_dev_journals/remisser_dev_journal.md)
-
+... And more dev journals can be found [here](https://web.archive.org/web/20011202135731/http://bidforpower.com/journals/).
-
+# About the repository
-- When player receives a hit stun (`g_hitStun 1`):
+We're making a replica of the lost source code.
+The highest priority goal is to copy and recreate the complete logical structure of the BFP game. It would be a game SDK that'll provide a structured and standardized way to modify the mod.
+You'll notice some differences and things that the original Bid For Power didn't have/were forgotten, incomplete or poorly made such as:
+
+#### EFFECTS:
+- how particles move (e.g. bubbles are handled underwater and touching something solid vanishes to save performance)
+- explosion dynamic lights are back (these were broken after RC/beta versions)
+- real impact crack mark, when projectiles/ki attacks impact something solid on ground, walls or slopes (on original BFP, that hasn't been fixed)
+- particle aura is almost implemented
+- beam trails are bendy, almost similar to original BFP though
+- while charging ki near water, bubble particles appear
+- when player is still moving with/without friction and charging ki, antigrav rock particles appear
+- when antigrav rock and rock debris particles fall in a slope or inclined places, bounce and slide, that makes the movements more realistic
+- 3d particles for bubbles, antigrav rock and rock debris, that can be enabled with `cg_3dparticles` cvar. For 3d rock particles, you need 3 models inside models/effects/ directory: `pebble1.md3`, `pebble2.md3` and `pebble3.md3`; otherwise, it will appear the standard ones. That option is included in BFP OPTIONS menu in VIEW & FX, also with a new explosion type option in EXPLOSIONS: Ultra Hardcore
+- piercing weapons, like Razor disk, emit spark particles when hitting a player
+- beam struggle emits spark particles in the middle of both beams (in original BFP, emits in the zeroed origin)
+- new particles: charge smoke, which appears when charging ki near the ground
+- new cool missile trails (that replaces original Q3 missile smoke trails), includes rainbow option (it can be disabled by setting `cg_oldRocket 1`). These trails are quite similar to the Rocket pod weapon from [Bazooka Q3 (BQ3)](https://planetquake.gamespy.com/View10ca.html?view=Articles.Detail&id=693) mod
+
+#### PMOVE:
+- balanced player pmove physics:
+ - players can interact bounce pads like Q3 does (on original BFP, the physics are like you're sliding heavily on the ground and not bouncing as usually do, you're being pushed when touching bounce pads)
+ - no weird underwater movements while going intentionally down and moving crazily fast touching the ground (this might be a bug/glitch from original BFP)
+ - no specified stuck animation, so any animation is correctly handled when being stuck (on original BFP, when the player is stuck or pretty near to something solid, the reason is still unknown though. It does a jumping forward/backward animation, that doesn't make sense)
+ - water movement handling is different from original BFP, but it works similarly
+
+#### ATTACKS:
+- beam with chargeAutoFire can fire correctly by holding the attack key (on original BFP, beam is detonated at the first instance and player gets damaged by the detonation)
+- the owner who fires rdmissile with chargeAutoFire, can fire without dealing damage (on original BFP, every time is being used after some charge, player gets damaged after firing per weaponTime)
+
+#### GAMEMODES:
+- survival gamemode is pretty well balanced (on original BFP, when everytime the player changes a different character model from their own prefix, dies and respawns during warmup, the warmup resets. So, that's unfair)
+- monster/oozaru gamemode has the following in-game differences compared to the original:
+ - the player monster is labeled 'MON' on the scoreboard
+ - the player monster has a larger floating sprite chat
+ - the player monster has a larger shadow effect similar to regular players
+ - the player monster has a large dynamic light when charging or using ki
+ - the player monster's ki trails are larger than other players'
+ - the player monster generates bigger bubbles, smoke, and antigrav rock particles when charging/using ki boost
+ - the player monster can pick up items on the floor or where its hitbox can touch
+ - the player monster can use teleporter without getting stuck on most maps, but that may not work 100% on all of them
+ - player monster's first-person and first-person vis mode viewpoints work properly
+ - player monster's third-person camera has improved focus in the code (similar to BFP's standard third-person view but scaled for giant characters), it can be enabled by setting `BFP_MONSTER_CAMERA_VIEWPOS` macro to `0` and recompile the code, although the traceable crosshair looks a bit jerky
+ - `g_monster` cvar enables the monster/big monkey feature (that happened in the RC/beta versions)
+ - available in the UI, maps marked as `monster` besides `oozaru` can be viewed in the GAME SERVER menu
+- team last man standing has the following in-game differences compared to the original:
+ - players cannot switch teams after joining a team (on original BFP, the dead player -who was forced to spectate- can join during the match, that was against the rules)
+ - players cannot voluntarily switch to spectator after joining a team
+ - players attempting to switch teams/spectate receive centerprint messages
+ - maps marked as `tlms` besides `lms` can be viewed in the GAME SERVER menu
+
+#### UI:
+- some adjusted UI buttons
+- BFP OPTIONS menu is upgraded, big explosions and smoke options are back (these were removed after RC/beta versions), also shell and ring options are available and these are options are interactive with explosion type option. Sprite and particle aura types are available on aura type option
+- in the first GAME SERVER menu has a horizontal scrolling description about the selected gametype
+- GAME SERVER menu has pagination
+- PLAYER SETTINGS menu has pagination
+- SERVER INFO menu displays all server info (on original BFP, the info was badly displayed and nothing was shown) and it has pagination
+- DRIVER INFO menu is fixed (on original BFP crashes) and it has pagination on extensions
+- CHANGE MAP button is added in in-game pause MENU, allows you to change the map without restarting the server and making everyone reconnect
+
+#### MISC:
+- `cg_drawTeamOverlay 2` is fixed on the lower right corner (on original BFP looks non-visible)
+- `g_allowSpectatorChat` cvar is functional, spectators can't send messages if the cvar is disabled
+- `cg_forceSkin` forces the default skin for each model correctly when changing models/skins (on original BFP, that changes to the first model of the prefix with default skin)
+- some cvars didn't save changed values after quitting the game that happened on original BFP (means that `CVAR_ARCHIVE` wasn't on them), but these are now applied on replica
+- kiCharge, boostCost and blockCost cvars work differently
+- removed some unused cvars
+- spectator mode can toggle ki boost as if the player is flying
+- ki boost consumption is applied in msec if `timescale` is different from it (on original BFP only applies until 0.89 and from 0.89 until 0.80 reduces one per one very slowly)
+- file size differences between the QVM and the original BFP QVM can be quite significant
+- configstrings in CS_PLAYERS like "c1" and "hc" are removed to be optimized, handicap is disabled logically because it isn't used at all
+- some Q3e patches are applied, like events and function stuff...
+- ...
+
+Any fixes, improvements and contributions are welcome. But we can't accept secondary things and other stuff that don't reach the goals.
-
+# References and clues to know how should be the game
+
+Documentations, references and extracted stuff will give us clues to reach the goals.
+
+- Old documentations and networking:
+
+ * [Guide](docs/Guide.md)
+ * [Creating custom plugin models](docs/Create_Custom_Models.md)
+ * [Networking](docs/bfp_networking.md)
-- When player is being ready to shot (holding a key):
+- Attackset and Weapon config files:
-
+ * [Attackset Config File](docs/bfp_attacksets_config_file.md)
+ * [Weapon Config File](docs/bfp_weapon_config_file.md)
@@ -157,20 +277,15 @@ Weapon settings:
* [bfp_weapon.cfg](cfgs/bfp_weapon.cfg)
* [bfp_weapon2.cfg](cfgs/bfp_weapon2.cfg)
-BFP config (general binding and some client stuff, unused. WARNING: when executing, game crashes):
+BFP config (a sample of how the game saves general binding and BFP cvar stuff in a cfg file):
* [bfp.cfg](cfgs/bfp.cfg)
-Other q3 config:
+# How to build
- * [q3config.cfg](cfgs/q3config.cfg)
+### Introduction
-
-### How to build
-
-#### Introduction
-
-*IMPORTANT NOTE TO THE DEVELOPMENT*: all source code files should be **UTF-8 without BOM** and **Unix (LF)**, otherwise, it will cause compiler errors when using MakefileQVM (most likely, syntax error will be displayed).
+*IMPORTANT NOTE TO THE DEVELOPMENT*: all source code files must be **UTF-8 without BOM** and **Unix (LF)**, otherwise, it will cause compiler errors when using MakefileQVM (most likely, syntax error will be displayed).
- `.map` file is a linker map file, which is generated by the linker when it links together multiple object files into an executable or shared library. It contains information about the symbols (such as functions and variables) defined in each object file, as well as their addresses in the final executable or library.
The information in the map file can be useful for debugging and performance analysis. For example, it can help you identify which functions are taking the most time to execute, or which functions are being called from which parts of the code.
@@ -281,6 +396,26 @@ The information in the map file can be useful for debugging and performance anal
7- `git`
3. Open Cygwin, go to the directory where you cloned the repository and compile with `make`
+
+ * #### _MSVC (.dll)_:
+
+ Requires MSVC (Microsoft Visual C++) 2005 or newer (Visual Studio Community) to be installed.
+
+ 1. Run `bfpq3.sln`
+
+ Open the solution in `win32-msvc` directory. If you're using a newer version of MSVC and it's appearing a dialog box that tries to migrate the solution and their stuff, continue by clicking Ok. After the browser will open about the solution migration details, you can close it and ignore the warnings that comments about the old MSVC stuff from 2005.
+
+ 2. Build the solution
+
+ In the *Solution Configurations* tab, you can switch `Debug` or `Release` and `Win32` or `x64`.
+ Right-click to the solution in the *Solution Explorer* and click `Build Solution`, check the log until the build is succeded. Go to `Win32` or `x64` folder in `win32-msvc`, look inside `Debug` or `Release` and get the dlls you built.
+
+ 3. Debugging
+
+ You need the dlls built with `Debug` and running in-game.
+ In the MSVC tab, `Debug` > `Attach to Process` or press `Ctrl+Alt+P` and use the `Attach to Process` dialog to attach the debugger to the process (in that case select `Quake3.exe` / `ioQuake3.exe` / `Quake3e.exe` or any Quake 3 executable launcher). After, while that's running, add some breakpoint in some line of the code where you want to debug and test.
+
+ Some old MSVC tutorial can be found in **Focus on Mod Programming for Quake III Arena** in `APPENDIX A - Debugging Your Mod in Visual Studio` section.
- ### Linux:
@@ -311,6 +446,11 @@ The information in the map file can be useful for debugging and performance anal
make -f MakefileQVM clean
```
+ Optionally, you can set the destination directory, so the pk3 file will appear on this directory:
+ ```sh
+ make -f MakefileQVM DESTDIR=/your/path/q3/baseq3mod
+ ```
+
* #### _Building QVM (using .bat)_:
1. The alternative to execute and get the compiled qvms with `build.bat` requires [`wine` package](https://www.winehq.org/). So, in that part, needs the i386 package:
@@ -362,18 +502,33 @@ The information in the map file can be useful for debugging and performance anal
... Optionally, you can play the parameters like `ARCH=x86_64` (compiles 64-bits builds), `PLATFORM=windows` (compiles dlls), `PLATFORM=linux` (compiles shared libraries (.so files)) ...
+ * To compile and copy release builds at the destination directory, `DESTDIR` parameter is mandatory:
+ ```sh
+ make install DESTDIR=/your/path/q3/baseq3mod # compiles release builds and copy the builds to the destination directory (you can also put ARCH=x86 PLATFORM=windows if you want)
+ ```
+
+ * To compile and copy debug builds at the destination directory, `DESTDIR` parameter is mandatory:
+ ```sh
+ make install DESTDIR=/your/path/q3/baseq3mod # compiles debug builds and copy the builds to the destination directory (you can also put ARCH=x86 PLATFORM=windows if you want)
+ ```
+
-### Notes
+# Notes
**IMPORTANT NOTE**: This repository was initialized from https://github.com/marconett/q3a.
#### Added source code files:
+- ai_bfp.c
+- ai_bfp.h
+- bg_events.h
+- bg_meansofdeath.h
- cg_cvar.h
+- cg_trails.c
- g_cvar.h
-- ui_cvar.h
- ui_bfpoptions.c
+- ui_cvar.h
- ui_mem.c
- ui_mp3decoder.c
- ui_mp3decoder.h
@@ -389,31 +544,51 @@ The information in the map file can be useful for debugging and performance anal
#### Unused source code files and unavailable in the build tools:
-- cg_particles.c
+- g_rankings.c
+- g_rankings.h
+- ui_login.c
- ui_rankings.c
- ui_rankstatus.c
- ui_signup.c
- ui_specifyleague.c
- ui_spreset.c
-### License
+# Legal
-The mod source code is GPLv3 licensed, the source code contents are based on Quake III Arena which is GPLv2 licensed.
+The mod source code is [GPLv2 licensed](./COPYING.txt), the contents are based on Quake III Arena.
-The ancient abandoned MP3 decoder (`ui_mem.c`, `ui_mem.h`, `ui_mp3decoder.c` and `ui_mp3decoder.h`) is based on code from various contributors:
+The ancient abandoned MP3 decoder (`ui_mem.c`, `ui_mem.h`, `ui_mp3decoder.c` and `ui_mp3decoder.h`) uses _Open Source Modification License (OSML) v1.0_ and is based on code from various contributors:
- Copyright (C) 1993 Sun Microsystems
- Copyright (C) 1995-1997 Michael Hipp
- Copyright (C) 1999 Aaron Holtzman
- Copyright (C) 2000-2001 Tim Angus
-## Credits
+The images, screenshots, and URLs, even in the docs, included in this repository are used for reference purposes only and are not covered by the GPL license. They're sourced from various locations and are subject to their respective copyrights and terms of use.
+
+> [!WARNING]
+> Any derivative works must follow the same rules and disclose their code to the public as to encourage fan projects based on Bid For Power.
+
+### Bid For Power name
+
+Nobody owns the "Bid For Power" name. Bid For Power was founded by Chris James and likely ended up in the palm of Yrgol's hand (although Yrgol doesn't own the assets). The owner, the maintainer and the contributors of the repository don't own this name.
+This does not give any single person or a group of people to sell the name, basically it belongs to the original community.
+The Bid For Power team may provide sufficient security against any claims or improper use of the name.
+
+### Disclaimer
+
+The game elements such as characters, and events depicted in this game are fictitious. Any resemblance to actual persons, living or deceased, or real events is purely coincidental.
+
+# Credits
Bid For Power is made by these staff members. We don't own materials such as art designs, maps and character models from their assets.
-#### Bid For Power Staff Members
+
+
+### Bid For Power Staff Members
+
Ansel
-Skin Artist
+Skin Artist
Anthony
2D Artist
@@ -456,3 +631,5 @@ Project Lead, Lead Programmer
::Additional Assistance::
Mooky, Perfect Chaos, Dakota, Bardock, DethAyngel, Ebola, Badhead, $onik, Gigatron, Timex & Nat.
+
+
diff --git a/build.bat b/build.bat
index c30d971..86e143b 100644
--- a/build.bat
+++ b/build.bat
@@ -115,6 +115,29 @@ echo.
echo.
)
+if not "%1" == "" (
+ if not exist "%1" (
+ echo.
+ echo *************************************
+ echo ERROR: "%1 doesn't exist"
+ echo *************************************
+ echo.
+ goto :quit
+ )
+ if exist pak9.pk3 (
+ echo.
+ move pak9.pk3 "%1"
+ echo pak9.pk3 has been moved successfully!
+ echo.
+ ) else (
+ echo.
+ echo *************************************
+ echo ERROR: pak9.pk3 doesn't exist
+ echo *************************************
+ echo.
+ )
+)
+
:quit
set ERROR_CGAME_QVM=
set ERROR_QAGAME_QVM=
diff --git a/cfgs/bfp.cfg b/cfgs/bfp.cfg
index 79c51e8..2a166d5 100644
--- a/cfgs/bfp.cfg
+++ b/cfgs/bfp.cfg
@@ -1,4 +1,4 @@
-c model bfp3-gokussj5/default
+c model bfp2-tetsedah/default
b TAB "+scores"
b ENTER "+button2"
b ESCAPE "togglemenu"
@@ -7,7 +7,7 @@ b + "sizeup"
b - "sizedown"
b / "weapnext"
b 0 "weapon 10"
-b 1 "weapon 1bind 2 weapon 2bind 3 weapon 3bind 4 weapon 4bind 5 weapon 5"
+b 1 "weapon 1"
b 2 "weapon 2"
b 3 "weapon 3"
b 4 "weapon 4"
@@ -17,19 +17,21 @@ b 7 "weapon 7"
b 8 "weapon 8"
b 9 "weapon 9"
b = "sizeup"
+b F "+button12"
b [ "weapprev"
b \ "+mlook"
b ] "weapnext"
b _ "sizedown"
b ` "toggleconsole"
b a "+moveleft"
-b b "vstr piccolo"
b c "+movedown"
b d "+moveright"
b f "+button12"
-b m "vstr ginu"
-b n "vstr goku"
-b o "kiusetoggle"
+b j "+button3"
+b k "set_ki_charge"
+b l "kiusetoggle"
+b q "+button8"
+b r "map_restart 0"
b s "+back"
b t "messagemode"
b w "+forward"
@@ -49,15 +51,7 @@ b END "centerview"
b F1 "vote yes"
b F2 "vote no"
b F3 "ui_teamorders"
-b F4 "vstr vegeta"
-b F5 "vstr beb"
-b F6 "vstr beb"
-b F7 "vstr gotenks"
-b F8 "vstr trunks"
-b F9 "vstr freeza1"
-b F10 "vstr cell"
b F11 "screenshot"
-b F12 "vstr freeza1"
b MOUSE1 "+attack"
b MOUSE2 "+button9"
b MOUSE3 "+zoom"
@@ -66,27 +60,27 @@ b MWHEELUP "weapprev"
c cg_thirdPerson 1
c cg_thirdPersonRange 110
c cg_thirdPersonHeight -60
-c cg_fixedThirdPerson 1
+c cg_fixedThirdPerson 0
c cg_lightAuras 1
c cg_lightExplosions 1
c cg_lightweightAuras 0
-c cg_transformationAura 1
-c cg_simpleHUD 0
+c cg_transformationAura 0
+c cg_simpleHUD 1
c cg_drawOwnModel 0
-c cg_polygonAura 1
+c cg_polygonAura 0
c cg_spriteAura 0
-c cg_highPolyAura 1
-c cg_smallOwnAura 0
+c cg_highPolyAura 0
+c cg_smallOwnAura 1
c cg_permaglowUltimate 1
c cg_superdeformed 0
c cg_flytilt 1
-c cg_kiTrail 50
-c cg_beamTrail 25
+c cg_kiTrail 99
+c cg_beamTrail 81
c cg_bigExplosions 1
c cg_chargeupAlert 1
c cg_stfu 0
-c cg_lifekills 109
-c cg_lifedeaths 153
+c cg_lifekills 51
+c cg_lifedeaths 469
c cg_drawKiWarning 1
c sv_pure 1
c cg_explosionShell 1
@@ -96,7 +90,7 @@ c cg_crosshairColor 7
c cg_stableCrosshair 0
c cg_explosionRing 1
c cg_lowpolysphere 0
-c cg_musicUnpacked 1
+c cg_musicUnpacked 0
c cg_playHitSound 0
-c g_hitStun 1
+c g_hitStun 0
bfp
diff --git a/cfgs/bfp_attacksets.cfg b/cfgs/bfp_attacksets.cfg
index 956b0d9..7ce4f4b 100644
--- a/cfgs/bfp_attacksets.cfg
+++ b/cfgs/bfp_attacksets.cfg
@@ -1,11 +1,11 @@
attackset 1
attack 0 21
-attack 1 25
-attack 2 24
-attack 3 28
-attack 4 96
-modelPrefix kr-
-defaultModel kr-gotenks
+attack 1 16
+attack 2 15
+attack 3 23
+attack 4 13
+modelPrefix bfp1-
+defaultModel bfp1-kyah
attackset 2
attack 0 21
@@ -52,517 +52,4 @@ attack 4 17
modelPrefix bfp6-
defaultModel bfp6-shilo
-attackset 7
-attack 0 21
-attack 1 24
-attack 2 19
-attack 3 12
-attack 4 11
-modelPrefix gt-
-defaultModel gt-pan
-
-attackset 8
-attack 0 21
-attack 1 27
-attack 2 15
-attack 3 28
-attack 4 52
-modelPrefix bfp33-
-defaultmodel bfp33-chousu
-
-attackset 9
-attack 0 21
-attack 1 20
-attack 2 44
-attack 3 11
-attack 4 31
-modelPrefix genki
-defaultModel genki
-
-attackset 10
-attack 0 21
-attack 1 14
-attack 2 25
-attack 3 101
-attack 4 26
-modelPrefix popo
-defaultModel popo
-
-attackset 11
-attack 0 21
-attack 1 14
-attack 2 25
-attack 3 18
-attack 4 26
-modelPrefix kriccolo
-defaultModel kriccolo
-
-attackset 12
-attack 0 21
-attack 1 57
-attack 2 95
-attack 3 79
-attack 4 86
-modelPrefix bfp44-
-defaultModel bfp44-broly
-
-attackset 13
-attack 0 21
-attack 1 25
-attack 2 18
-attack 3 11
-attack 4 19
-modelPrefix bfp50-
-defaultModel bfp50-puipui
-
-attackset 14
-attack 0 21
-attack 1 27
-attack 2 10
-attack 3 20
-attack 4 22
-modelPrefix yajirobe
-defaultModel yajirobe
-
-attackset 15
-attack 0 21
-attack 1 25
-attack 2 24
-attack 3 28
-attack 4 107
-modelPrefix bfp24-
-defaultModel bfp24-gohangsm
-
-attackset 16
-attack 0 21
-attack 1 16
-attack 2 15
-attack 3 23
-attack 4 13
-modelPrefix bfp12-
-defaultModel bfp12-freeza
-
-attackset 17
-attack 0 21
-attack 1 27
-attack 2 20
-attack 3 14
-attack 4 11
-modelPrefix gurdo
-defaultModel gurdo
-
-attackset 18
-attack 0 21
-attack 1 56
-attack 2 65
-attack 3 79
-attack 4 95
-modelPrefix bfp58-
-defaultModel bfp58-chibgoku
-
-attackset 19
-attack 0 21
-attack 1 25
-attack 2 24
-attack 3 28
-attack 4 26
-modelPrefix paikuhan
-defaultModel paikuhan
-
-attackset 20
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 11
-attack 4 95
-modelPrefix ussj
-defaultModel ussj
-
-attackset 21
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 11
-attack 4 17
-modelPrefix vegeta
-defaultModel vegeta
-
-attackset 22
-attack 0 21
-attack 1 20
-attack 2 24
-attack 3 23
-attack 4 19
-modelPrefix bfp5-
-defaultmodel bfp5-vegetto
-
-attackset 23
-attack 0 16
-attack 1 14
-attack 2 14
-attack 3 15
-attack 4 26
-modelPrefix a19
-defaultModel a19
-
-attackset 24
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 19
-attack 4 18
-modelPrefix bfp45-
-defaultmodel bfp45-gotenks
-
-attackset 25
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 11
-attack 4 99
-modelPrefix majinveg
-defaultModel majinveg
-
-attackset 26
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 11
-attack 4 32
-modelPrefix bfp53-
-defaultmodel bfp53-oozaru
-
-attackset 27
-attack 0 21
-attack 1 14
-attack 2 24
-attack 3 28
-attack 4 96
-modelPrefix srkaio
-defaultModel srkaio
-
-attackset 28
-attack 0 21
-attack 1 25
-attack 2 30
-attack 3 24
-attack 4 46
-modelPrefix bfp41-
-defaultModel bfp41-dende
-
-attackset 29
-attack 0 21
-attack 1 20
-attack 2 24
-attack 3 11
-attack 4 13
-modelPrefix bfp11-
-defaultModel bfp11-buu
-
-attackset 30
-attack 0 21
-attack 1 20
-attack 2 24
-attack 3 18
-attack 4 19
-modelPrefix yamcha
-defaultModel yamcha
-
-attackset 31
-attack 0 10
-attack 1 25
-attack 2 24
-attack 3 28
-attack 4 19
-modelPrefix cyrax
-defaultModel cyrax
-
-attackset 32
-attack 0 21
-attack 1 16
-attack 2 15
-attack 3 23
-attack 4 13
-modelPrefix bfp29-
-defaultModel bfp29-Koola
-
-attackset 33
-attack 0 21
-attack 1 44
-attack 2 28
-attack 3 44
-attack 4 32
-modelPrefix bfp46-
-defaultModel bfp46-Ginger
-
-atackset 34
-attack 0 21
-attack 1 10
-attack 2 15
-attack 3 24
-attack 4 109
-modelPrefix android16
-defaultmodel android16
-
-attackset 35
-attack 0 21
-attack 1 14
-attack 2 15
-attack 3 18
-attack 4 22
-modelPrefix android17
-defaultModel android17
-
-attackset 36
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 12
-attack 4 99
-modelPrefix bfp8-
-defaultModel bfp8-broly
-
-attackset 37
-attack 0 55
-attack 1 27
-attack 2 47
-attack 3 24
-attack 4 63
-modelPrefix bfp56-
-defaultModel bfp56-Tien
-
-attackset 38
-attack 0 28
-attack 1 24
-attack 2 19
-attack 3 13
-attack 4 17
-modelPrefix bfp7-
-defaultModel bfp7-vegota
-
-attackset 39
-attack 0 21
-attack 1 14
-attack 2 24
-attack 3 12
-attack 4 26
-modelPrefix bfp49-
-defaultModel bfp49-dragon
-
-attackset 40
-attack 0 21
-attack 1 18
-attack 2 24
-attack 3 11
-attack 4 40
-modelPrefix bfp9-
-defaultmodel bfp9-gogeta
-
-attackset 41
-attack 0 54
-attack 1 95
-attack 2 24
-attack 3 11
-attack 4 32
-modelPrefix bfp100-
-defaultmodel bfp100-vegetahell
-
-attackset 42
-attack 0 21
-attack 1 25
-attack 2 24
-attack 3 28
-attack 4 19
-modelPrefix notinuse
-defaultModel notinuse
-
-attackset 43
-attack 0 14
-attack 1 35
-attack 2 25
-attack 3 12
-attack 4 19
-modelPrefix bfp13-
-defaultModel bfp13-gokulo
-
-attackset 44
-attack 0 21
-attack 1 25
-attack 2 61
-attack 3 28
-attack 4 13
-modelPrefix bfp57-
-defaultModel bfp57-fatbuu
-
-attackset 45
-attack 0 21
-attack 1 25
-attack 2 24
-attack 3 17
-attack 4 19
-modelPrefix bfp14-
-defaultModel bfp14-yamhan
-
-attackset 46
-attack 0 25
-attack 1 24
-attack 2 17
-attack 3 99
-attack 4 26
-modelPrefix bfp16-veccolo
-defaultModel bfp16-veccolo
-
-attackset 47
-attack 0 21
-attack 1 27
-attack 2 45
-attack 3 79
-attack 4 32
-modelPrefix bfp34-
-defaultModel bfp34-cell
-
-attackset 48
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 19
-attack 4 32
-modelPrefix bfp26-
-defaultModel bfp26-recoom2
-
-attackset 49
-attack 0 18
-attack 1 24
-attack 2 28
-attack 3 19
-attack 4 31
-modelPrefix bfp19-
-defaultModel bfp19-gokhun
-
-attackset 50
-attack 0 21
-attack 1 20
-attack 2 24
-attack 3 50
-attack 4 13
-modelPrefix bfp40-
-defaultModel bfp40-kai
-
-attackset 51
-attack 0 21
-attack 1 12
-attack 2 24
-attack 3 11
-attack 4 19
-modelPrefix bfp20-
-defaultModel bfp20-ssj2gohan
-
-attackset 52
-attack 0 21
-attack 1 14
-attack 2 24
-attack 3 58
-attack 4 42
-modelPrefix bfp52-
-defaultModel bfp52-drgero
-
-attackset 53
-attack 0 21
-attack 1 25
-attack 2 24
-attack 3 19
-attack 4 37
-modelPrefix ussj-
-defaultModel ussj-goku
-
-attackset 54
-attack 0 21
-attack 1 10
-attack 2 24
-attack 3 11
-attack 4 13
-modelPrefix bfp38-
-defaultModel bfp38-zarbon
-
-attackset 55
-attack 0 21
-attack 1 16
-attack 2 24
-attack 3 12
-attack 4 17
-modelPrefix jeice
-defaultModel jeice
-
-attackset 56
-attack 0 21
-attack 1 14
-attack 2 15
-attack 3 54
-attack 4 32
-modelPrefix bfp42-
-defaultModel bfp42-nappa
-
-attackset 57
-attack 0 21
-attack 1 25
-attack 2 24
-attack 3 28
-attack 4 19
-modelPrefix bfp30-
-defaultModel bfp30-gohan
-
-attackset 58
-attack 0 21
-attack 1 25
-attack 2 24
-attack 3 11
-attack 4 13
-modelPrefix bfp54-
-defaultModel bfp54-kiwi
-
-attackset 59
-attack 0 44
-attack 1 44
-attack 2 44
-attack 3 44
-attack 4 77
-modelPrefix bfp27-
-defaultModel bfp27-videl
-
-attackset 60
-attack 0 21
-attack 1 10
-attack 2 94
-attack 3 32
-attack 4 22
-modelPrefix bfp36-
-defaultModel bfp36-celljr
-
-attackset 61
-attack 0 21
-attack 1 24
-attack 2 49
-attack 3 48
-attack 4 32
-modelPrefix bfp35-
-defaultModel bfp35-berta
-
-attackset 62
-attack 0 21
-attack 1 10
-attack 2 44
-attack 3 31
-attack 4 32
-modelPrefix bfp55-
-defaultModel bfp55-Yarkan
-
-attackset 63
-attack 0 21
-attack 1 44
-attack 2 28
-attack 3 44
-attack 4 32
-modelPrefix bfp46-
-defaultModel bfp46-Ginger
-
end
\ No newline at end of file
diff --git a/cfgs/bfp_weapon.cfg b/cfgs/bfp_weapon.cfg
index d3407bb..461411a 100644
--- a/cfgs/bfp_weapon.cfg
+++ b/cfgs/bfp_weapon.cfg
@@ -1,7 +1,7 @@
(large_ki_blast)
weaponNum 10
attackType missile
-weaponTime 750
+weaponTime 1250
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
@@ -10,8 +10,8 @@ chargeAttack 0
chargeAutoFire 0
minCharge 0
maxCharge 0
-damage 30
-splashDamage 30
+damage 40
+splashDamage 40
chargeDamageMult 0
maxDamage 0
radius 40
@@ -20,15 +20,18 @@ chargeRadiusMult 0
chargeExpRadiusMult 0
maxRadius 0
maxExpRadius 0
-missileSpeed 6000
+missileSpeed 4000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -36,6 +39,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 20
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(impact_beam)
weaponNum 11
@@ -47,11 +56,11 @@ kiPct 0
kiCost 250
chargeAttack 1
chargeAutoFire 0
-minCharge 2
+minCharge 1
maxCharge 6
-damage 10
-splashDamage 10
-chargeDamageMult 10
+damage 5
+splashDamage 5
+chargeDamageMult 5
maxDamage 50
radius 30
explosionRadius 350
@@ -62,12 +71,15 @@ maxExpRadius 0
missileSpeed 2000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 1
@@ -75,6 +87,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(super_homing)
weaponNum 12
@@ -101,12 +119,15 @@ maxExpRadius 0
missileSpeed 4000
homing 0.5
homingRange 2000
+homingAcceleration 0
range 0
loopingAnim 1
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -114,38 +135,47 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(deathball)
weaponNum 13
attackType missile
-weaponTime 1500
+weaponTime 750
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
-kiCost 750
+kiCost 250
chargeAttack 1
chargeAutoFire 0
minCharge 2
maxCharge 6
-damage 80
-splashDamage 30
-chargeDamageMult 10
-maxDamage 0
-radius 75
-explosionRadius 400
-chargeRadiusMult 25
-chargeExpRadiusMult 150
-maxRadius 200
+damage 20
+splashDamage 20
+chargeDamageMult 20
+maxDamage 100
+radius 130
+explosionRadius 800
+chargeRadiusMult 30
+chargeExpRadiusMult 300
+maxRadius 0
maxExpRadius 0
-missileSpeed 5000
+missileSpeed 3000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 1
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 1
@@ -153,6 +183,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 100
+missileAcceleration 0
+multishot 0
+bounces 1
+bounceFriction 0.7
+missileDuration 2000
(eyebeam)
weaponNum 14
@@ -179,12 +215,15 @@ maxExpRadius 0
missileSpeed 0
homing 0
homingRange 0
-range 2500
+homingAcceleration 0
+range 2200
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -192,6 +231,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 0
(finger_beam)
weaponNum 15
@@ -218,12 +263,15 @@ maxExpRadius 0
missileSpeed 0
homing 0
homingRange 0
+homingAcceleration 0
range 8192
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -231,6 +279,12 @@ blinding 0
extraKnockback 0
railTrail 1
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 0
(finger_blast)
weaponNum 16
@@ -238,7 +292,7 @@ attackType hitscan
weaponTime 50
randomWeaponTime 100
kiCostAsPct 1
-kiPct .010
+kiPct .007
kiCost 0
chargeAttack 0
chargeAutoFire 0
@@ -257,12 +311,15 @@ maxExpRadius 0
missileSpeed 0
homing 0
homingRange 0
-range 1500
+homingAcceleration 0
+range 2200
loopingAnim 1
noAttackAnim 0
alternatingXOffset 0
-randYOffset 35
-randXOffset 35
+randYOffset 0
+randXOffset 0
+coneOfFireX 50
+coneOfFireY 50
piercing 0
reflective 0
priority 0
@@ -270,6 +327,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 0
(ultimate_blast)
weaponNum 17
@@ -283,9 +346,9 @@ chargeAttack 1
chargeAutoFire 0
minCharge 2
maxCharge 6
-damage 10
-splashDamage 10
-chargeDamageMult 30
+damage 20
+splashDamage 20
+chargeDamageMult 20
maxDamage 100
radius 50
explosionRadius 100
@@ -296,12 +359,15 @@ maxExpRadius 700
missileSpeed 2500
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 1
@@ -309,11 +375,17 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(homing_special)
weaponNum 18
attackType rdmissile
-weaponTime 1250
+weaponTime 1000
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
@@ -335,12 +407,15 @@ maxExpRadius 0
missileSpeed 2000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -349,6 +424,12 @@ extraKnockback 0
railTrail 0
movementPenalty 0
explosionSpawn 9
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(homing_special_spawn)
weaponNum 9
@@ -372,15 +453,18 @@ chargeRadiusMult 0
chargeExpRadiusMult 50
maxRadius 0
maxExpRadius 250
-missileSpeed 700
+missileSpeed 1000
homing 0.9
homingRange 800
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -388,6 +472,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(power_wave_blast)
weaponNum 19
@@ -396,13 +486,13 @@ weaponTime 1200
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
-kiCost 600
+kiCost 500
chargeAttack 1
-minCharge 4
+minCharge 3
maxCharge 6
-damage 25
-splashDamage 25
-chargeDamageMult 25
+damage 60
+splashDamage 60
+chargeDamageMult 10
maxDamage 0
radius 25
explosionRadius 100
@@ -413,12 +503,15 @@ maxExpRadius 0
missileSpeed 2500
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 1
@@ -426,6 +519,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(tornado_blast)
weaponNum 20
@@ -433,7 +532,7 @@ attackType hitscan
weaponTime 100
randomWeaponTime 0
kiCostAsPct 1
-kiPct .010
+kiPct .015
kiCost 0
chargeAttack 0
chargeAutoFire 0
@@ -452,12 +551,15 @@ maxExpRadius 0
missileSpeed 0
homing 0
homingRange 0
-range 500
+homingAcceleration 0
+range 450
loopingAnim 1
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 1
reflective 1
priority 0
@@ -465,6 +567,12 @@ blinding 0
extraKnockback 200
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 0
(ki_blast)
weaponNum 21
@@ -488,15 +596,18 @@ chargeRadiusMult 0
chargeExpRadiusMult 0
maxRadius 0
maxExpRadius 0
-missileSpeed 6000
+missileSpeed 5000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -504,20 +615,26 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0.9
+missileDuration 10000
(razor_disk)
weaponNum 22
attackType missile
-weaponTime 1000
+weaponTime 1250
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
-kiCost 250
+kiCost 500
chargeAttack 1
chargeAutoFire 0
minCharge 2
maxCharge 6
-damage 300
+damage 100
splashDamage 0
chargeDamageMult 0
maxDamage 0
@@ -527,15 +644,18 @@ chargeRadiusMult 25
chargeExpRadiusMult 0
maxRadius 0
maxExpRadius 0
-missileSpeed 2500
+missileSpeed 4000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 1
reflective 0
priority 3
@@ -543,6 +663,12 @@ blinding 0
extraKnockback -1000
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(homing_razor_disk)
weaponNum 23
@@ -554,10 +680,10 @@ kiPct 0
kiCost 250
chargeAttack 1
chargeAutoFire 0
-minCharge 2
-maxCharge 6
-damage 20
-splashDamage 20
+minCharge 1
+maxCharge 1
+damage 35
+splashDamage 35
chargeDamageMult 0
maxDamage 0
radius 75
@@ -569,12 +695,15 @@ maxExpRadius 0
missileSpeed 1000
homing 0.5
homingRange 2000
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 1
reflective 0
priority 3
@@ -582,6 +711,12 @@ blinding 0
extraKnockback -1000
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(ki_storm)
weaponNum 24
@@ -589,16 +724,16 @@ attackType missile
weaponTime 200
randomWeaponTime 0
kiCostAsPct 1
-kiPct .025
+kiPct .020
kiCost 0
chargeAttack 0
chargeAutoFire 0
minCharge 0
maxCharge 0
-damage 15
-splashDamage 15
+damage 10
+splashDamage 10
chargeDamageMult 0
-maxDamage 15
+maxDamage 0
radius 15
explosionRadius 100
chargeRadiusMult 0
@@ -608,19 +743,28 @@ maxExpRadius 0
missileSpeed 8000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 1
noAttackAnim 0
alternatingXOffset 15
randYOffset 25
randXOffset 7
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
blinding 0
-extraKnockback 0
+extraKnockback -25
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(homingball)
weaponNum 25
@@ -632,10 +776,10 @@ kiPct 0
kiCost 100
chargeAttack 1
chargeAutoFire 0
-minCharge 2
-maxCharge 2
-damage 30
-splashDamage 30
+minCharge 1
+maxCharge 1
+damage 40
+splashDamage 40
chargeDamageMult 0
maxDamage 0
radius 20
@@ -644,15 +788,18 @@ chargeRadiusMult 0
chargeExpRadiusMult 0
maxRadius 0
maxExpRadius 0
-missileSpeed 700
+missileSpeed 800
homing 0.3
-homingRange 2000
+homingRange 2500
+homingAcceleration 0
range 0
loopingAnim 1
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -660,6 +807,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(corkscrew_blast)
weaponNum 26
@@ -673,25 +826,28 @@ chargeAttack 1
chargeAutoFire 0
minCharge 2
maxCharge 6
-damage 25
+damage 35
splashDamage 0
-chargeDamageMult 25
+chargeDamageMult 15
maxDamage 100
radius 10
explosionRadius 250
chargeRadiusMult 0
-chargeExpRadiusMult 25
+chargeExpRadiusMult 0
maxRadius 0
maxExpRadius 0
missileSpeed 3000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 1
@@ -699,6 +855,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(blinding_flash)
weaponNum 27
@@ -710,13 +872,13 @@ kiPct 0
kiCost 100
chargeAttack 1
chargeAutoFire 0
-minCharge 3
-maxCharge 3
+minCharge 2
+maxCharge 2
damage 1
splashDamage 0
chargeDamageMult 0
maxDamage 0
-radius 800
+radius 1200
explosionRadius 0
chargeRadiusMult 0
chargeExpRadiusMult 0
@@ -725,12 +887,15 @@ maxExpRadius 0
missileSpeed 0
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 1
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 0
@@ -738,11 +903,17 @@ blinding 1
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 0
(mantis_blast)
weaponNum 28
attackType beam
-weaponTime 500
+weaponTime 650
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
@@ -751,220 +922,28 @@ chargeAttack 1
chargeAutoFire 0
minCharge 3
maxCharge 6
-damage 10
-splashDamage 10
-chargeDamageMult 15
-maxDamage 75
-radius 0
-explosionRadius 0
-chargeRadiusMult 15
-chargeExpRadiusMult 120
-maxRadius 45
-maxExpRadius 500
-missileSpeed 2000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(mouthbeam)
-weaponNum 29
-attackType sbeam
-weaponTime 250
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 250
-chargeAttack 0
-chargeAutoFire 0
-minCharge 0
-maxCharge 0
damage 15
splashDamage 15
-chargeDamageMult 0
-maxDamage 15
-radius 50
-explosionRadius 200
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 1000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(Force Field)
-weaponNum 30
-attackType forcefield
-weaponTime 200
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 90
-chargeAttack 1
-chargeAutoFire 1
-minCharge 3
-maxCharge 0
-damage 2
-splashDamage 2
-chargeDamageMult 0
-maxDamage 2
-radius 800
-explosionRadius 0
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 1
-priority 2
-blinding 0
-extraKnockback 1
-railTrail 0
-movementPenalty 0
-
-(kameso_blast)
-weaponNum 31
-attackType beam
-weaponTime 1500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 1000
-chargeAttack 1
-chargeAutoFire 0
-minCharge 2
-maxCharge 6
-damage 80
-splashDamage 10
-chargeDamageMult 30
-maxDamage 100
-radius 50
-explosionRadius 250
-chargeRadiusMult 25
-chargeExpRadiusMult 200
-maxRadius 0
-maxExpRadius 750
-missileSpeed 2500
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(homing_beam)
-weaponNum 32
-attackType beam
-weaponTime 1500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 600
-chargeAttack 1
-chargeAutoFire 0
-minCharge 2
-maxCharge 6
-damage 10
-splashDamage 10
chargeDamageMult 10
-maxDamage 50
-radius 30
-explosionRadius 350
-chargeRadiusMult 0
-chargeExpRadiusMult 200
-maxRadius 0
-maxExpRadius 0
-missileSpeed 800
-homing 0.3
-homingRange 2000
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(sky_blast)
-weaponNum 35
-attackType beam
-weaponTime 500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 250
-chargeAttack 1
-chargeAutoFire 0
-minCharge 3
-maxCharge 6
-damage 20
-splashDamage 10
-chargeDamageMult 15
-maxDamage 75
+maxDamage 0
radius 0
-explosionRadius 6000
+explosionRadius 0
chargeRadiusMult 15
chargeExpRadiusMult 120
maxRadius 45
-maxExpRadius 10000
+maxExpRadius 500
missileSpeed 2000
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
+coneOfFireX 0
+coneOfFireY 0
piercing 0
reflective 0
priority 1
@@ -972,83 +951,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
-
-(genki)
-weaponNum 37
-attackType missile
-weaponTime 4000
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 100
-chargeAttack 1
-chargeAutoFire 0
-minCharge 2
-maxCharge 0
-damage 60
-splashDamage 60
-chargeDamageMult 20
-maxDamage 0
-radius 25
-explosionRadius 400
-chargeRadiusMult 90
-chargeExpRadiusMult 200
-maxRadius 200
-maxExpRadius 0
-missileSpeed 900
-homing 0
-homingRange 0
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(final_kamehameha)
-weaponNum 40
-attackType beam
-weaponTime 1200
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 600
-chargeAttack 1
-minCharge 4
-maxCharge 6
-damage 70
-splashDamage 0
-chargeDamageMult 0
-maxDamage 0
-radius 20
-explosionRadius 7000
-chargeRadiusMult 60
-chargeExpRadiusMult 1200
-maxRadius 500
-maxExpRadius 12000
-missileSpeed 3000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(aga)
weaponNum 99
@@ -1057,16 +965,16 @@ weaponTime 200
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
-kiCost 100
+kiCost 200
chargeAttack 1
chargeAutoFire 1
minCharge 4
maxCharge 0
-damage 1
+damage 20
splashDamage 0
chargeDamageMult 0
maxDamage 0
-radius 8000
+radius 900
explosionRadius 0
chargeRadiusMult 0
chargeExpRadiusMult 0
@@ -1075,138 +983,27 @@ maxExpRadius 0
missileSpeed 0
homing 0
homingRange 0
+homingAcceleration 0
range 0
loopingAnim 1
noAttackAnim 0
alternatingXOffset 0
randYOffset 0
randXOffset 0
-piercing 0
-reflective 1
-priority 2
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 2
-
-(Weak_beam_rage)
-weaponNum 61
-attackType missile
-weaponTime 400
-randomWeaponTime 0
-kiCostAsPct 1
-kiPct .025
-kiCost 0
-chargeAttack 0
-chargeAutoFire 0
-minCharge 0
-maxCharge 0
-damage 8
-splashDamage 5
-chargeDamageMult 0
-maxDamage 10
-radius 50
-explosionRadius 100
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 100
-missileSpeed 3000
-homing 0.3
-homingRange 4000
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 50
-randYOffset 40
-randXOffset 50
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 25
-railTrail 0
-movementPenalty 0
-
-(ghost)
-weaponNum 46
-attackType rdmissile
-weaponTime 1500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 400
-chargeAttack 1
-chargeAutoFire 0
-minCharge 3
-maxCharge 6
-damage 55
-splashDamage 55
-chargeDamageMult 0
-maxDamage 0
-radius 30
-explosionRadius 120
-chargeRadiusMult 0
-chargeExpRadiusMult 40
-maxRadius 0
-maxExpRadius 0
-missileSpeed 2000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
coneOfFireX 0
coneOfFireY 0
piercing 0
-reflective 0
-priority 0
+reflective 1
+priority 2
blinding 0
extraKnockback 0
railTrail 0
-movementPenalty 0
-explosionSpawn 9
-
-(beam_rage)
-weaponNum 109
-attackType missile
-weaponTime 400
-randomWeaponTime 0
-kiCostAsPct 1
-kiPct .075
-kiCost 0
-chargeAttack 0
-chargeAutoFire 0
-minCharge 0
-maxCharge 0
-damage 15
-splashDamage 5
-chargeDamageMult 0
-maxDamage 15
-radius 50
-explosionRadius 100
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 100
-missileSpeed 3000
-homing 0.9
-homingRange 4000
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 50
-randYOffset 40
-randXOffset 50
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 25
-railTrail 0
movementPenalty 2
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 0
end
diff --git a/cfgs/bfp_weapon2.cfg b/cfgs/bfp_weapon2.cfg
index 582f2a3..f7723e6 100644
--- a/cfgs/bfp_weapon2.cfg
+++ b/cfgs/bfp_weapon2.cfg
@@ -1,635 +1,21 @@
-(Absorb)
-weaponNum 42
-attackType hitscan
-weaponTime 100
-randomWeaponTime 0
-kiCostAsPct 1
-kiPct -20
-kiCost 0
-chargeAttack 0
-chargeAutoFire 0
-minCharge 0
-maxCharge 0
-damage 8
-splashDamage 0
-chargeDamageMult 0
-maxDamage 0
-radius 220
-explosionRadius 0
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 600
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 1
-reflective 1
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(Kaioken)
-weaponNum 44
-attackType forcefield
-weaponTime 850
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 60
-chargeAttack 1
-chargeAutoFire 0
-minCharge 3
-maxCharge 0
-damage 70
-splashDamage 70
-chargeDamageMult 2
-maxDamage 80
-radius 140
-explosionRadius 0
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 1
-priority 2
-blinding 0
-extraKnockback 200
-railTrail 0
-movementPenalty 0
-
-(WEAK BEAM TIER 3) (Krillin and Cell Jr)
-weaponNum 45
-attackType beam
-weaponTime 1200
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 500
-chargeAttack 1
-minCharge 3
-maxCharge 6
-damage 10
-splashDamage 5
-chargeDamageMult 15
-maxDamage 55
-radius 25
-explosionRadius 100
-chargeRadiusMult 25
-chargeExpRadiusMult 120
-maxRadius 75
-maxExpRadius 0
-missileSpeed 2500
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(Force Tornado)
-weaponNum 49
-attackType forcefield
-weaponTime 200
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 100
-chargeAttack 1
-chargeAutoFire 1
-minCharge 3
-maxCharge 0
-damage 10
-splashDamage 10
-chargeDamageMult 0
-maxDamage 10
-radius 800
-explosionRadius 0
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 1
-priority 2
-blinding 0
-extraKnockback 100
-railTrail 0
-movementPenalty 2
-
-(Bomb)
-weaponNum 54
-attackType missile
-weaponTime 1000
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 350
-chargeAttack 1
-chargeAutoFire 0
-minCharge 3
-maxCharge 6
-damage 55
-splashDamage 55
-chargeDamageMult 0
-maxDamage 55
-radius 20
-explosionRadius 650
-chargeRadiusMult 0
-chargeExpRadiusMult 650
-maxRadius 0
-maxExpRadius 0
-missileSpeed 1000
-homing 0
-homingRange 0
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(weak ass finger_beam)
-weaponNum 55
-attackType hitscan
-weaponTime 1500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 200
-chargeAttack 0
-chargeAutoFire 0
-minCharge 0
-maxCharge 0
-damage 20
-splashDamage 10
-chargeDamageMult 0
-maxDamage 0
-radius 1
-explosionRadius 200
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 20000
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 1
-movementPenalty 0
-
-(Tiens weak homing_special)
-weaponNum 47
-attackType rdmissile
-weaponTime 1250
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 150
-chargeAttack 1
-chargeAutoFire 0
-minCharge 1
-maxCharge 6
-damage 25
-splashDamage 15
-chargeDamageMult 0
-maxDamage 0
-radius 30
-explosionRadius 120
-chargeRadiusMult 0
-chargeExpRadiusMult 40
-maxRadius 0
-maxExpRadius 0
-missileSpeed 2000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-explosionSpawn 9
-
-(Fire Crusher Ball)
-weaponNum 48
-attackType missile
-weaponTime 2000
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 250
-chargeAttack 1
-chargeAutoFire 0
-minCharge 3
-maxCharge 6
-damage 30
-splashDamage 30
-chargeDamageMult 5
-maxDamage 0
-radius 30
-explosionRadius 250
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 4000
-homing 0.5
-homingRange 4000
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(Surpreme Kai)
-weaponNum 50
-attackType missile
-weaponTime 1700
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 250
-chargeAttack 1
-chargeAutoFire 0
-minCharge 3
-maxCharge 6
-damage 30
-splashDamage 30
-chargeDamageMult 5
-maxDamage 0
-radius 30
-explosionRadius 250
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 4000
-homing 0.5
-homingRange 4000
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(Timed-Self-destuct)
-weaponNum 52
-attackType rdmissile
-weaponTime 1250
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 200
-chargeAttack 1
-chargeAutoFire 0
-minCharge 1
-maxCharge 6
-damage 15
-splashDamage 10
-chargeDamageMult 40
-maxDamage 80
-radius 30
-explosionRadius 120
-chargeRadiusMult 0
-chargeExpRadiusMult 40
-maxRadius 0
-maxExpRadius 0
-missileSpeed 850
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-explosionSpawn 53
-
-(Ssj4 goku's Cone Attack)
-weaponNum 65
-attackType hitscan
-weaponTime 100
-randomWeaponTime 0
-kiCostAsPct 1
-kiPct 0.010
-kiCost 0
-chargeAttack 0
-chargeAutoFire 0
-minCharge 0
-maxCharge 0
-damage 9
-splashDamage 0
-chargeDamageMult 0
-maxDamage 0
-radius 200
-explosionRadius 0
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 200
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 1
-priority 0
-blinding 0
-extraKnockback 200
-railTrail 0
-movementPenalty 0
-
-(Double beamer {quick bba missile})
-weaponNum 57
-attackType missile
-weaponTime 800
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 200
-chargeAttack 1
-chargeAutoFire 0
-minCharge 2
-maxCharge 6
-damage 30
-splashDamage 30
-chargeDamageMult 10
-maxDamage 0
-radius 75
-explosionRadius 400
-chargeRadiusMult 2
-chargeExpRadiusMult 2
-maxRadius 200
-maxExpRadius 0
-missileSpeed 4000
-homing 0.3
-homingRange 3000
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(blinding_flash_push)
-weaponNum 56
-attackType forcefield
-weaponTime 500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 100
-chargeAttack 1
-chargeAutoFire 0
-minCharge 2
-maxCharge 3
-damage 5
-splashDamage 0
-chargeDamageMult 0
-maxDamage 0
-radius 800
-explosionRadius 0
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 1
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 1
-extraKnockback 200
-railTrail 0
-movementPenalty 1
-
-(long light kame)
-weaponNum 58
-attackType beam
-weaponTime 2000
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 250
-chargeAttack 1
-chargeAutoFire 0
-minCharge 4
-maxCharge 6
-damage 5
-splashDamage 5
-chargeDamageMult 5
-maxDamage 50
-radius 30
-explosionRadius 350
-chargeRadiusMult 0
-chargeExpRadiusMult 200
-maxRadius 0
-maxExpRadius 0
-missileSpeed 2000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(Tri Beam)
-weaponNum 63
-attackType beam
-weaponTime 500
-randomWeaponTime 0
-kiCostAsPct 200
-kiPct 0
-kiCost 400
-chargeAttack 1
-chargeAutoFire 0
-minCharge 0
-maxCharge 6
-damage 5
-splashDamage 5
-chargeDamageMult 14
-maxDamage 100
-radius 50
-explosionRadius 100
-chargeRadiusMult 25
-chargeExpRadiusMult 200
-maxRadius 0
-maxExpRadius 700
-missileSpeed 2500
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(retard ki_blast)
-weaponNum 77
-attackType missile
-weaponTime 1000
+(mouthbeam)
+weaponNum 29
+attackType sbeam
+weaponTime 250
randomWeaponTime 0
kiCostAsPct 0
kiPct 0
-kiCost 4000
+kiCost 250
chargeAttack 0
chargeAutoFire 0
minCharge 0
maxCharge 0
-damage 20
-splashDamage 20
+damage 15
+splashDamage 15
chargeDamageMult 0
-maxDamage 0
-radius 20
-explosionRadius 125
+maxDamage 15
+radius 50
+explosionRadius 200
chargeRadiusMult 0
chargeExpRadiusMult 0
maxRadius 0
@@ -637,190 +23,7 @@ maxExpRadius 0
missileSpeed 1000
homing 0
homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(dragon)
-weaponNum 86
-attackType missile
-weaponTime 1400
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 850
-chargeAttack 1
-chargeAutoFire 0
-minCharge 3
-maxCharge 6
-damage 300
-splashDamage 200
-chargeDamageMult 0
-maxDamage 200
-radius 20
-explosionRadius 250
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 800
-homing 0.4
-homingRange 6000
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(light kame)
-weaponNum 94
-attackType beam
-weaponTime 500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 250
-chargeAttack 1
-chargeAutoFire 0
-minCharge 4
-maxCharge 6
-damage 5
-splashDamage 5
-chargeDamageMult 5
-maxDamage 50
-radius 30
-explosionRadius 350
-chargeRadiusMult 0
-chargeExpRadiusMult 200
-maxRadius 0
-maxExpRadius 0
-missileSpeed 2000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 1
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
-(Random Explosions)
-weaponNum 95
-attackType missile
-weaponTime 20
-kiCost 60
-radius 10
-damage 22
-splashDamage 18
-missilespeed 8000
-explosionRadius 100
-reflective 0
-priority 0
-randYOffset 300
-randXOffset 300
-randzoffset 200
-
-(ghost kamikaze)
-weaponNum 96
-attackType missile
-weaponTime 1400
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 120
-chargeAttack 1
-chargeAutoFire 0
-minCharge 4
-maxCharge 6
-damage 15
-splashDamage 20
-chargeDamageMult 5
-maxDamage 0
-radius 20
-explosionRadius 1
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 1000
-homing 1.0
-homingRange 4000
-homingAcceleration 1.4
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 500
-coneOfFireY 200
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-missileGravity 0
-missileAcceleration 0.96
-multishot 4
-bounces 0
-bounceFriction 0
-missileDuration 10000
-
-(STANDERED BEAM TIER 4) (Brolly-Gokssj3-Vegmajin ect...)
-weaponNum 79
-attackType beam
-weaponTime 1200
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 500
-chargeAttack 1
-minCharge 3
-maxCharge 6
-damage 15
-splashDamage 5
-chargeDamageMult 20
-maxDamage 80
-radius 25
-explosionRadius 100
-chargeRadiusMult 25
-chargeExpRadiusMult 120
-maxRadius 75
-maxExpRadius 0
-missileSpeed 2500
-homing 0
-homingRange 0
+homingAcceleration 0
range 0
loopingAnim 0
noAttackAnim 0
@@ -836,6 +39,12 @@ blinding 0
extraKnockback 0
railTrail 0
movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 0
+bounceFriction 0
+missileDuration 10000
(multiball)
weaponNum 100
@@ -981,6 +190,54 @@ bounces 0
bounceFriction 0
missileDuration 3000
+(ki_storm_bounces)
+weaponNum 103
+attackType missile
+weaponTime 200
+randomWeaponTime 0
+kiCostAsPct 1
+kiPct .020
+kiCost 0
+chargeAttack 0
+chargeAutoFire 0
+minCharge 0
+maxCharge 0
+damage 25
+splashDamage 25
+chargeDamageMult 0
+maxDamage 0
+radius 15
+explosionRadius 100
+chargeRadiusMult 0
+chargeExpRadiusMult 0
+maxRadius 0
+maxExpRadius 0
+missileSpeed 8000
+homing 0
+homingRange 0
+homingAcceleration 0
+range 0
+loopingAnim 1
+noAttackAnim 0
+alternatingXOffset 15
+randYOffset 25
+randXOffset 7
+coneOfFireX 0
+coneOfFireY 0
+piercing 0
+reflective 0
+priority 0
+blinding 0
+extraKnockback -25
+railTrail 0
+movementPenalty 0
+missileGravity 0
+missileAcceleration 0
+multishot 0
+bounces 1
+bounceFriction 0.95
+missileDuration 3000
+
(homingball_accel)
weaponNum 104
attackType missile
@@ -1078,128 +335,4 @@ bounceFriction 0.93
noZBounce 0.000001
missileDuration 2000
-(Timed-Self-destuct)
-weaponNum 106
-attackType rdmissile
-weaponTime 1250
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 200
-chargeAttack 1
-chargeAutoFire 0
-minCharge 1
-maxCharge 6
-damage 15
-splashDamage 10
-chargeDamageMult 40
-maxDamage 80
-radius 30
-explosionRadius 120
-chargeRadiusMult 0
-chargeExpRadiusMult 40
-maxRadius 0
-maxExpRadius 0
-missileSpeed 850
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-explosionSpawn 53
-
-(aga2)
-weaponNum 107
-attackType forcefield
-weaponTime 200
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 200
-chargeAttack 1
-chargeAutoFire 1
-minCharge 3
-maxCharge 0
-damage 20
-splashDamage 0
-chargeDamageMult 0
-maxDamage 0
-radius 900
-explosionRadius 0
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 0
-homing 0
-homingRange 0
-range 0
-loopingAnim 1
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 1
-priority 2
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 2
-
-(Very LOOOONG BEAM)
-weaponNum 108
-attackType beam
-weaponTime 5500
-randomWeaponTime 0
-kiCostAsPct 0
-kiPct 0
-kiCost 500
-chargeAttack 1
-chargeAutoFire 0
-minCharge 4
-maxCharge 6
-damage 100
-splashDamage 80
-chargeDamageMult 5
-maxDamage 110
-radius 20
-explosionRadius 190
-chargeRadiusMult 0
-chargeExpRadiusMult 0
-maxRadius 0
-maxExpRadius 0
-missileSpeed 3000
-homing 0
-homingRange 0
-range 0
-loopingAnim 0
-noAttackAnim 0
-alternatingXOffset 0
-randYOffset 0
-randXOffset 0
-coneOfFireX 0
-coneOfFireY 0
-piercing 0
-reflective 0
-priority 0
-blinding 0
-extraKnockback 0
-railTrail 0
-movementPenalty 0
-
end
diff --git a/cfgs/default.cfg b/cfgs/default.cfg
index d7ef398..05a9b26 100644
--- a/cfgs/default.cfg
+++ b/cfgs/default.cfg
@@ -1,44 +1,39 @@
-attackName 0 "Kikou"
-attackName 1 "Sokidan"
-attackName 2 "Renzoku Energy Dan"
-attackName 3 "Masenko"
-attackName 4 "Angry Gohan Attack"
+attackName 0 "Ki Blast"
+attackName 1 "Tornado Blast"
+attackName 2 "Sky Seeker"
+attackName 3 "Earth Seeker"
+attackName 4 "Heaven's Wrath"
attackIcon 0 "icons/kiblast"
-attackIcon 1 "icons/lightningball"
-attackIcon 2 "icons/lstorm_icon"
-attackIcon 3 "icons/mantisblast"
-attackIcon 4 "icons/aga"
+attackIcon 1 "icons/tornadoblast"
+attackIcon 2 "icons/redattack_icon"
+attackIcon 3 "icons/earthseeker_icon"
+attackIcon 4 "icons/powerwaveblast"
missileSound 0 "sound/weapons/rocket/rockfly.wav"
-missileSound 1 "sound/bfp/homingballfly.wav"
-missileSound 2 "sound/weapons/rocket/rockfly.wav"
+missileSound 2 "sound/bfp/homingballfly.wav"
missileSound 3 "sound/weapons/rocket/rockfly.wav"
+missileSound 4 "sound/weapons/rocket/rockfly.wav"
flashSound 0 "sound/weapons/bfg/bfg_fire.wav"
-flashSound 1 "sound/bfp/homingballfire.wav"
-flashSound 2 "sound/weapons/bfg/bfg_fire.wav"
-firingSound 3 "sound/weapons/rocket/rockfly.wav"
+flashSound 1 "sound/weapons/bfg/bfg_fire.wav"
+flashSound 2 "sound/bfp/homingballfire.wav"
+flashSound 3 "sound/weapons/bfg/bfg_fire.wav"
+chargeSound 2 "sound/bfp/attackcharge1.wav"
+chargeSound 4 "sound/bfp/attackcharge1.wav"
+firingSound 1 "sound/weapons/rocket/rockfly.wav"
firingSound 4 "sound/weapons/rocket/rockfly.wav"
-chargeSound 1 "sound/bfp/attackcharge1.wav"
-chargeSound 3 "sound/bfp/attackcharge1.wav"
-
-attackFireVoice 0 "sound\player\bfp2-gohan_ssj\goh_ka.wav"
-attackChargeVoice 1 0 "sound\player\bfp2-gohan_ssj\goh_whyu.wav"
-attackFireVoice 2 "sound\player\bfp2-gohan_ssj\goh_ka.wav"
-attackChargeVoice 3 1 "sound\player\bfp2-gohan_ssj\goh_daddy.wav"
-attackFireVoice 3 "sound\player\bfp2-gohan_ssj\goh_masenko.wav"
-attackFireVoice 4 "sound\player\bfp2-gohan_ssj\goh_screamki.wav"
+constantFireAttack 1 1
attackTag 0 "tag_left"
-attackTagPart 0 torso
+attackTagPart 0 "torso"
attackTag 1 "tag_right"
-attackTagPart 1 torso
+attackTagPart 1 "torso"
attackTag 2 "tag_right"
-attackTagPart 2 torso
-attackTag 3 "tag_left"
-attackTagPart 3 torso
+attackTagPart 2 "torso"
+attackTag 3 "tag_right"
+attackTagPart 3 "torso"
attackTag 4 "tag_right"
-attackTagPart 4 torso
+attackTagPart 4 "torso"
missileShader 0 "KiBlastAttackShader"
missileModel 0 "models/weaphits/kiblast.md3"
@@ -48,34 +43,34 @@ missileDlight 0 200
missileDlightColor 0 1 0.75 0
missileModelRotation 0 0.2
-missileShader 1 "LightningBallAttackShader"
-missileRadius 1 50
-missileRotation 1 15
-missileDlight 1 200
-missileDlightColor 1 1 0.5 0
-flashShader 1 "LightningBallChargeShader"
-flashRadius 1 25
-
-missileShader 2 "lightning_storm"
-missileRadius 2 30
+missileShader 2 "redattack"
+missileRadius 2 50
missileRotation 2 15
-missileTrailFunc 2 "rocket"
missileDlight 2 200
-missileDlightColor 2 0 0 0.75
-missileModelRotation 2 0.2
+missileDlightColor 2 1 0 0.5
+flashShader 2 "redattack"
+flashRadius 2 25
-missileShader 3 "MantisAttackShader"
-beamShader 3 "MantisBeamShader"
-missileRadius 3 75
+missileShader 3 "earth_seeker"
+missileRadius 3 30
missileRotation 3 15
-missileTrailFunc 3 "beam"
+missileTrailFunc 3 "rocket"
missileDlight 3 200
-missileDlightColor 3 1 1 0.5
-flashShader 3 "MantisFlashShader"
-flashRadius 3 15
-flashScaleFactor 3 2
-firingFlashRadius 3 30
-firingFlashScaleFactor 3 1.4
+missileDlightColor 3 1 0 0.3
+missileModelRotation 3 0.2
+
+missileShader 4 "PowerWaveAttackShader"
+beamShader 4 "PowerWaveBeamShader"
+missileRadius 4 0
+missileRadiusChargeMult 4 75
+missileTrailFunc 4 "beam"
+missileDlight 4 200
+missileDlightColor 4 0.18 0.77 1
+flashShader 4 "PowerWaveFlashShader"
+flashRadius 4 20
+flashScaleFactor 4 0
+firingFlashRadius 4 50
+firingFlashScaleFactor 4 1.5
noExplosion 0 0
noExplosionSound 0 0
@@ -96,42 +91,23 @@ explosionRingScaleFactorChargeMult 0 0
explosionShellScaleFactor 0 0.95
explosionShellScaleFactorChargeMult 0 0
-noExplosion 1 0
-noExplosionSound 1 0
-explosionRing 1 1
-explosionShell 1 1
-explosionModel 1 "models/weaphits/sphere_hi.md3"
-explosionShader 1 "LightningBallExplosionShader"
-explosionRocks 1 10
-explosionSmoke 1 1
-explosionSparks 1 20
-explosionSmokeRadius 1 200
-explosionSmokeLife 1 1500
-explosionSmokeSpeed 1 10
-explosionScaleFactor 1 3
-explosionScaleFactorChargeMult 1 0
-explosionRingScaleFactor 1 3
-explosionRingScaleFactorChargeMult 1 0
-explosionShellScaleFactor 1 6
-explosionShellScaleFactorChargeMult 1 0
+noExplosion 1 1
noExplosion 2 0
noExplosionSound 2 0
explosionRing 2 1
explosionShell 2 1
-explosionModel 2 "models/weaphits/sphere_hi.md3"
-explosionShader 2 "LStormExplosionShader"
explosionRocks 2 10
explosionSmoke 2 1
explosionSparks 2 20
explosionSmokeRadius 2 200
-explosionSmokeLife 2 500
+explosionSmokeLife 2 1500
explosionSmokeSpeed 2 10
-explosionScaleFactor 2 0.95
+explosionScaleFactor 2 3
explosionScaleFactorChargeMult 2 0
-explosionRingScaleFactor 2 0.95
+explosionRingScaleFactor 2 3
explosionRingScaleFactorChargeMult 2 0
-explosionShellScaleFactor 2 0.95
+explosionShellScaleFactor 2 6
explosionShellScaleFactorChargeMult 2 0
noExplosion 3 0
@@ -139,24 +115,38 @@ noExplosionSound 3 0
explosionRing 3 1
explosionShell 3 1
explosionModel 3 "models/weaphits/sphere_hi.md3"
-explosionShader 3 "MantisExplosionShader"
+explosionShader 3 "earth_seeker_explosion"
explosionRocks 3 10
-explosionSmoke 3 3
+explosionSmoke 3 1
explosionSparks 3 20
explosionSmokeRadius 3 200
-explosionSmokeLife 3 1500
+explosionSmokeLife 3 500
explosionSmokeSpeed 3 10
-explosionScaleFactor 3 3
-explosionScaleFactorChargeMult 3 1
-explosionRingScaleFactor 3 4
-explosionRingScaleFactorChargeMult 3 1
-explosionShellScaleFactor 3 6
-explosionShellScaleFactorChargeMult 3 1
+explosionScaleFactor 3 0.95
+explosionScaleFactorChargeMult 3 0
+explosionRingScaleFactor 3 0.95
+explosionRingScaleFactorChargeMult 3 0
+explosionShellScaleFactor 3 0.95
+explosionShellScaleFactorChargeMult 3 0
-noExplosion 4 1
+noExplosion 4 0
+noExplosionSound 4 0
+explosionRing 4 1
+explosionShell 4 1
explosionModel 4 "models/weaphits/sphere_hi.md3"
-explosionShader 4 "AGAAttackShader"
-explosionScaleFactor 4 1
-explosionScaleFactorChargeMult 4 2
+explosionShader 4 "PowerWaveExplosionShader"
+explosionRocks 4 10
+explosionSmoke 4 3
+explosionSparks 4 20
+explosionSmokeRadius 4 200
+explosionSmokeLife 4 1500
+explosionSmokeSpeed 4 10
+explosionScaleFactor 4 3
+explosionScaleFactorChargeMult 4 1
+explosionRingScaleFactor 4 4
+explosionRingScaleFactorChargeMult 4 1
+explosionShellScaleFactor 4 6
+explosionShellScaleFactorChargeMult 4 1
end
+
diff --git a/cfgs/q3config.cfg b/cfgs/q3config.cfg
deleted file mode 100644
index ed45439..0000000
--- a/cfgs/q3config.cfg
+++ /dev/null
@@ -1,333 +0,0 @@
-// generated by quake, do not modify
-unbindall
-bind TAB "+scores"
-bind ENTER "+button2"
-bind ESCAPE "togglemenu"
-bind SPACE "+moveup"
-bind + "sizeup"
-bind - "sizedown"
-bind / "weapnext"
-bind 0 "weapon 10"
-bind 1 "weapon 1bind 2 weapon 2bind 3 weapon 3bind 4 weapon 4bind 5 weapon 5"
-bind 2 "weapon 2"
-bind 3 "weapon 3"
-bind 4 "weapon 4"
-bind 5 "weapon 5"
-bind 6 "weapon 6"
-bind 7 "weapon 7"
-bind 8 "weapon 8"
-bind 9 "weapon 9"
-bind = "sizeup"
-bind [ "weapprev"
-bind \ "+mlook"
-bind ] "weapnext"
-bind _ "sizedown"
-bind ` "toggleconsole"
-bind a "+moveleft"
-bind b "vstr piccolo"
-bind c "+movedown"
-bind d "+moveright"
-bind f "+button12"
-bind m "vstr ginu"
-bind n "vstr goku"
-bind o "kiusetoggle"
-bind s "+back"
-bind t "messagemode"
-bind w "+forward"
-bind ~ "toggleconsole"
-bind CAPSLOCK "+button9"
-bind PAUSE "pause"
-bind UPARROW "+forward"
-bind DOWNARROW "+back"
-bind LEFTARROW "+left"
-bind RIGHTARROW "+right"
-bind ALT "+button7"
-bind CTRL "+button10"
-bind SHIFT "+button8"
-bind DEL "+lookdown"
-bind PGDN "+lookup"
-bind END "centerview"
-bind F1 "vote yes"
-bind F2 "vote no"
-bind F3 "ui_teamorders"
-bind F4 "vstr vegeta"
-bind F5 "vstr beb"
-bind F6 "vstr beb"
-bind F7 "vstr gotenks"
-bind F8 "vstr trunks"
-bind F9 "vstr freeza1"
-bind F10 "vstr cell"
-bind F11 "screenshot"
-bind F12 "vstr freeza1"
-bind MOUSE1 "+attack"
-bind MOUSE2 "+button9"
-bind MOUSE3 "+zoom"
-bind MWHEELDOWN "weapnext"
-bind MWHEELUP "weapprev"
-seta g_allowSpectatorChat "1"
-seta g_doWarmup "0"
-seta cg_lowpolysphere "0"
-seta cg_lifedeaths "153"
-seta cg_lifekills "109"
-seta cg_stfu "0"
-seta cg_superdeformed "0"
-seta cg_permaglowUltimate "1"
-seta cg_smallOwnAura "0"
-seta cg_spriteAura "0"
-seta cg_simpleHUD "0"
-seta cg_lightweightAuras "0"
-seta ui_bigFont "0.4"
-seta ui_smallFont "0.25"
-seta cg_noTaunt "0"
-seta cg_smoothClients "0"
-seta cg_scorePlums "1"
-seta cg_cameraOrbitDelay "50"
-seta cg_noVoiceText "0"
-seta cg_noVoiceChats "0"
-seta cg_teamChatsOnly "0"
-seta cg_drawFriend "1"
-seta cg_drawTeamOverlay "0"
-seta cg_deferPlayers "1"
-seta cg_forceSkin "0"
-seta cg_forceModel "0"
-seta cg_teamChatHeight "0"
-seta cg_teamChatTime "3000"
-seta cg_bobroll "0.002"
-seta cg_bobpitch "0.002"
-seta cg_bobup "0.005"
-seta cg_runroll "0.005"
-seta cg_runpitch "0.002"
-seta cg_railTrailTime "400"
-seta cg_lagometer "1"
-seta cg_simpleItems "0"
-seta cg_crosshairY "0"
-seta cg_crosshairX "0"
-seta cg_crosshairHealth "1"
-seta cg_crosshairSize "24"
-seta cg_drawRewards "1"
-seta cg_drawAttacker "1"
-seta cg_drawAmmoWarning "1"
-seta cg_drawIcons "1"
-seta cg_draw3dIcons "0"
-seta cg_drawSnapshot "0"
-seta cg_drawFPS "0"
-seta cg_drawTimer "0"
-seta cg_drawStatus "1"
-seta cg_draw2D "1"
-seta cg_gibs "1"
-seta cg_stereoSeparation "0.4"
-seta cg_fov "90"
-seta cg_zoomfov "22.5"
-seta cg_drawGun "1"
-seta cg_playHitSound "0"
-seta cg_flytilt "1"
-seta cg_lightExplosions "1"
-seta cg_lightAuras "1"
-seta cg_transformationAura "1"
-seta cg_thirdPersonRange "110"
-seta cg_thirdPersonHeight "-60"
-seta g_hitStun "1"
-seta g_basePL "756"
-seta cg_explosionRing "1"
-seta cg_stableCrosshair "0"
-seta cg_musicUnpacked "1"
-seta cg_crosshairColor "7"
-seta cg_particles "1"
-seta cg_explosionSmoke "1"
-seta cg_explosionShell "1"
-seta cg_drawKiWarning "1"
-seta cg_chargeupAlert "1"
-seta cg_bigExplosions "1"
-seta cg_beamTrail "25"
-seta cg_kiTrail "50"
-seta cg_highPolyAura "1"
-seta cg_polygonAura "1"
-seta cg_drawOwnModel "0"
-seta cg_fixedThirdPerson "1"
-seta cg_thirdPerson "1"
-seta cg_shadows "1"
-seta sv_pure "1"
-seta net_socksPassword ""
-seta net_socksUsername ""
-seta net_socksPort "1080"
-seta net_socksServer ""
-seta net_socksEnabled "0"
-seta net_noipx "0"
-seta net_noudp "0"
-seta server16 ""
-seta server15 ""
-seta server14 ""
-seta server13 ""
-seta server12 ""
-seta server11 ""
-seta server10 ""
-seta server9 ""
-seta server8 ""
-seta server7 ""
-seta server6 ""
-seta server5 ""
-seta server4 ""
-seta server3 ""
-seta server2 ""
-seta server1 ""
-seta cg_marks "1"
-seta cg_drawCrosshairNames "1"
-seta cg_drawCrosshair "3"
-seta cg_brassTime "2500"
-seta ui_browserShowEmpty "1"
-seta ui_browserShowFull "1"
-seta ui_browserSortKey "4"
-seta ui_browserGameType "0"
-seta ui_browserMaster "1"
-seta g_spSkill "2"
-seta g_spVideos ""
-seta g_spAwards ""
-seta g_spScores5 ""
-seta g_spScores4 ""
-seta g_spScores3 ""
-seta g_spScores2 ""
-seta g_spScores1 ""
-seta ui_ctf_friendly "0"
-seta ui_ctf_timelimit "30"
-seta ui_ctf_capturelimit "8"
-seta ui_team_friendly "1"
-seta ui_team_timelimit "20"
-seta ui_team_fraglimit "0"
-seta ui_tourney_timelimit "15"
-seta ui_tourney_fraglimit "0"
-seta ui_ffa_timelimit "0"
-seta ui_ffa_fraglimit "35"
-seta com_soundMegs "8"
-seta s_mixPreStep "0.05"
-seta s_mixahead "0.2"
-seta s_khz "22"
-seta s_doppler "1"
-seta s_separation "0.5"
-seta s_musicvolume "0.25"
-seta s_volume "0.8"
-seta vid_ypos "22"
-seta vid_xpos "3"
-seta r_lastValidRenderer "GeForce 6700 XL/PCI/SSE2/3DNOW!"
-seta r_primitives "0"
-seta r_railSegmentLength "32"
-seta r_railCoreWidth "15"
-seta r_railWidth "16"
-seta r_facePlaneCull "1"
-seta r_gamma "1"
-seta r_swapInterval "0"
-seta r_textureMode "GL_LINEAR_MIPMAP_NEAREST"
-seta r_finish "0"
-seta r_dlightBacks "1"
-seta r_dynamiclight "1"
-seta r_drawSun "0"
-seta r_fastsky "0"
-seta r_ignoreGLErrors "1"
-seta r_flares "0"
-seta r_lodbias "0"
-seta r_lodCurveError "250"
-seta r_ignoreFastPath "1"
-seta r_smp "0"
-seta r_subdivisions "4"
-seta r_vertexLight "0"
-seta r_simpleMipMaps "1"
-seta r_customaspect "1"
-seta r_customheight "1024"
-seta r_customwidth "1600"
-seta r_fullscreen "1"
-seta r_mode "3"
-seta r_ignorehwgamma "0"
-seta r_overBrightBits "1"
-seta r_depthbits "0"
-seta r_stencilbits "8"
-seta r_stereo "0"
-seta r_colorbits "0"
-seta r_texturebits "0"
-seta r_detailtextures "1"
-seta r_roundImagesDown "1"
-seta r_picmip "1"
-seta r_ext_texture_env_add "1"
-seta r_ext_compiled_vertex_array "1"
-seta r_ext_multitexture "1"
-seta r_ext_gamma_control "1"
-seta r_ext_compressed_textures "0"
-seta r_allowExtensions "1"
-seta r_glDriver "opengl32"
-seta cg_viewsize "100"
-seta cg_predictItems "1"
-seta cl_punkbuster "0"
-seta cl_anonymous "0"
-seta sex "male"
-seta handicap "100"
-seta color2 "5"
-seta color1 "4"
-seta g_blueTeam "Pagans"
-seta g_redTeam "Stroggs"
-seta team_headmodel "*james"
-seta team_model "james"
-seta headmodel "bfp1-freeza"
-seta model "bfp3-gokussj5/default"
-seta snaps "20"
-seta rate "25000"
-seta name "EBFP Freak"
-seta cl_maxPing "800"
-seta m_filter "0"
-seta m_side "0.25"
-seta m_forward "0.25"
-seta m_yaw "0.022"
-seta m_pitch "0.022000"
-seta cg_autoswitch "1"
-seta r_inGameVideo "1"
-seta cl_allowDownload "0"
-seta cl_freelook "1"
-seta cl_mouseAccel "0"
-seta sensitivity "5"
-seta cl_run "1"
-seta cl_packetdup "1"
-seta cl_maxpackets "30"
-seta cl_pitchspeed "140"
-seta cl_yawspeed "140"
-seta sv_strictAuth "1"
-seta sv_lanForceRate "1"
-seta sv_master5 ""
-seta sv_master4 ""
-seta sv_master3 ""
-seta sv_master2 ""
-seta sv_floodProtect "1"
-seta sv_maxPing "0"
-seta sv_minPing "0"
-seta sv_maxRate "0"
-seta sv_punkbuster "0"
-seta sv_hostname "English Bid for Power 2.0"
-seta vm_ui "2"
-seta vm_game "2"
-seta vm_cgame "2"
-seta joy_threshold "0.150000"
-seta in_joyBallScale "0.02"
-seta in_joystick "0"
-seta in_logitechbug "0"
-seta in_mouse "1"
-seta in_mididevice "0"
-seta in_midichannel "1"
-seta in_midiport "1"
-seta in_midi "0"
-seta com_introplayed "1"
-seta com_blood "1"
-seta com_maxfps "85"
-seta com_hunkMegs "150"
-seta dmflags "0"
-seta fraglimit "35"
-seta timelimit "0"
-seta sv_maxclients "8"
-seta cm_playerCurveClip "1"
-seta g_maxGameClients "0"
-seta capturelimit "0"
-seta g_friendlyFire "0"
-seta g_teamAutoJoin "0"
-seta g_teamForceBalance "0"
-seta g_warmup "20"
-seta g_log "games.log"
-seta g_logSync "0"
-seta g_banIPs ""
-seta g_filterBan "1"
-seta g_allowVote "1"
-seta com_zoneMegs "16"
diff --git a/docs/Create_Custom_Models.md b/docs/Create_Custom_Models.md
index 667ac99..823450e 100644
--- a/docs/Create_Custom_Models.md
+++ b/docs/Create_Custom_Models.md
@@ -297,6 +297,11 @@ treated as a sprite. Has no effect for hitscan attacks.
The rotation of the missile.
+- ```missileModelRotation [attack index] [float]```
+
+The rotation of the missile model. Set with float number: between 0.1 and 999.0 ...
+
+
- ```missileSpinHoriz [attack index] [0/1]```
Used to switch the axis of rotation on the missile.
diff --git a/docs/bfp_attacksets_config_file.md b/docs/bfp_attacksets_config_file.md
new file mode 100644
index 0000000..f797f0f
--- /dev/null
+++ b/docs/bfp_attacksets_config_file.md
@@ -0,0 +1,29 @@
+# Attacksets Config File
+
+The attackset config file (*bfp_attacksets.cfg*) is used to set attacks to every player model prefix.
+Reads *bfp_attacksets.cfg* once in the server at the start after reading *bfp_weapon.cfg*.
+
+### attackset
+
+- ```attackset [int]```
+Attackset identifier for this group.
+
+### attack
+
+- ```attack [attack index] [weaponNum]```
+Sets the attack in this index, weaponNum is the attack identifier where helds attack properties.
+
+### modelPrefix
+
+- ```modelPrefix [string]```
+Sets prefix for this player model group which starts with this prefix.
+
+### defaultModel
+
+- ```defaultModel [string]```
+Sets default player model if it loads by using only the [modelPrefix](#modelPrefix) string in console.
+
+### end
+
+- ```end```
+Should be the last word in the *bfp_attacksets.cfg* file.
\ No newline at end of file
diff --git a/docs/bfp_cvars_task.md b/docs/bfp_cvars_task.md
deleted file mode 100644
index 3b8ccef..0000000
--- a/docs/bfp_cvars_task.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# BFP CVARS
-
-## PENDING:
-
-- g_basepl [1-999]: set the starting power level from one thousand to one mil.
-- g_blockLength = "3"
-- g_blockDelay = "2"
-- g_kiChargePct = "15"
-- g_kiCharge = "0"
-- g_kiRegenPct = "0.6"
-- g_kiRegen = "0"
-- g_blockCostPct = "3"
-- g_blockCost = "0"
-- g_boostCostPct = "0"
-- g_boostCost = "150"
-- g_flightCostPct = "0"
-- g_flightCost = "50"
-- g_chargeDelay = "250"
-- g_meleeRange = "32"
-- g_meleeDiveRange = "700"
-- g_meleeDamage = "10"
-
-#### Cvar Gametypes:
-
-- g_gametype 3 = sur (survival)
-- g_gametype 4 = ooz (oozaru, in original BFP, that hasn't been added on UI, but add it anyway)
-- g_gametype 5 = tdm (team deathmatch) // originally, in Q3, this number is 3
-- g_gametype 6 = lms (last man standing)
-- g_gametype 7 = ctf (capture the flag) // originally, in Q3, this number is 4
-
-... and maybe more, look in [cvar_bfp_list.txt](docs/cvar_bfp_list.txt)
-
-
-## WIP:
-
-- g_hitStun [0/1]: turn on or off the melee hit stun.
-- cg_drawKiWarning [0/1]: turn on or off the low ki warning.
-
-#### Cvar Gametypes:
-
-- g_gametype 2 = single player (in original BFP, that doesn't show in UI, so make Single Player option hidden)
-
-## DONE BUT NOT IN CGAME:
-
-- cg_stfu [0/1]: disable character voices when firing attacks.
-- cg_lowpolysphere [0/1]: force the use of a lower polycount sphere.
-- cg_kitrail [0-99]: set the length of the ki trail. 0 turns it off.
-- cg_lightauras [0/1]: turn on or off the aura dynamic lights.
-- cg_lightexplosions [0/1]: turn on or off the explosion dynamic lights.
-- cg_chargeupAlert [0/1]: turn on or off the "ready" message when charging attacks.
-- cg_explosionShell [0/1]: turn on or off the explosion shell.
-- cg_explosionSmoke [0/1]: turn on or off the explosion smoke.
-- cg_explosionRing [0/1]: turn on or off the explosion ring.
-- cg_particles [0/1]: turn on or off particle effects.
-- cg_playHitSound [0/1]: turn on or off the q3 hit "ping".
-
-
-## COMPLETED:
-
-- [x] ~~cg_yrgolroxor~~
-- [x] ~~cg_thirdPersonHeight~~
-- [x] ~~cg_fixedThirdPerson~~
-- [x] ~~cg_drawOwnModel~~
-- [x] ~~cg_stableCrosshair~~
-- [x] ~~cg_musicUnpacked~~
-- [x] ~~cg_crosshairColor~~
-- [x] ~~cg_crosshairHealth~~
-- [x] ~~cg_flytilt~~
-
-#### Cvar Gametypes:
-
-- [x] ~~g_gametype 0 = dm (aka FFA)~~ (already in Q3 code)
-- [x] ~~g_gametype 1 = 1v1 (tournament)~~ (already in Q3 code)
\ No newline at end of file
diff --git a/docs/bfp_networking.md b/docs/bfp_networking.md
new file mode 100644
index 0000000..420412a
--- /dev/null
+++ b/docs/bfp_networking.md
@@ -0,0 +1,191 @@
+The following stuff is used in original BFP networking and has been found using engine code. Although optimizations or ways to not use network flags or values are found, the original flags and indexes must be present to see how it was envisioned.
+
+* PMF_ flags: the same as Quake 3
+* EF_ flags: the same as Quake 3
+* STAT_ indexes:
+ * STAT_0 = health
+ * STAT_3 = armor
+ * STAT_4 = dead yaw
+ * STAT_5 = bit mask for client ready intermission
+ * STAT_6 = powerlevel
+ * STAT_7 = flight jump anim transition seconds, the maximum is until 21 sec and stops changing to 0, even when stop flying also reproduces this stat index like starting to fly (looks weird)
+ * STAT_8 = ki
+ * STAT_9 = maximum ki
+ * STAT_10 = melee attack time
+ * STAT_11 = a weird timer with 2 msec appears in the first 3 msec of melee attack time (STAT_10) when melee attack time is starting from 25 msec (what the hell?)
+ * STAT_13 = active firing state (for rdmissile and beam attack types with attackCharge)
+ * STAT_14 = force field weapon state
+ * STAT_15 = fly tilt: moving left is lesser than 0 until -80, moving right is more than 0 until 80
+* PERS_ (persistant) indexes:
+ * PERS_0 = score
+ * PERS_1 = total points damage inflicted so damage beeps can sound on change
+ * PERS_2 = rank
+ * PERS_3 = player team
+ * PERS_4 = spawn count (respawn)
+ * PERS_7 = health/armor of last person we attacked
+ * PERS_8 = count of the number of times you died
+ * PERS_11 = defend awards (defending the flag in CTF)
+ * PERS_13 = ??? (Q3: kills with the guantlet)
+ * PERS_14 = powerlevel
+ * PERS_15 = ??? (it appears when spawning at the first time of all in-game)
+* PW_ (powerups) indexes:
+ * PW_0 = that's where PW_HASTE and PW_BATTLESUIT are marked as PW_NONE after picking up
+ * PW_1 = PW_QUAD
+ * PW_2 = PW_REDFLAG placed on PW_BATTLESUIT
+ * PW_3 = PW_BLUEFLAG placed on PW_HASTE
+ * PW_5 = PW_FLIGHT reused for flight enabled/disabled
+ * PW_6 = ki recharge
+ * PW_7 = ki use/toggle
+ * PW_8 = blocking seconds (to defend yourself from melee, ki attacks: beams, projectiles, ...)
+ * PW_9 = melee toggle
+ * PW_10 = hit stun
+ * PW_11 = ki attack charge points
+ * PW_12 = monster flag (g_gametype 4)
+ * PW_13 = beam firing state?
+ * PW_14 = jump (what the hell?)
+* WP_ (weapons) indexes:
+ * WP_0, WP_1, WP_2, WP_3 and WP_4 are the weapons in the attack selection
+ * WP_4 (WP_GRENADE_LAUNCHER) = as the last ki attack, but also when the player is being attacked or damaged, begins from 2000 as milliseconds until 0, looks like a timer, the purpose remains unknown
+ * WP_5 (WP_ROCKET_LAUNCHER) = ki recharge time to enable
+ * WP_6 (WP_LIGHTNING) = hit stun delay (after receiving a hit stun)
+ * WP_7 (WP_RAILGUN) = block delay (after using block)
+ * WP_8 (WP_PLASMAGUN) = ki use/boost toogle
+ * WP_9 (WP_BFG) = flight toggle key control (to avoid spamming the enable flight key, just enable once and disable once)
+ * WP_10 (WP_GRAPPLING_HOOK) = blind seconds
+ * WP_11 = for rapid ki attacks like ki storm (alternates: -1 and 1)
+ * WP_13 = toggles if the player can use zanzoken or not
+ * WP_14 = directional left and right keys to move left or right while pressing, adds time msec, not sure if that's a timer to handle WP_13 for zanzoken
+ * WP_15 = enables beam struggle
+
+* EV_* (events) indexes:
+```c
+ * EV_NONE // 0
+ * EV_UNUSED_INDEX1 // 1
+ * EV_UNUSED_INDEX2 // 2
+ * EV_UNUSED_INDEX3 // 3
+ * EV_UNUSED_INDEX4 // 4
+ * EV_UNUSED_INDEX5 // 5
+ * EV_UNUSED_INDEX6 // 6
+ * EV_UNUSED_INDEX7 // 7
+ * EV_UNUSED_INDEX8 // 8
+ * EV_UNUSED_INDEX9 // 9
+ * EV_MELEE_READY // 10, preparing melee
+ * EV_MELEE // 11, melee attack
+ * EV_UNUSED_INDEX12 // 12
+ * EV_TIER_RESET // 13, reset tier when the player respawns and changes to the default or a bit less ki energy?
+ * EV_TIER_0 // 14, EV_TIER_0-4 (14-18), when the player frags, increases their PL and obtains a new skill (in the last tier, transforms)
+ * EV_TIER_1 // 15
+ * EV_TIER_2 // 16
+ * EV_TIER_3 // 17
+ * EV_TIER_4 // 18
+ * EV_ZANZOKEN_IN // 19, Short-Range Teleport (Zanzoken)
+ * EV_ZANZOKEN_OUT // 20, stop/leaves zanzoken
+ * EV_KI_BOOST // 21, enables aura/ki trail
+ * EV_ENABLE_FLIGHT // 22, enable flight
+ * EV_FOOTSTEP // 23
+ * EV_FOOTSTEP_METAL // 24
+ * EV_FOOTSPLASH // 25
+ * EV_FOOTWADE // 26
+ * EV_SWIM // 27
+ * EV_STEP_4 // 28
+ * EV_STEP_8 // 29
+ * EV_STEP_12 // 30
+ * EV_STEP_16 // 31
+ * EV_FALL_SHORT // 32
+ * EV_FALL_MEDIUM // 33
+ * EV_FALL_FAR // 34
+ * EV_JUMP_PAD // 35
+ * EV_JUMP // 36
+ * EV_JUMP_2 // 37
+ * EV_WATER_TOUCH // 38
+ * EV_WATER_LEAVE // 39
+ * EV_WATER_UNDER // 40
+ * EV_WATER_CLEAR // 41
+ * EV_ITEM_PICKUP // 42
+ * EV_GLOBAL_ITEM_PICKUP // 43
+ * EV_NOAMMO // 44
+ * EV_CHANGE_WEAPON // 45
+ * EV_FIRE_WEAPON // 46
+ * EV_USE_ITEM0 // 47
+ * EV_USE_ITEM1 // 48
+ * EV_USE_ITEM2 // 49
+ * EV_USE_ITEM3 // 50
+ * EV_USE_ITEM4 // 51
+ * EV_USE_ITEM5 // 52
+ * EV_USE_ITEM6 // 53
+ * EV_USE_ITEM7 // 54
+ * EV_USE_ITEM8 // 55
+ * EV_USE_ITEM9 // 56
+ * EV_USE_ITEM10 // 57
+ * EV_USE_ITEM11 // 58
+ * EV_USE_ITEM12 // 59
+ * EV_USE_ITEM13 // 60
+ * EV_USE_ITEM14 // 61
+ * EV_USE_ITEM15 // 62
+ * EV_ITEM_RESPAWN // 63
+ * EV_ITEM_POP // 64
+ * EV_PLAYER_TELEPORT_IN // 65
+ * EV_PLAYER_TELEPORT_OUT // 66
+ * EV_GRENADE_BOUNCE // 67
+ * EV_GENERAL_SOUND // 68
+ * EV_GLOBAL_SOUND // 69
+ * EV_GLOBAL_TEAM_SOUND // 70
+ * EV_BULLET_HIT_FLESH // 71
+ * EV_BULLET_HIT_WALL // 72
+ * EV_MISSILE_HIT // 73
+ * EV_MISSILE_MISS // 74
+ * EV_MISSILE_MISS_METAL // 75
+ * EV_MISSILE_DETONATE // 76, in some moment, ki attack beam/projectile
+ * EV_RAILTRAIL // 77
+ * EV_SHOTGUN // 78
+ * EV_UNUSED_INDEX79 // 79
+ * EV_PAIN // 80
+ * EV_DEATH1 // 81
+ * EV_DEATH2 // 82
+ * EV_DEATH3 // 83
+ * EV_OBITUARY // 84
+ * EV_POWERUP_QUAD // 85
+ * EV_POWERUP_BATTLESUIT // 86
+ * EV_UNUSED_INDEX87 // 87
+ * EV_GIB_PLAYER // 88
+ * EV_UNUSED_INDEX89 // 89
+ * EV_UNUSED_INDEX90 // 90
+ * EV_SCOREPLUM // 91
+ * EV_TAUNT // 92
+ * EV_UNUSED_INDEX93 // 93
+ * EV_UNUSED_INDEX94 // 94
+ * EV_UNUSED_INDEX95 // 95
+ * EV_UNUSED_INDEX96 // 96
+ * EV_DEBUG_LINE // 97
+ * EV_STOPLOOPINGSOUND // 98
+ * EV_BEAM_STRUGGLE // 99, beam struggle
+```
+
+* bg_itemlist indexes:
+```c
+ * NULL // 0
+ * item_armor_shard // 1
+ * item_armor_combat // 2
+ * item_armor_body // 3
+ * item_health_small // 4
+ * item_health // 5
+ * item_health_large // 6
+ * item_health_mega // 7
+ * weapon // 8
+ * weapon // 9
+ * weapon // 10
+ * weapon // 11
+ * weapon // 12
+ * NULL // from 13 until 116
+ * holdable_teleporter // 117
+ * holdable_medkit // 118
+ * item_quad // 119
+ * item_enviro // 120
+ * item_haste // 121
+ * item_invis // 122
+ * team_CTF_redflag // 123
+ * team_CTF_blueflag // 124
+```
+
+In original BFP, when the players aim with the crosshair against their opponents to show their powerlevel, uses `entityState_t->time2` (32-bit integer size) instead `entityState_t->frame` (16-bit integer size).
+With `entityState_t->frame` was enough for that size from 0 until 1000.
diff --git a/docs/bfp_weapon_config_file.md b/docs/bfp_weapon_config_file.md
new file mode 100644
index 0000000..f20bc06
--- /dev/null
+++ b/docs/bfp_weapon_config_file.md
@@ -0,0 +1,262 @@
+# Weapon Config File
+
+The weapon config file (*bfp_weapon.cfg* and *bfp_weapon2.cfg*) is used to define attacks, every attack has its own speed, radius, damage, ...
+Reads *bfp_weapon.cfg*, and after *bfp_weapon2.cfg* (if exists), once in the server at the start. There are some properties used in `bg_pmove.c`.
+
+### (attack_name)
+
+- `(attack_name)`
+Attack tag, starts to read the attack properties below and continues setting the properties of this attack until the next attack tag or the [end](#end) of file.
+
+### weaponNum
+
+- ```weaponNum [int]```
+Attack identifier.
+
+### attackType
+
+- ```attackType [missile/rdmissile/beam/sbeam/hitscan/forcefield]```
+Sets attack type.
+ - `missile` shots a projectile.
+ - `rdmissile` is like a projectile that splits projectiles by the number of ki charge points. Requires another attack to set, see [explosionSpawn](#explosionSpawn).
+ - `beam` is a steerable beam you can aim, if something solid touches the beam trail, beam explodes. If another beam collides against other beam, creates a beam struggle.
+ - `sbeam` is another steerable beam you can aim, but only when you hold the attack key. It doesn't work for beam struggles.
+ - `hitscan` is used for instant shots and short range attacks, like shockwave, lightning and rail trail attacks.
+ - `forcefield` creates a magnetic field or an explosion at the center of the attacker.
+
+### weaponTime
+
+- ```weaponTime [int]```
+Attack timer, sets milliseconds for the attack, it fires after ending these milliseconds.
+
+### randomWeaponTime
+
+- ```randomWeaponTime [int]```
+Sets and adds milliseconds for the [weaponTime](#weaponTime), randomizes attack timer calculation.
+
+### kiCostAsPct
+
+- ```kiCostAsPct [0/1]```
+Enables ki cost percentage to calculate, see [kiPct](#kiPct).
+
+### kiPct
+
+- ```kiPct [0.0 - 1.0 (float)]```
+Calculates maximum ki user multiplied by the ki percentage. Works if it's more than 0 and lesser than 1.
+
+### kiCost
+
+- ```kiCost [0-10000]```
+Ki cost to reduce the user's ki. Works without ki percentage.
+
+### chargeAttack
+
+- ```chargeAttack [0/1]```
+Enables the charge attack depending the ki charge points to release the attack above or equal the minimum ki charge points, see [minCharge](#minCharge).
+
+### chargeAutoFire
+
+- ```chargeAutoFire [0/1]```
+Releases the attack with every charge points released after [weaponTime](#weaponTime) milliseconds.
+
+### minCharge
+
+- ```minCharge [0-6]```
+Minimum ki charge points to release the attack with [chargeAttack](#chargeAttack).
+
+### maxCharge
+
+- ```maxCharge [0-6]```
+Maximum ki charge points for the attack with [chargeAttack](#chargeAttack).
+
+### damage
+
+- ```damage [int]```
+Attack damage.
+
+### splashDamage
+
+- ```splashDamage [int]```
+Attack splash damage from the explosion.
+
+### chargeDamageMult
+
+- ```chargeDamageMult [int]```
+Charge damage multiplier, calculates the damage multiplied per ki charge points of the attack.
+
+### maxDamage
+
+- ```maxDamage [int]```
+Maximum damage deal for the attack.
+
+### radius
+
+- ```radius [int]```
+Projectile radius.
+
+### explosionRadius
+
+- ```explosionRadius [int]```
+Explosion radius.
+
+### chargeRadiusMult
+
+- ```chargeRadiusMult [int]```
+Charge radius multiplier for the projectile, calculates the radius for the projectile multiplied per ki charge points of the attack.
+
+### chargeExpRadiusMult
+
+- ```chargeExpRadiusMult [int]```
+Charge radius multiplier for the explosion, calculates the explosion radius multiplied per ki charge points of the attack.
+
+### maxRadius
+
+- ```maxRadius [int]```
+Maximum radius for the projectile.
+
+### maxExpRadius
+
+- ```maxExpRadius [int]```
+Maximum explosion radius.
+
+### missileSpeed
+
+- ```missileSpeed [int]```
+Projectile speed.
+
+### homing
+
+- ```homing [0.0 - 1.0 (float)]```
+Homing projectile trajectory. If lesser, rotates less. Works if it's more than 0 and lesser than 1.
+
+### homingRange
+
+- ```homingRange [float]```
+Homing projectile range. The radius of the homing projectile to chase opponents.
+
+### range
+
+- ```range [float]```
+Attack range. Applicable only to hitscan attack type.
+
+### loopingAnim
+
+- ```loopingAnim [0/1]```
+Looping attack animation for the player model. If enabled, the player remains in the strike attack animation without returning to the prepare attack animation.
+
+### noAttackAnim
+
+- ```noAttackAnim [0/1]```
+No attack animation for the player model. If enabled, the player remains in the strike attack animation skipping prepare attack animation even at the start.
+
+### alternatingXOffset
+
+- ```alternatingXOffset [0/1]```
+Alternates projectile fire, switching from side to side: left to right, right to left and thus.
+
+### randYOffset
+
+- ```randYOffset [float]```
+Randomizes the upward vector offset of projectile fire.
+
+### randXOffset
+
+- ```randXOffset [float]```
+Randomizes the horizontal vector offset of projectile fire.
+
+### coneOfFireX
+
+- ```coneOfFireX [int]```
+Sets the horizontal vector where the projectile is headed.
+
+### coneOfFireY
+
+- ```coneOfFireY [int]```
+Sets the upward vector where the projectile is headed.
+
+### piercing
+
+- ```piercing [0/1]```
+Enable piercing projectile. Applicable only to missile attack type, it won't work flawlessly for beam and sbeam attack types. Pierces the opponent by hitting 4 times if it stands in the same point.
+
+### reflective
+
+- ```reflective [0/1]```
+Enable reflective attack, reflects attacker' projectiles where the user aims. Applicable only to hitscan attack type with [range](#range).
+
+### priority
+
+- ```priority [int]```
+Projectile priority. Not applicable to hitscan and forcefield attack types. Not applicable to two beam attack type collisions, these create a beam struggle. If more, breaks and explodes the opponent projectile. If both have the same priority, both break and explode.
+
+### blinding
+
+- ```blinding [0/1]```
+The attack blinds the opponent during 6 seconds.
+
+### missileGravity
+
+- ```missileGravity [int]```
+The projectile moves down depending on how much gravity is going down. Not applicable to hitscan and forcefield attack types.
+
+### missileAcceleration
+
+- ```missileAcceleration [float]```
+Adds projectile acceleration, speeds up per frame. Not aplicable to hitscan and forcefield attack types. If between 0.0 and 1.0, deceleration.
+
+### missileDuration
+
+- ```missileDuration [int]```
+Projectile lifetime. Not applicable to hitscan and forcefield attack types.
+
+### multishot
+
+- ```multishot [int]```
+Number of projectiles per shot. Applicable only to missile attack type.
+
+### bounces
+
+- ```bounces [0/1]```
+Enable projectile bouncing when colliding something solid. Applicable only to missile attack type.
+
+### bounceFriction
+
+- ```bounceFriction [float]```
+Adds bounce friction. Applicable only to missile attack type. If lesser, less bounce force. Applicable only to missile attack type.
+
+### noZBounce
+
+- ```noZBounce [0/1]```
+Fix the projectile vertical component to a deterministic bounce height, ignores [missileGravity](#missileGravity) property. Applicable only to missile attack type.
+
+### extraKnockback
+
+- ```extraKnockback [int]```
+Adds knockback while the opponent deals damage from the attack. Lesser than 0, less knockback. If -1000, no knockback.
+
+### railTrail
+
+- ```railTrail [0/1]```
+Instant rail trail, turns into like Q3 Railgun weapon. Applicable only to hitscan attack type.
+
+### movementPenalty
+
+- ```movementPenalty [int]```
+Movement penalty in seconds after using the attack, the user can't move after those seconds have passed. Applicable only to forcefield attack type.
+
+### explosionSpawn
+
+- ```explosionSpawn [weaponNum]```
+Sets the splitting projectile from rdmissile attack type.
+
+### end
+
+- ```end```
+Should be the last word in the *bfp_weapon.cfg* file.
+
+## ORIGINAL BFP NOTES
+- [blinding](#blinding) is only applicable to forcefield attack type.
+- beam attack type with [chargeAutoFire](#chargeAutoFire) detonates at the first moment and attacker deals damage.
+- rdmissile attack type with [chargeAutoFire](#chargeAutoFire) splits the projectile at the first moment and attacker deals damage.
+- There's a bug on the beam attack type with [piercing](#piercing), players are stuck in the charging/firing status and their beam/sbeam can't shot from their muzzle and are coming from zeroed origin.
+- As for ```usesGravity [0/1]```, it's UNUSED. Theoretically, it was used during the development of the original mod and was never applied to the gameplay.
diff --git a/docs/bind_bfp_list.txt b/docs/bind_bfp_list.txt
deleted file mode 100644
index cbf8961..0000000
--- a/docs/bind_bfp_list.txt
+++ /dev/null
@@ -1,70 +0,0 @@
-]\bindlist
-TAB "+scores"
-ENTER "+button2"
-ESCAPE "togglemenu"
-SPACE "+moveup"
-+ "sizeup"
-- "sizedown"
-/ "weapnext"
-0 "weapon 10"
-1 "weapon 1bind 2 weapon 2bind 3 weapon 3bind 4 weapon 4bind 5 weapon 5"
-2 "weapon 2"
-3 "weapon 3"
-4 "weapon 4"
-5 "weapon 5"
-6 "weapon 6"
-7 "weapon 7"
-8 "weapon 8"
-9 "weapon 9"
-= "sizeup"
-[ "weapprev"
-\ "+mlook"
-] "weapnext"
-_ "sizedown"
-` "toggleconsole"
-a "+moveleft"
-b "vstr piccolo"
-c "+movedown"
-d "+moveright"
-f "fly"
-l "+speed"
-m "vstr ginu"
-n "vstr goku"
-o "kiusetoggle"
-p "pause"
-s "+back"
-t "messagemode"
-w "+forward"
-~ "toggleconsole"
-CAPSLOCK "+button9"
-PAUSE "pause"
-UPARROW "+forward"
-DOWNARROW "+back"
-LEFTARROW "+left"
-RIGHTARROW "+right"
-ALT "+button7"
-CTRL "+button10"
-SHIFT "+button8"
-DEL "+lookdown"
-PGDN "+lookup"
-END "centerview"
-F1 "vote yes"
-F2 "vote no"
-F3 "ui_teamorders"
-F4 "vstr vegeta"
-F5 "vstr beb"
-F6 "vstr beb"
-F7 "vstr gotenks"
-F8 "vstr trunks"
-F9 "vstr freeza1"
-F10 "vstr cell"
-F11 "screenshot"
-F12 "vstr freeza1"
-MOUSE1 "+attack"
-MOUSE2 "+button9"
-MOUSE3 "+zoom"
-MWHEELDOWN "weapnext"
-MWHEELUP "weapprev"
-]\condump bind_bfp_list.txt
-Dumped console text to bind_bfp_list.txt.
-
diff --git a/docs/cmd_bfp_list.txt b/docs/cmd_bfp_list.txt
deleted file mode 100644
index edb4cfa..0000000
--- a/docs/cmd_bfp_list.txt
+++ /dev/null
@@ -1,239 +0,0 @@
-]\cmdlist
-cmdlist
-seta
--scores
--speed
--moveright
--movedown
--button2
-clear
--button7
--button10
-condump
--forward
--back
--button8
-cvarlist
-+button8
-voip
-bind
-loaddefered
-teamtask
-stats
-teamvote
-callteamvote
-vote
-callvote
-setviewpos
-addbot
-levelshot
-follow
-team
-noclip
-notarget
-god
-give
-votell
-vosay_team
-vosay
-vtaunt
-vtell
-vsay_team
-vsay
-tell
-say_team
-say
-kill
-transformorbit
-button12
-+fly
--melee
-+melee
--block
-+block
--kiuse
-+kiuse
--kiboost
-+kiboost
-loaddeferred
-startOrbit
-tcmd
-vtell_attacker
-vtell_target
-tell_attacker
-tell_target
-weapon
-weapprev
-weapnext
-sizedown
-sizeup
--zoom
-+zoom
-+scores
-viewpos
-prevskin
-nextskin
-prevframe
-nextframe
-testmodel
-testgun
-exportCubemaps
-gfxmeminfo
-minimize
-gfxinfo
-screenshotJPEG
-screenshot
-modelist
-modellist
-skinlist
-shaderlist
-imagelist
--attack
-wait
-which
-touchFile
-fdir
-dir
-path
-map
-ping
-globalservers
-music
-s_info
-s_stop
-s_list
-stopmusic
-play
-stopvideo
-video
-model
-fs_referencedList
-fs_openedList
-showip
-serverstatus
-rcon
-localservers
-reconnect
-connect
-stoprecord
-cinematic
-demo
-record
-disconnect
-vid_restart
-snd_restart
-clientinfo
-configstrings
-cmd
--voiprecord
-+voiprecord
--mlook
-+mlook
--button14
-+button14
--button13
-+button13
--button12
-+button12
--button11
-+button11
-+button10
--button9
-+button9
-+button7
--button6
-+button6
--button5
-+button5
--button4
-+button4
--button3
-+button3
-+button2
--button1
-+button1
--button0
-+button0
-+attack
-+speed
-+moveright
--moveleft
-+moveleft
--strafe
-+strafe
--lookdown
-+lookdown
--lookup
-+lookup
-+back
-+forward
--right
-+right
--left
-+left
-+movedown
--moveup
-+moveup
-centerview
-messagemode4
-messagemode3
-messagemode2
-messagemode
-togglemenu
-toggleconsole
-exec
-unbindall
-rehashbans
-net_restart
-sayto
-flushbans
-exceptdel
-bandel
-exceptaddr
-banaddr
-listbans
-killserver
-spdevmap
-spmap
-devmap
-sectorlist
-map_restart
-dumpuser
-systeminfo
-serverinfo
-status
-clientkick
-kicknum
-kickall
-kickbots
-banClient
-banUser
-kick
-heartbeat
-vminfo
-vmprofile
-in_restart
-meminfo
-game_restart
-writeconfig
-changeVectors
-quit
-setenv
-bindlist
-unbind
-echo
-vstr
-execq
-cvar_restart
-cvar_modified
-unset
-reset
-setu
-sets
-set
-toggle
-print
-234 commands
-]\condump cmd_bfp_list.txt
-Dumped console text to cmd_bfp_list.txt.
-
diff --git a/docs/cvar_bfp_list.txt b/docs/cvar_bfp_list.txt
deleted file mode 100644
index ee402e4..0000000
--- a/docs/cvar_bfp_list.txt
+++ /dev/null
@@ -1,200 +0,0 @@
-]\cg_
- cg_yrgolroxor = "0"
- cg_particleAura = "0"
- cg_timescaleFadeSpeed = "0"
- cg_timescaleFadeEnd = "1"
- cg_cameraOrbit = "0"
- cg_stats = "0"
- cg_thirdPersonAngle = "0"
- cg_tracerlength = "100"
- cg_tracerwidth = "1"
- cg_tracerchance = "0.4"
- cg_footsteps = "1"
- cg_showmiss = "0"
- cg_noplayeranims = "0"
- cg_nopredict = "0"
- cg_errordecay = "100"
- cg_debugevents = "0"
- cg_debugposition = "0"
- cg_debuganim = "0"
- cg_animspeed = "1"
- cg_swingSpeed = "0.3"
- cg_centertime = "3"
- cg_gunZ = "0"
- cg_gunY = "0"
- cg_gunX = "0"
- cg_ignore = "0"
- cg_brassTime = "2500"
- cg_drawCrosshair = "3"
- cg_drawCrosshairNames = "1"
- cg_marks = "1"
- cg_thirdPerson = "1"
- cg_fixedThirdPerson = "1"
- cg_drawOwnModel = "0"
- cg_polygonAura = "1"
- cg_highPolyAura = "1"
- cg_kiTrail = "50"
- cg_beamTrail = "25"
- cg_bigExplosions = "1"
- cg_chargeupAlert = "1"
- cg_drawKiWarning = "1"
- cg_explosionShell = "1"
- cg_explosionSmoke = "1"
- cg_particles = "1"
- cg_crosshairColor = "7"
- cg_musicUnpacked = "1"
- cg_stableCrosshair = "0"
- cg_explosionRing = "1"
- cg_thirdPersonHeight = "-60"
- cg_thirdPersonRange = "110"
- cg_transformationAura = "1"
- cg_lightAuras = "1"
- cg_lightExplosions = "1"
- cg_flytilt = "1"
- cg_playHitSound = "0"
- cg_drawGun = "1"
- cg_zoomfov = "22.5"
- cg_fov = "90"
- cg_gibs = "1"
- cg_draw2D = "1"
- cg_drawStatus = "1"
- cg_drawTimer = "0"
- cg_drawFPS = "0"
- cg_drawSnapshot = "0"
- cg_draw3dIcons = "0"
- cg_drawIcons = "1"
- cg_drawAmmoWarning = "1"
- cg_drawAttacker = "1"
- cg_drawRewards = "1"
- cg_crosshairSize = "24"
- cg_crosshairHealth = "1"
- cg_crosshairX = "0"
- cg_crosshairY = "0"
- cg_simpleItems = "0"
- cg_lagometer = "1"
- cg_railTrailTime = "400"
- cg_runpitch = "0.002"
- cg_runroll = "0.005"
- cg_bobup = "0.005"
- cg_bobpitch = "0.002"
- cg_bobroll = "0.002"
- cg_teamChatTime = "3000"
- cg_teamChatHeight = "0"
- cg_forceModel = "0"
- cg_forceSkin = "0"
- cg_deferPlayers = "1"
- cg_drawTeamOverlay = "0"
- cg_drawFriend = "1"
- cg_teamChatsOnly = "0"
- cg_noVoiceChats = "0"
- cg_noVoiceText = "0"
- cg_cameraOrbitDelay = "50"
- cg_scorePlums = "1"
- cg_smoothClients = "0"
- cg_noTaunt = "0"
- cg_lightweightAuras = "0"
- cg_simpleHUD = "0"
- cg_spriteAura = "0"
- cg_smallOwnAura = "0"
- cg_permaglowUltimate = "1"
- cg_superdeformed = "0"
- cg_stfu = "0"
- cg_lifekills = "183"
- cg_lifedeaths = "212"
- cg_lowpolysphere = "1"
- cg_autoswitch = "1"
- cg_predictItems = "1"
- cg_viewsize = "100"
- cg_stereoSeparation = "0"
- cg_shadows = "1"
-]\g_
- g_enableBreath = "0"
- g_enableDust = "0"
- g_blockLength = "3"
- g_blockDelay = "2"
- g_kiChargePct = "15"
- g_kiCharge = "0"
- g_kiRegenPct = "0.6"
- g_kiRegen = "0"
- g_blockCostPct = "3"
- g_blockCost = "0"
- g_boostCostPct = "0"
- g_boostCost = "150"
- g_flightCostPct = "0"
- g_flightCost = "50"
- g_maxSpawnPL = "999"
- g_plKillBonusPct = "0.1"
- g_chargeDelay = "250"
- g_meleeRange = "32"
- g_meleeDiveRange = "700"
- g_meleeDamage = "10"
- g_podiumDrop = "70"
- g_podiumDist = "80"
- g_motd = ""
- g_debugAlloc = "0"
- g_debugDamage = "0"
- g_debugMove = "0"
- g_inactivity = "0"
- g_forcerespawn = "20"
- g_weaponrespawn = "5"
- g_quadfactor = "3"
- g_knockback = "1000"
- g_gravity = "800"
- g_speed = "320"
- g_needpass = "0"
- g_password = ""
- g_synchronousClients = "0"
- g_restarted = "0"
- g_noFlight = "0"
- g_meleeOnly = "0"
- g_botsFile = ""
- g_arenasFile = ""
- g_allowVote = "1"
- g_filterBan = "1"
- g_banIPs = ""
- g_logSync = "0"
- g_log = "games.log"
- g_warmup = "20"
- g_teamForceBalance = "0"
- g_teamAutoJoin = "0"
- g_friendlyFire = "0"
- g_maxGameClients = "0"
- g_spScores1 = ""
- g_spScores2 = ""
- g_spScores3 = ""
- g_spScores4 = ""
- g_spScores5 = ""
- g_spAwards = ""
- g_spVideos = ""
- g_spSkill = "2"
- g_basePL = "952"
- g_hitStun = "1"
- g_doWarmup = "0"
- g_allowSpectatorChat = "1"
- g_gametype = "0"
- g_redTeam = "Stroggs"
- g_blueTeam = "Pagans"
-]\ui_
- ui_cdkeychecked = "0"
- ui_spSelection = ""
- ui_ffa_fraglimit = "2"
- ui_ffa_timelimit = "1"
- ui_tourney_fraglimit = "2"
- ui_tourney_timelimit = "3"
- ui_team_fraglimit = "9"
- ui_team_timelimit = "2"
- ui_team_friendly = "1"
- ui_ctf_capturelimit = "8"
- ui_ctf_timelimit = "30"
- ui_ctf_friendly = "0"
- ui_browserMaster = "1"
- ui_browserGameType = "0"
- ui_browserSortKey = "4"
- ui_browserShowFull = "1"
- ui_browserShowEmpty = "1"
- ui_smallFont = "0.25"
- ui_bigFont = "0.4"
- ui_singlePlayerActive = "0"
-]\condump cvar_bfp_list.txt
-Dumped console text to cvar_bfp_list.txt.
-
diff --git a/docs/old_dev_journals/ansel_dev_journal.md b/docs/old_dev_journals/ansel_dev_journal.md
new file mode 100644
index 0000000..a1391ce
--- /dev/null
+++ b/docs/old_dev_journals/ansel_dev_journal.md
@@ -0,0 +1,71 @@
+# Another character design - 11/14/2001 by Ansel
+
+Hey
+I just finished texturing Pyrate a few weeks ago, so I thought I'd get some concept sketches going for another character.
+
+"Ryuujin - Stands for Dragon God in japanese. He is, obviously, a god who has spent the past 3 millenia training his mind and his body. He rarely speaks, and is rarely seen, but he always shows up when there is trouble. His ki is "different," kind of mysterious and very powerful. He wields two scerpent-dragon swords whose blades are made of the same ki as a kienzan." (taken from pyrofragger's devjournal)
+
+
+
+Sorry for the bad image quality, I don't have a scanner.
+
+This is not a final concept, and as always feedback is welcome.
+
+-Ansel
+
+
+
+
+# Concept Drawings - 09/04/2001 by Ansel
+
+As you probably already know, BFP will be having original characters, so I went and did a few concept drawings. Here is my first concept-
+
+
+
+If you have any questions or comments, please stop by the forums.
+
+-Ansel
+
+
+
+
+# New Icons - 07/19/2001 by Ansel
+
+I finished all of the new attack icons for BFP. I won't show them all, but here is three more. You already saw the kamahameha icon in Chris' journal.
+
+
+
+
+
+
+
+
+
+By the way, the attacks in order are: Bakuhatsuha, SBC, and Big Bang Attack.
+
+-Ansel
+
+
+
+
+# What happend? Gohan happend - 04/03/2001 by Ansel
+
+We promised that we would show sceens of the new models, here is one.
+Its a screen of Pyrofragger's new Gohan model, skinned.
+The model is seperated in this shot though, we didn't have time to get a render of him put together.
+
+
+
+
+Expect renders of gohan and krillin's new skins soon.
+
+Ansel
+
+
+
+
+# First Update - 04/03/2001 by Ansel
+
+Just finshed the Gohan and Krillin skins for the new models, which means my phase 1 work is done.
+Ansel
+
diff --git a/docs/old_dev_journals/anthony_dev_journal.md b/docs/old_dev_journals/anthony_dev_journal.md
new file mode 100644
index 0000000..d5d4b0c
--- /dev/null
+++ b/docs/old_dev_journals/anthony_dev_journal.md
@@ -0,0 +1,50 @@
+# Old DBZ BFP art - 01/21/2002 by Anthony
+
+Hey guys, seeing as how some fan paks are gonna be released with DBZ stuff that works in BFP, I thought I'd post some of the old drawings I did for the original character select screen. So anyway, an example:
+
+
+
+
+To see the others, go here:
+
+
+[Ant's DBZ Stash](https://web.archive.org/web/20020210151755/http://www.antsin3d.com/dbz/)
+
+
+Hope ya enjoy it, it would have been a nice select screen ^_^
+Oh yeah, if you'd like to see some other neat stuff(just some 3d), see [HERE](https://web.archive.org/web/20020210151755/http://www.antsin3d.com/tempcg)
+
+
+
+
+# Play it chill - 01/18/2002 by Anthony
+
+What's up guys? The launch is going over like either a movie premier or New Year's Eve, or a mix of both. Play it cool and you'll get it. I counted 2500 people in line over at Fileplanet, and everyone else is getting just as hammered. We know one thing: you won't have any problems finding opponents! I hope you guys all enjoy it, as I'm sure you will. Remember, take it easy and everyone will get the mod. Don't want anyone to get hurt ;] Have a BLAST!
+
+
+
+
+# Stuff - 08/02/2001 by Anthony
+
+The box art image will be released shortly. On another note, I'd like to request that you not send any money to me in any way, be it capital, goods, or services. I'm not going to play off your gratitude and anxiousness for this mod by asking for such. I would consider doing so disgusting. Naturally it's not my place to judge the souls of others who may wish to....
+PS: Of course, Icey's now gone, as are his journal entries which I was referring to above. We'll have more info sometime late next week, probably another happy announcement.
+
+
+
+
+# Art - 04/17/2001 by Anthony
+
+Just a little update, I finished the box art I was working on. You'll all get to see it in due course, a little closer to release. Of course, I can still tease you...
+
+
+
+Anthony
+
+
+
+
+# Cover / Promotional Art - 03/07/2001 by Anthony
+
+Hey guys. Since my old posts aren't here any longer, I'll remind you newbies who I am. I do stuff like the character select screen drawings, which I hope you all enjoy. Currently I'm working on cover and promotional art for BFP. Note that I am not in any way suggesting that BFP is going to be released outside of freeware on the Net-we just want to have plenty of graphics and titles that can be used to promote the best mod around, both for Phase 1 and the following products. Anyway, since it's all under the blanket until we see release, I won't be able to really describe my progress here-just suffice to say that progress is being made!
+
+
diff --git a/docs/old_dev_journals/chris_dev_journal.md b/docs/old_dev_journals/chris_dev_journal.md
new file mode 100644
index 0000000..3b75171
--- /dev/null
+++ b/docs/old_dev_journals/chris_dev_journal.md
@@ -0,0 +1,582 @@
+# BFP release time - 01/18/2002 by Chris
+
+We hope to have Bid For Power ready by 6 PM pacific time.
+
+
+
+# Still need mirrors - 01/18/2002 by Chris
+
+Now that Bid For Power has been released we need Mirrors now more then ever. If you have managed to fight through the lines and have gotten your hands on a copy of Bid For Power and would like to host is please email me at chris@bidforpower.com
+We are getting some reports that windows XP users are having problems getting the .exe version of Bid For Power to work properly. If this occurs then rename the .exe version to .zip and it should work fine.
+
+
+
+
+# Hold on... Almost there... - 01/18/2002 by Chris
+
+Bid For Power will be ready around 10 o'clock eastern time. Our ftp decided that it didnt like the bfp and ate it so we are re-uploading.
+
+*update*
+
+We are getting some reports that windows XP users are having problems getting the .exe version of Bid For Power to work properly. If this occurs then rename the .exe version to .zip and it should work fine.
+
+
+
+
+# Last minute mirrors - 01/18/2002 by Chris
+
+Hey guys.
+Any gaming sites still interested in mirroring Bid For Power please send me an email to chris@bidforpower.com
+
+Please no tripod or geocities accounts.
+
+Thanks.
+
+We are getting some reports that windows XP users are having problems getting the .exe version of Bid For Power to work properly. If this occurs then rename the .exe version to .zip and it should work fine.
+
+
+
+
+# Mirror, mirror, on the wall... - 01/11/2002 by Chris
+
+If there are any webmasters who be interested in mirroring Bid For Power please [email me](mailto:chris@gamethrust.com). All that we ask in return is that your site is capable of numbering the amount of downloads.
+Thanks.
+
+
+
+
+# Bid For Power - 12/27/2001 by Chris
+
+If you want to truly understand something, try to change it.
+
+
+
+
+# Clouds composed of water vapor are up in the, you know, sky. - 12/17/2001 by Chris
+
+We're trying to get the last two models animated, skinned, prepped, etc... You name it, we're trying to get it done but progress is slow and delays are very fast. What I can say about the release is that it won't happen before Christmas.
+Well now that the bad new is out of the way I can hint at the good news, as to wether we can get it out before New Year's is very much up in the air but the release will be soon.
+
+
+
+
+# The Monday Update - Chris - 11/26/2001
+
+Dash has been revising his Palace level and it's looking amazing. He has been working on improving the lighting, adding different floor textures, creating new details in different areas, and optimizing the building structure to make it run smoother. The screenshots speak for themselves though:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# The Monday Update - Chris - 11/20/2001
+
+Another late Monday's Update but I think this one is worth it as we're showing off six new in-game screenshots and one brand new rendering. The six in-game screenshots show off our three completed characters, Kyah, Gothax, and Shilo all modeled and skinned by Rodney. These screenshots are taken from the new beta the team has been playing around with. The rendering is of, Ryuujin, Pyrofraggors new character he is woking on.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+I also thought I'd go ahead and post some of the recent developer journals as they have quite a bit of good updates in them.
+
+- Yrgol's Developer Journal
+_"What we are currently working on is equivalent to beta 2 for most mods, or in bfp jargon phase 1. We had phase 1 completed for dbz quite a long time ago, and then the funimation/infogrames stuff happened. Most of the time since then has been used for converting bfp to not use dragonball z copyrights._
+
+_Direct hit detection has been made more reliable. This makes the kienzan equivalent and the deathball easier to use._
+
+_Explosion damage is no longer all or nothing. The damage decreases as distance from the explosion increases like in baseq3._
+
+_Attack radius has been separated from explosion radius. The attack radius and explosion radius for each of the attacks have been tweaked._
+
+_The knockback physics for attacks and explosions has been seriously tweaked. Attacks can now be used to rocket jump._
+
+_Boost jumping has been toned down. It was simply too excessive before._
+
+_Another total rebalance of all attacks happened."_
+
+- Pyrofragger's Developer Journal
+_"Sometime this week i'll have pictures of Ryuujin for you guys. The model is done and uv mapped, but i have to redo much of the physiquing and ansel still has to skin it. You'll see skinned renders in here soon as well as ingame shots on the mainpage."_
+
+
+
+
+# beta and a little extra - 11/18/2001 by Chris
+
+Yrgol uploaded a new beta for the team to test out. i've been playing it for awhile now and it really plays great. The overall balancings (mentioned in Yrgol's journal) really work nice now, the game has an awesome feel to it.
+I noticed my comments concerning Nintendo marketing mainly to kiddies didn't go unheard as I got quite a few emails about the comments. The emails I got ranged in intelligence but overall a lot of people are really looking forward to the new resident evil games exclusively on the gamecube. But I still think gamecube is being marketed to kiddies.
+
+Also, a few journal entries back I mentioned putting up a signed copy of Half-Life, by the team at Valve Software, on [Ebay.com](https://web.archive.org/web/20020210145200/http://www.ebay.com/). Well I finally got around to it and you can now bid on a [first edition of Half-Life signed by the team at Valve Software](https://web.archive.org/web/20020102191742/http://cgi.ebay.com/aw-cgi/eBayISAPI.dll?ViewItem&item=1299439693).
+
+
+
+
+# The Monday Update... On Friday - Chris - 11/10/2001
+
+Sorry for the late update but I didn't get back from the Gamecube party till later then expected. As promised here are the brand new in-game screenshots of our new character Shilo, modeled by Rodney. Shilo has some strong anime influences and it really comes out in his shape, check it out:
+
+
+
+
+
+
+
+
+
+
+
+
+
+Here goes my rant about the Nintendo Gamecube for anyone interested. I had some high hopes for the Gamecube considering Nintendo's track record with it's releases. Usually with its releases it has some good launch titles and really makes good use of the system however the games I played (which were all on High Definition Panasonic Flat Screen TV's) seemed less then great. The graphics were good on the Star Wars game but other games like Luigi's Mansion, Waverace, and Star Fox were kind of bland, it seems like Gamecube is going in the "Kiddy Games" direction. I hope Nintendo starts to get more into realistic graphics, if they dont I could see them singling out a lot of people.
+
+If anyone knows of any launch parties for the Xbox in the Seattle area please email as I would be very interested in attending. Been dying to play an Xbox.
+
+
+
+
+# The Monday Friday Update - 11/08/2001 by Chris
+
+On Friday we will be posting our Monday Update as things got a little hectic on Monday so we pushed it to this Friday. All is not lost as we will be showing off Shilo, the new model by Rodney, in-game.
+In a semi-gaming related note, does anyone know if Xbox will be having a release party or anything similiar to Nintendo's Gamecube club where they basicly have parties where everyone can come and play the games? I found out Nintendo is having a Gamecube Club party in Seattle which is only about 20 minutes from where I live so this weekend I will be heading over there to check out some of their games. If anyone else in the seattle area is going as well drop me a line.
+
+
+
+
+# Mondays update - 11/05/2001 by Chris
+
+Hi guys.
+I'm a little backed up right now but hope to have mondays update later tonight. Until then I suggest you either download the second test version of wolfenstein or try and grab a copy of the star wars teaser though its difficult to find a good site to download it from.
+
+
+
+
+# Monday Update - Chris - 10/29/2001
+
+Some brand new in-game screenshots of three of our new characters; Kyah, Gothax, and Pyrate. The action takes place in the Triblecle Creek, an improved version of Dash's Namek level. Next update will include in-game screenshots of our newest character, Shilo.
+
+
+
+
+
+
+
+
+
+
+
+
+
+I also thought I would repost some info dug out from the recent Developer journal updates as some big stuff was mention;
+
+Yrgol's Dev Journal:
+
+- _"Per-skin config files have been added that allow things like attack names and voices to be defined by the skin. I have plans to move a lot of stuff into these config files, including attack shaders, sounds, and models. The individual models will never be able to define how the attacks operate, only how they are displayed to the user. "_
+
+- _"I'm working on attack scripting for servers only. This will allow new attacks of any rough type that bfp already contains to be added to the game with little or no code. I have a script file set up and partially implemented that has like 30 variables for each of the attacks. This will not allow models to define new attacks, but it will allow team members to work on attacks without me, or mods of bfp. It also lets server ops who hate the game balance to tweak it significantly. "_
+
+Remisser's Dev Journal:
+
+- _"I compiled the music... if anyone has any clue as to how to make these files smaller, tell me..."_
+
+Chris Dev Journal:
+
+- _"Last time I updated I hinted at reworking our version of capture the flag and I've spent some time working different additions to it. Some new twists to classic game which add a lot more to it."_
+
+- _"I spent sometime on designing a single-player/co-op gameplay mode which is definately a fast paced experience very similiar to something like... Dragonball Z. Keeping with our "new release/ new gameplay mode" idea I hope to have this be our first new gameplay mode release after our initial release."_
+
+
+
+
+# Gameplay additions - 10/24/2001 by Chris
+
+Last time I updated I hinted at reworking our version of capture the flag and I've spent some time working different additions to it. Some new twists to classic game which add a lot more to it.
+I also spent sometime on designing a single-player/co-op gameplay mode which is definately a fast paced experience very similiar to something like... Dragonball Z. Keeping with our "new release/ new gameplay mode" idea I hope to have this be our first new gameplay mode release after our initial release.
+
+In other news, I've been thinking about starting an auction on [Ebay.com](https://web.archive.org/web/20020210145200/http://www.ebay.com/) (No, I wont be auctioning Bid For Power) of a first edition copy of Half-life I have which was signed by everyone from the original team at Valve Software. I got rid of all my game boxes and stumbled across Half-life, it's just been sitting on a shelf (unopened) for years now and thought someone else might appreciate it more.
+
+
+
+
+# The Skies Above Heaven - Chris - 10/23/2001
+
+Here are some new shots of the brand new skyline for Dash's Palace level. We will be updating with a new shot of Pyro's character just as soon as he gets everything finished on it, stay tuned.
+
+
+
+
+
+
+
+
+
+
+
+
+# Midnight Update - 09/30/2001 by Chris
+
+It's midnight and I got finished looking through most of the posts on the forum so I thought I'd make an update to the dev journal. In a week from wednesday we will be having our internal due date for all the models going into the first release of Bid For Power (just so I dont get a lot of brainless posts on the forum, no this does not mean we are releasing in a week).
+I was playing NilreMK's new model, pyrate, and it's an awesome piece of work, once it's fully skinned and animated we'll be sure to post shots of it on the main page along with Pyro's new model.
+
+I finally got DSL installed. I heard all those horror stories about having trouble getting the line to work and never thought it would happen to me. Well it did. Everything from having my line dropped, wrong equipment shipped, faulty NIC, secondary line problems, and pissed off tech support people who had me on hold for a total of 4 hours.
+
+Since I now have the luxury of playing games over the internet without having huge slowdowns I have been addicted to RTCW, especially having sniper only games.
+
+
+
+
+# Status update - 09/14/2001 by Chris
+
+We've been working hard on getting the new Bid For Power ready and as Yrgol mentioned earlier our biggest competition is from ourselves. The new modelers in my opinion have outclassed our previous work that really shows the talent of our modelers.
+As far as a release date is concerned were working hard to get everything ready but due to several things we haven’t been able to name a date nor will we till we are absolute. Apologizes if any previous updates had people thinking otherwise but I guess that’s the way it is when developing games.
+
+Our site has become riddled with advertisements which unless we’re in nascar don’t go together. We will likely be starting up a donation box similar to what planetnamek had which if we are successful we can remove all the advertisements (except for the banners at the top).
+
+Also, Computer Gaming Monthly ran a blurb about Bid For Power and I have been unable to find a copy of the magazine to check it out myself. If anyone has a copy of the article and can scan it and send it to me at chris@gamethrust.com it would be a big help.
+
+Thanks.
+
+
+
+
+# Announcement - 08/05/2001 by Chris
+
+Expect the announcement to go up within the next hour. Thanks for your patience.
+
+
+
+# Updated features - 07/17/2001 by Chris
+
+Some new things to mention.
+We've created some new Attack icons which look a lot more realistic then the current set of attack icons. Ansel has been working on this new set and they look great, here is an early version of the Kamehameha:
+
+
+
+
+
+Another thing to mention is the new interface we are planning. With the release of TA's code we can now implement a more advanced UI into Bid For Power. We're still working on the different looks but hopefully we can have something more solid to show next update.
+
+In Yrgol's previous update he mentioned the custom model support. We decided to branch out with this and add several different versions of Vegeta. Now instead of choosing between different skins you can now select different models of Vegeta. Here is what we have planned.
+
+Vegeta from the Freeza Saga
+Vegeta from the Cell Saga
+Vegeta from the Buu Saga
+
+Each model will range in size and shape and with three different modelers they will each have their own unique look. I should also note that we will still be keeping the existing set of skins for the models.
+
+Should have some good things to show off in the next update.
+
+
+
+
+# Android #16 mirrors - 06/20/2001 by Chris
+
+We are low on mirror sites for Android #16 so if you have a site that is willing to host Android #16 please [email me](mailto:chris@gamethrust.com).
+
+
+
+# Life insurance pays off triple if you die on a business flight. - 06/18/2001 by Chris
+
+The site laucnh is going great and now we've pretty much got everything up and running as the image section will be ready soon enough. The dev journals, team section, faq, about, etc... are all doing great and if they haven't been updated yet will be later tonight. Android #16 should be ready for download tomorrow. We're packing him up right now and then sending him off to the good folks at fileplanet to be mirrored. The FAQ section will get a nice little update over the night and if you guys have any suggestions for new questions please post them in our [forum](https://web.archive.org/web/20020210145200/http://www.bidforpower.com/forum/showthread.php?s=83d20c406900acb34d0a60f904548094&threadid=6049). Expect some even cooler looking screenshots for tomorrow's update.
+
+
+
+# The martyrdom of Saint Me - 05/29/2001 by Chris
+
+Much has been happening but due to the new site we have been laying off on updating which is mainly my fault. It has been a busy couple of weeks and since the shadow of E3 has past us we can start to kick it into high gear again.
+The ETA on the new site is still up in the air but the good news is that when we release the new site we will also release a playable Android #16 for Quake 3 Arena much like we did with our Vegeta model.
+
+We should have some more news soon. As far as other stuff I finally met Chuck Palahniuk at a recent reading for his new book, Choke. He is a very cool guy and now I will stop quoting Fight Club and start quoting other work by him.
+
+
+
+
+# Where you going with this ikea boy? - 04/27/2001 by Chris
+
+Tomorrow I will finish up work on the in-game footage and should have something fairly nice to show off though it will take a little while for Fileplanet to begin mirroring the clips so they may not be officially posted till monday but they will be worth the wait.
+Some sites have been emailing me asking if they can have some type of banner to use to link Bid For Power from their sites. About time I posted this banner, we should have a button available soon as well.
+
+
+
+
+
+
+
+
+
+# insert Fight Club saying here - 04/23/2001 by Chris
+
+I am doing a little video work and I was wondering if anyone here knows of a good program to extract both audio and video from quake 3. It is very important it records both audio and video, if you know where I can download a copy of a program like that please email me at chris@gamethrust.com
+thanks.
+
+
+
+
+# I am Jacks smirking revenge - 04/15/2001 by Chris
+
+The link submission is over as I collected way too many then I expected but thanks to everyone who submitted. We cant add everyone and most likely we will only add about six links. As for the ETA on the new site it will be about 2 weeks as we are waiting for some help from gamethrust to convert the site over to php3 format.
+As you have noticed from some of the other members subtle hints work is closing on phase I.
+
+Anthony finished up work on the boxart for Bid For Power and it looks amazing, we are very eager to show it off. Very cool stuff.
+
+I managed to find some time over easter sunday to work on some levels for Half-life, take a peak:
+
+
+
+
+
+
+[Some other levels of mine](https://web.archive.org/web/20020215120703/http://www.bidforpower.com/chris/portfolio.htm)
+
+
+
+
+# Link Submission - 04/13/2001 by Chris
+
+I am working on the link section to Bid For Power. If you are interested in having your site added please email me at chris@gamethrust.com Please no clan sites.
+Please note that we are looking for sites that are either DBZ oriented, Quake 3 oriented, or Bid For Power oriented and not all that are submitted will be linked.
+
+As well as I am on the subject of updating the site, if there are any questions you think should be added to the FAQ email them to me and I will try and include them.
+
+And yes the new site will have a working image section...
+
+
+
+
+# New Model Clips - 04/09/2001 by Chris
+
+The clips were hurting our bandwidth so I went and moved them to a new server. They will be posted on the main site in a little bit.
+Just wanted to thank everyone that emailed me about mirroring the files.
+
+
+
+
+# I am Jacks Late Fee - 03/26/2001 by Chris
+
+Looks like RC 3 did not go as planned. We will have it out the door by the end of the week. As for where our models stand the buff version of Freeza has been modeled and exported and now Rodney is working on the skin for him.
+Pyro finished the models of gohan and krillin on sunday morning so Ansel is working on skinning both Krillin and Gohan. In case you were wondering krillin and gohan are modeled with their original Gi outfits.
+
+Later this week I can update with the body transformation of Freeza and the new models of Krillin and Gohan... who knows, maybe even an in-game movie.
+
+
+
+
+# I am Jacks missing Skin Artist - 03/24/2001 by Chris
+
+Hi guys.
+We are trying very hard to meet the monday deadline for RC III but we have run into a slight problem. We have three models which need to be skinned and currently our skin artists already have work or are discovering a life away from the computer for the weekend (could you blame them?)
+
+Being that we would really like to meet the monday deadline I am asking if any skin artist would like to give some temporary assistance and help with the skinning of our Krillin and Freeza models.
+
+The skin artist would have to be very good and have some prior experience to show plus he would have to work under the gun as we have about two days to get the skin done.
+
+If anyone would like to volunteer to skin our Krillin and Freeza models please email me at Chris@gamethrust.com with a link to some of your prior work.
+
+Thanks
+
+
+
+
+# I am jacks update - 03/23/2001 by Chris
+
+Well I know the website hasnt been updated in awhile and thats partly my fault but dont worry we have kept ourselves busy.
+Yrgol is working on a slight rebalance to phase I damages. Pyro has Krillin and Gohan done and Ansel should have the skins done this weekend meaning monday we will have a new gohan and krillin in game.
+
+NilreMK is working on exporting Tabouhs Freeza model(s) which should also be finished on monday, meaning monday will hopefully be a nice update of Gohan, Krillin, Freeza and also Kits new map and as a bonus Dashs updated namek waters.
+
+
+
+
+# Without pain, without sacrifice, we would have nothing. - 03/09/2001 by Chris
+
+I would like to announce that for Phase II of Bid For Power (which will chronicle the android and cell sagas) we will be introducing an advanced melee system, rebalanced attack system, and several new RPG elements including "Unique Characteristic Traits" which will give each character their own special abilties.
+An example of this will be that Piccolo will have the ability to regenerate health and Vegeta will have the ability to increase his power after experiencing an attack. Its getting pretty late so I will likely update tomorrow with a much more thurough explaination.
+
+Stay tuned...
+
+
+
+
+# With a gun barrel stuck between your teeth, you only speak in vowels. - 03/08/2001 by Chris
+
+Announcement(s) postponed till tomorrow...
+
+
+
+# I am Jacks raging bile duct. - 03/07/2001 by Chris
+
+
+Hi guys,
+Sorry that I havent produced that video clip yet but I was holding off until I setup my new system. I did some talking with Yrgol and we are going to be adding even more effects to the power struggle so once those effects are in and my system is up to par I will post the video.
+
+Now on to even better news... Phase II news. We consider phase I our test run, our basic platform to build on. We are now planning some of the various new features for phase II and we may be adding a ton of new features including advanced melee system, new tier system, and an RPG edge to it all, very cool stuff.
+
+Tomorrow I should have some more interesting news about our phase II plans and maybe even some bigger news, all depends on how things work out.
+
+Almost forgot, I thought I would list some of the Bid For Power fan sites out there for people to check out:
+
+[Bid For Power World](https://web.archive.org/web/20020210145200/http://bfpworld.digitalnova.com/)
+
+[Bid For Power Online](https://web.archive.org/web/20010609020639/http://bfponline.4t.com/news.html)
+
+[PlanetBFP](https://web.archive.org/web/20010515205855/http://www.planetbfp.com/)
+
+[Bid For Power Clan League](https://web.archive.org/web/20010516202746/http://www.bfpcl.f2s.com/)
+
+[Bid For Power Net](https://web.archive.org/web/20020120230500/http://bfpnet.cjb.net/)
+
+
+
+
+# I am Jacks cold sweat. - 02/19/2001 by Chris
+
+I have been tinkering on a new 30 second clip showing off a fight between some of the characters and unfortunately the movie of Piccolo fighting Krillin in the new Cell Tournament map feel to the cutting room floor but I thought I would atleast go ahead and post some screenshots of the now deceased video.
+If I were to post a movie clip showing off a battle between two opponents who would you want the movie to have in it and at which map? [Post your answer in our forum](https://web.archive.org/web/20010407130034/http://www.bidforpower.com/forum/showthread.php?threadid=18016)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# I am Jacks complete lack of surprise. - 02/18/2001 by Chris
+
+I was supposed to update with some screenshots of Cells Tournament but I decided to take it one step further and update with a vide clip from the map. I decided to create a clip of Gohan performing his Angry Gohan Attack but unfortunately the compression sucks and the 25 second clip runs out to be 17 megs.
+And with our server I dont think we will be able to handle it so most likely by tonight I will be updating with a very compressed smaller version of the AGA.
+
+
+
+
+# You have a kind of sick desperation in your laugh... - 02/13/2001 by Chris
+
+Gamethrust has posted a preview of Bid For Power :: RC 2. This is the first preview on RC 2 and most likely will be the only online preview of it and I suggest you all visit [Gamethrusts Preview of RC 2](https://web.archive.org/web/20010406214208/http://www.gamethrust.com/?page=game_preview&gameid=bidforpowerpc) .
+We have some big plans for RC 2 but I wont be able to comment on those for atleast a few days until then though I suggest you call check out [Gamethrusts Preview of RC 2](https://web.archive.org/web/20010406214208/http://www.gamethrust.com/?page=game_preview&gameid=bidforpowerpc) .
+
+
+
+
+# The things you own end up owning you. - 02/04/2001 by Chris
+
+Just a quick update before I start heavily updating the site over the weekday. I stumbled across this website and it really impressed me. The site is called [BidForPowerWorld](https://web.archive.org/web/20011202104243/http://bfpworld.n3.net/) and looks great.
+From what I hear they are in need of some additional staff members so if you are interested I suggest you send them an email.
+
+
+
+
+# Is Tyler my bad dream? Or am I Tylers? - 01/27/2001 by Chris
+
+I just thought I would update and tell everyone that both Planetquake and Quake3world have posted previews of Bid For Power
+[Quake3worlds Bid For Power Preview](https://web.archive.org/web/20011224224432/http://www.quake3world.com/modifications/bfp.html)
+
+[Planetquakes Bid For Power Preview](https://web.archive.org/web/20020221094152/http://www.planetquake.com/features/articles/bidforpower_a.shtml)
+
+A big thanks to both those sites for doing the previews. In case your wondering if any other sites will be doing previews of Bid For Power in the near future the answer is yes. The GNN network will be doing a preview of Bid For Power and it should be ready soon.
+
+Since everyone on the forums have been wanting to see more shots of our newer maps I will be posting screenshots of each map on our site over the next week.
+
+
+
+
+# Updates soon enough - 01/20/2001 by Chris
+
+We have been hard at work on Bid For Power and soon enough I will be updating the website with several shots from our new maps which include Northern Earth, Glacier Lands, The Cave, NamekXL, and the Cell Tournament map.
+As far as news on models the standard animations for Krillin and Gohan have been completely redone so they play a lot more smoothly now. We are about to shift work to Phase II in the next week on our models.
+
+I will also be updating the site with some roll-over clips of all of our models and I will also post a video clip of our Oozaru model which is fully modeled and skinned.
+
+The big updates should start on monday.
+
+
+
+
+# Hey its working again - 01/07/2001 by Chris
+
+As you can tell our Developer Journals are working once again. They were down quite awhile but now everything seems to be under control.
+First off, as you can tell by Planetnameks preview we have a beta copy of Bid For Power floating around. You can expect another preview soon by a Quake 3 oriented website.
+This beta was made only for websites to preview Bid For Power and we will not be releasing it publicly. There is a lot more to talk about but I'm a little tired right now so I need to get some sleep.
+
+
+
+
+# Emails and the almighty FAQ - 12/16/2000 by Chris
+
+I just wanted to let everyone know before they decide to email me or post on the messageboard about a specific question they should check out our FAQ and About sections, they have tons of information and 9 times out of 10 they have the answers to what your looking for.
+I get a lot of people emailing me asking about when bid for power will be released. The answer is and always has been "when its done" emailing me wont change this.
+
+Check out the FAQ and Abour section as they are a great resource of Bid For Power information.
+
+
+
+
+# Dev Journal Shift and some video clips - 12/14/2000 by Chris
+
+Recently Eddie did some tweaking on the developer journals so now only the five most recent journals are displayed on the main section while the others are displayed in the Dev journal section.
+It looks like I am going to have to push back the release of the in-game clip a little because we had some lower framerates when recording as well as a huge download.
+
+I will be working over the weekend of splicing together some cool action sequences from in-game and hopefully posting it sunday night.
+
+
+
+
+# FYI - 12/09/2000 by Chris
+
+I just updated the site and I just wanted to let everyone know that the vegeta and freeza featured in that update are not finished.
+I have an out of date version of vegeta so his skin is not the final version you will see in Bid For Power though if you check out the last image of vegeta at the bottom (in the pepper city map) you will see his final version.
+
+Last weekend Rodney finished up the animations and tweaks to our freeza model which is now a lot larger looking but the skin is not in its final form. It will be soon though.
+
+Should have a lot more updates coming soon.
+
+
+
+
+# Website Stuff - 11/29/2000 by Chris
+
+It seems the Developer Journals are now displaying properly. It's time for me to do another "State of the BFP” update.
+As you can see we moved servers and the reason for this is because the popularity of Bid For Power is increasing and our previous server was getting hit pretty hard so Gamethrust offered to host us on there server which is a lot faster. I just want to thank Eddie at Gamethrust who has worked his ass off programming the site and setting it up and without Eddie we wouldn’t be at this new server, so big thanks to Eddie.
+
+There are still one or two areas of the site that are offline but they will be online soon enough. Our forums are flowing with tons of activity and we have recently been leaking some really nice screenshots on to the forums so if you haven’t checked them out yet do so or your missing out on the eye candy.
+
+Right now some of the team is focusing on Battle For The Planet and incorporating both Brolly and Oozaru to the game play. As for whether or not Brolly will make it into the Phase I release of Bid For Power is still unclear, we should know by the end of the week.
+
+I’d also like to point out one of the cool little things of Bid For Power that has seemed to go unnoticed so far. In Bid For Power we allow the user to setup the visuals to his or her liking and by doing so we give the player a lot of options. For example our Auras, we allow the player to select between four different versions, this feature applies to a lot of the visuals in Bid For Power and I think the fans will be fairly impressed with it when Bid For Power is released.
+
+The past few updates are what I call “tech” updates which aren’t meant to show off game play as much as they are to show off various effects and features we have put into the current builds. I will be posting a few more tech updates followed by some in-game video clips (still bordering on tech updates) and from there hopefully I will post some game play shots.
+
+
+
+
+# Website Craziness - 11/29/2000 by Chris
+
+As you can tell the website is a little off right now while we are implmenting the developer journals. Sorry for the problems but they should be fixed by the end of the night.
diff --git a/docs/old_dev_journals/dash_dev_journal.md b/docs/old_dev_journals/dash_dev_journal.md
new file mode 100644
index 0000000..49be83c
--- /dev/null
+++ b/docs/old_dev_journals/dash_dev_journal.md
@@ -0,0 +1,256 @@
+# Wallpaper - 01/06/2002 by Dash
+
+I'm making some art, and using **Rodney**'s renders and one of **Ansel**'s Ryuujin draws, I've did this wallpaper for you guys.
+
+
+
+
+
+
+# The Rainbow under the Waterfall - 12/30/2001 by Dash
+
+Today I did some work on the Mystic Mountains Map; deathmatch version. Here are some new screenshots, featuring a Rainbow, generated by the waterfall. Enjoy
+
+
+
+
+
+
+
+
+
+
+
+
+# Mystic Mountains New Pics - 12/29/2001 by Dash
+
+Here are some new pics of **Mystic Mountains**. I finished the terrain, I added some trees ( by Pyrofragger ), but Kit Carson will add the other details and will do the entity work. Anyways it will be ready to be included in BFP release.
+
+
+
+
+
+
+
+(old lost image: https://web.archive.org/web/20020611021434/http://bidforpower.com/dash/mm_3.jpg)
+
+
+
+
+# Mystic Mountains - 12/28/2001 by Dash
+
+**Mystic Mountains**: this is the name of the **BFP CTF** map that me and kit are going to work on in the last days before the release. **Pyrofragger** is doing awesome trees models for it, and when they will be in there will be a lot of scale sense. I've updated the **POTW** with a screenshot of the waterfall in the map, wich isn't the final, but it show up a bit of its effect.
+
+
+
+
+# CTF Maps - 12/26/2001 by Dash
+
+Now that I have some things to show I can say you Merry Christmas. Me and Pyrofragger yesterday have started to think a Bid For Power CTF map and I have already finished the Terrain for it. Just to tell you, this map can contain 20 times Tribecle Creek (Namek Waters) or more. Thanks to Kit Carson for his sky. Wait some days for the completed one (With Giant Waterfalls). By the way, I'm creating the terrain for another CTF map, and on this one me and Kit will do a Team work.
+Here are some pics of the terrain.
+
+
+
+
+
+
+
+
+
+
+
+# Planet YH-85 Finished - 12/16/2001 by Dash
+
+I finished my new map: **Planet YH-85**. Here's a screenshots, featuring **Pyrofragger's** awesome Meteor model.
+
+
+
+
+
+
+# Planet YH-85 | New BFP Map - 12/15/2001 by Dash
+
+Ok that's the story... I'm creating an Urban Terror map, and I created a Terrain for that. I decided to use that terrain, with a different lighting, and different things, to create a map for Bid for Power. The map is now almost complete, I think I'll add a Meteor map object in the crater, if **Pyrofragger** create it for this map. It's name is **Planet YH-85** a desert, very cold and ostile planet, in the Eastern Galaxy.
+Yes, you can see well, you don't need goggles. It's the great Vegeta, and I'm using the Dragon Ball Z Fanpack with the new BFP Beta Test.
+Here are some screenshots:
+
+
+
+
+
+
+
+
+
+
+# Update - 12/14/2001 by Dash
+
+Just to update my Dev-J, here are 3 screenshots of **The Palace** level. Chris hasn't posted these on the main page, so I'm going to post'em here. And about the **Christmas Header**, yes I did it :)
+Booyakayshaa !
+
+
+
+
+
+
+
+
+
+
+
+# Dash Design Redesigned ! - 11/28/2001 by Dash
+
+I've redesigned Dash Design Go to check it ! There are some cool things to see...
+Thanks
+
+
+
+
+# What the new POTW is ? - 11/12/2001 by Dash
+
+Well, today I was working on a Gundam Universe map using EasyGen. But, I said, why don't I make a terrain to use into a Bid For Power map too ?
+**A Rethorical Question.**
+
+So I did this thing using Tribecle Creek (Namek Waters) Textures, and some from Kit's NamekXL (The Ground).
+
+I have yet to add some trees of curse and more details. So I dunno if I'll finish it in time for the imminent relase of the game.
+
+Visit Dash Design !
+**WWW.PLANETQUAKE.COM/DASH**
+
+
+
+
+# Wop, I'm back - 09/05/2001 by Dash
+
+I'm back and I've already made some new things for bfp... the new header of the site for instance. But, I have almost done the changing of the name of all the maps, and some are going to change a bit (lighting for instance) Now Im going to reveal the new names of the maps so pay attention:
+**Kami's Lookout** will change into **The Palace**
+**Namek Waters** will change into **A Strange Planet**
+
+**Abandoned Southern City** and **Northern Earth** will keep their names of curse.
+
+About [Dash Design](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash) my personal site, I will redesign it soon (I have the graphic done but I'm still converting the contenent)
+
+So stay tuned for updates.
+
+
+
+
+# Update - 08/15/2001 by Dash
+
+Bah, that's all I have to say about the event wich are happening. By the way, I will by busy untill the 1st of september, because of internet connection problems that won't be resolved till that date. ( So don't expect to see me a lot online )
+And today it's my birthday, the 15th of August. Happy Birthday Dash !
+
+
+
+
+# Dacity Finished - 07/19/2001 by Dash
+
+Here are 2 screenshots of my Abandoned Southern City. Final Version. I hope to have some Deepest Canyon screenshots ready soon.
+
+
+
+
+
+[[**Dash Design**](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash)]
+
+
+
+
+# The Deepest Canyon and other stuff... - 07/08/2001 by Dash
+
+Just a quick update today: here's a draw of my latest map The Deepest Canyon This map will be quite huge and eyecandy and it will contains those func_breakable entities from Yrgol. Stay tuned !
+
+
+
+[[**Dash Design**](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash)]
+
+Also Bid For Power Zone has got an interview of me. Check it out [here](https://web.archive.org/web/20020223210411/http://www.bfpzone.f2s.com/dash.shtml).
+
+
+
+
+# Namek Waters Final3 - 06/28/2001 by Dash
+
+Namek Waters is finished, all the small bugs are fixed. Check [these](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash/mystuff/namekwaters3/page_01.htm) screenshots, showing in-game Goku and some cool things.
+Note: Namek Waters Final3 is the same of Final2, only optimized.
+
+[[**Dash Design**](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash)]
+
+
+
+
+# Kami's Fixed and other things - 06/26/2001 by Dash
+
+Here's a shot of the latest build of my Kami's Lookout map. Look at the new palms.
+
+
+
+Here are in detail the things I've added and fixed in these days:
+
+
+**::Namek Waters FINAL3 Version:**
+
+- Added a Geiser
+- New Waterfall Texture,
+- New Particle Effect for the waterfalls' splashes,
+- New Lighting, not overbrighted,
+- Gridsize changed from 256 256 256 to 512 512 512.
+
+**::Kami's Lookout FINAL5 Version:**
+
+- New texture for the tiles outside, less repetitive and more small
+like in the show,
+- New Lighting, not overbrighted,
+- Added a red carpet inside.
+- New Palms,
+- Sounds when you hit the palms
+
+[[**Dash Design**](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash)]
+
+
+
+
+# Namek Waters 2nd Edition - 06/24/2001 by Dash
+
+Today I've compiled 2 maps. One is Dover Base for Gundam Universe and one is... **Namek Waters** -Super Special Edition. Here are some screenshots... Enjoy
+[Click Here For The PICS](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash/mystuff/namekwaters2/page_01.htm)
+
+
+[[**Dash Design**](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash)]
+
+
+
+
+# Water Effects - 06/23/2001 by Dash
+
+It's time for the first update of Namek Waters. Here are my new effects for the water (waterfalls, sprays, geisers ). These shots are WITHOUT lighting, so expect tomorrow another update, showing the new nice lighting too.
+
+
+
+
+
+
+Wait till tomorrow for a new update... [[**Dash Design**](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash)]
+
+
+
+
+# Work that I've done and work in progress... - 06/22/2001 by Dash
+
+Wow, my first update in my super cool dev-journal. Here it is what I've done for Bid for Power.
+- Northern Earth,
+- Namek Waters,
+- Kami's Lookout,
+- Abandoned Southern City,
+- All the textures for the maps above,
+- Some minor things.
+
+Now I'm going to fix all my works. Kami's Lookout is finished and fixed, Northern Earth too. I'm going to recompile Namek Waters with a more contrast lighting and maybe make some fixes in Abandoned City.
+Here are some nice in-game screenshots in the city:
+
+[Click Here](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash/mystuff/city1scr/page_01.htm)
+
+I'll upate with screenshots of the updated Namek Waters when I have it ready (Soon). HEY ! Don't forget to check my personal web page:
+
+[[**Dash Design**](https://web.archive.org/web/20020223210411/http://www.planetquake.com/dash)]
diff --git a/docs/pyrofragger_dev_journal.md b/docs/old_dev_journals/pyrofragger_dev_journal.md
similarity index 96%
rename from docs/pyrofragger_dev_journal.md
rename to docs/old_dev_journals/pyrofragger_dev_journal.md
index 90efbfa..6cee84a 100644
--- a/docs/pyrofragger_dev_journal.md
+++ b/docs/old_dev_journals/pyrofragger_dev_journal.md
@@ -1,727 +1,728 @@
-# Meteor - 12/16/2001 by PyroFragger
-
-I spent a couple hours today helping dash with his new map by creating this meteor map object that rests in the crater.
-
-
-
-It looks 10x as cool ingame.
-
-
-
-
-
-
-# Hehe, i bet you thought i forgot:) - 12/13/2001 by PyroFragger
-
-
-Trying to be a little more faithful to my promises, spent the last 4 hours working on this:
-
-
-
-Excuse the slow load times of the bfp server. This is Tetsedah, one of the bfp character drawing contest winners. I sat down and reconstructed the mesh using my basemodel instead of Rodney's. Here's a picture of the sketch that Jeff aka Dark Cloud drew:
-
-
-
-Also, that awkward shadow on the collar of the jacket is just a rendering artifact, no idea what causes this but it's not part of the model so don't worry.
-
-
-
-
-
-# Arggg - 12/12/2001 by PyroFragger
-
-
-My computer crashed and i lost some tweaking work i had done to the model. Not a problem, but i have to postpone the shots till tomorrow. Hopefully this won't happen once i have more RAM. Home stretch:) I aim to have both of the models ingame with temporary solid color skins by the end of this weekend, hopefully the skins will be done really soon.
-/me hands Ansel more gatorade
-
-Go Go Go!
-
-
-
-
-
-
-# Go baby Go - 12/12/2001 by PyroFragger
-
-
-I can have Ryuujin INGAME the day after i get the skin from Ansel. In the mean time, i work on the second model. It\'s close to done, i\'ll show it off later tonight around 10 oclock pm EST. The finish line is in sight, cheer ansel on!
-/me hands Ansel a gatorade
-
-
-
-
-
-
-
-# Ryuujin Model Finished - 12/04/2001 by PyroFragger
-
-
-Finished all the stuff i need to do for Ryuujin and last weekend sent the maps to Ansel for skinning. Here's a pic:
-
-
-
-It was gonna be in the monday update but there was no monday update so i'm posting it here.
-
-
-
-
-
-
-
-# Information - 11/22/2001 by PyroFragger
-
-
-This isn't an eyecandy update, just an informational one. As yrgol said, we can release BFP around a week after he has all the models completed. I have ryuujin completed and ready to go ingame as soon as i get a skin from ansel (who's on vacation at the moment) and will have my second character completed sometime next week after i get another concept from ansel. By that time Pyrate will most likely be ingame, so we should be seeing a release very shortly. In my next update i'll have snazzy posed renders of Ryuujin and Komasurao, not saying when though because there's too much that can happen that can delay a date.
-
-
-
-
-
-
-# Ryuujin Coming Soon - 11/17/2001 by PyroFragger
-
-
-Sometime this week i'll have pictures of Ryuujin for you guys. The model is done and uv mapped, but i have to redo much of the physiquing and ansel still has to skin it. You'll see skinned renders in here soon as well as ingame shots on the mainpage.
-
-
-
-
-
-
-# BaseModel Version 2.0 - 11/13/2001 by PyroFragger
-
-
-For those who care, i reuploaded the BaseModel zip with fixed weighting on the ass. It was collapsing during some of the animations, fixed that right up. Version 2.0, same link as below.
-Also, the biped is HIDDEN, not missing. Just go to the fifth tab and unhide all.
-
-
-
-
-
-
-
-# The Basemodel - 11/12/2001 by PyroFragger
-
-Here it is. It's fully UV Mapped and Physiqued , with all the tags correctly parented to the biped. For those of you without MAX, there is also the 3ds version that is simply uv mapped, you'll have to attach it to a skeleton in whatever program you use and parent the tags aswell. The uv mapping is done in such a way that all you need is ONE texture for the entire upper and lower body. The head has its own mapping (the head mapping is very very messy but it gets the job done, fix it if you feel like it). I recommend 1 128x128 texture for the head and a 512x512 for the body.
-*!*!* UPDATE *!*!*!*! I've ported it to MAX 4. If you've downloaded it already and use MAX4, redownload now!
-
-Download It Here (broken link): https://web.archive.org/web/20011218203246/http://www.bidforpower.com/Pyro/BaseModel.zip
-
-Enjoy. Remember, ALL that i ask is for a little credit, and please, if you're going to edit it, edit it enough that its a new character, don't just change his hair to give him a mohawk and release it. I want to see quality character models made from this.
-
-EDIT: Also, if (for whatever reason) you've seen any bfp skins at all, you'll notice that the uv mapping is set up in the same way as Rodney's. Again, this is NOT Rodney's basemodel, you can check the uv wireframes as well as the actual geometry yourself if you're skeptical.
-
-
-
-
-
-
-
-# Homestead blows - 11/10/2001 by PyroFragger
-
-
-Seems every time i use homestead to show pictures to you guys i exceed my monthly bandwidth in only one day, lol. If anyone has another free hosting site that allows img tagging (i.e. NOT GEOCITIES!), tell me.
-
-
-
-
-
-
-# BAMMMMMMMMMM! - 11/09/2001 by PyroFragger
-
-
-Heh, if you live in another timezone, sue me, its still 11 oclock over here. I still have some work to do on the model but i figure i'll show you what i have today. This is about 6 and a half hours worth of work:
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base1.jpg)
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base2.jpg)
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base3.jpg)
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base4.jpg)
-
-This isn't really Komosurao, just threw his head on the basemodel i'll be releasing later on (most likely tomorrow, i spent more time on the model than i thought i would, but it payed off). The more experienced MAX users on the editing forum should find this useful as either a guide or as a base for their own models. Enjoy.
-
-
-
-
-
-
-
-
-# On Track - 11/08/2001 by PyroFragger
-
-
-All's in order for tomorrow, tomorrow i will be releasing the following to the budding designers in our community:
-A MAX file containing my new basemodel i am using to create my bfp characters. The model will be uv mapped and physiqued to a standard bfp skeleton
-
-A copy of the bfp animation list and an explanation for all of them
-
-I'll also have pics of my actual bfp models.
-
-
-
-
-
-
-
-# Friday Update - 11/06/2001 by PyroFragger
-
-Thank God i'm through with all this work, i'm home free for a bit. Look for an update friday with new stuff.
-
-
-
-
-
-
-# Info - 11/01/2001 by PyroFragger
-
-I don't really spend much time here anymore obviously, mostly because schoolwork this year really picked up. As it stands, i have two exams tomorrow, and two large reports due tuesday and wednesday, followed by some more exams. I'd say after next week, i'll be in the clear for a bit, but until then its tough to say how much i'll be able to do. I MAY be able to squeeze a couple hours of work into Saturday. What i've decided to do is make my own base model for all my characters. Until now, i'd been using Rodney's because it had been determined that the new bfp would just be different skins and head models on the same body. Well we've obviously strayed away from that to give the finished product more variety, so i'd like to make my own basemodel instead of simply just edit other people's work. The same head models will stay though, since those are mine. Once my basemodel is done i'll be able to more easily invent new characters off of it, and i will release the finished, uvmapped, and physiqued basemodel to developers in the community in the form of a bfpsdk to base their own custom bfp models on, provided that in the readme you mention the basemodel came from me.
-Most of you have probably already read yrgol's dev journal. That's exactly what i'll be doing with all of my custom models in the future. As he said, there's no way it will be in the first release (which is why i'll only be releasing one of my models for the first release), but eventually all models will have visually different attacks and auras.
-
-Here's some information on the characters that will eventually be in a future release of bfp:
-
-Komasurao - Decided to make him evil. He has a very dark, cruel energy. He's a very deceptive and ingenious fighter, he can trick other fighters with illusions and his attacks pack a punch.
-
-Ryuujin - Stands for Dragon God in japanese. He is, obviously, a god who has spent the past 3 millenia training his mind and his body. He rarely speaks, and is rarely seen, but he always shows up when there is trouble. His ki is "different," kind of mysterious and very powerful. He wields two scerpent-dragon swords whose blades are made of the same ki as a kienzan.
-
-Tetsedha - A very determined and confident, but not overconfident, fighter. He lives for the fight and trains continously. His ki is very cold and his attacks are slower than those of Taipan. He takes fighting very seriously and is easily a match for anyone.
-
-Taipan - A very aggressive, firey fighter who is easily ticked off and a force to be reckoned with. He is the rival of Tetsedha, and also lives for the fight. The only thing that keeps him going is the continous struggle to become better than him. This competition has made him a very talented and dangerous fighter. His ki is very firey and his attacks are quick.
-
-Drexx - As a teenager, he was very violent and irritable, so he was taken in by a master to try and turn his anger and rage into controllable energy. After 18 years of training, he has become one of the most elite fighters in the universe. He possesses a very electric ki, and he is scared of no one.
-
-Obviously i'm not the most reliable person in the world right now, so i can't make a prediction on when these will all be done, but i can they'll be done soon. I'll try and squeeze in time here and there inbetween working sessions.
-
-
-
-
-
-
-
-# ........ - 10/20/2001 by PyroFragger
-
-
-Well i'm getting over this motivational slump i've been in for the past month. A lot of things been going on where i just either couldn't find the time for BFP, or just didn't feel like working on it. For those that have seen office space, you know what im talking about. There was about a 3 week period where i just didn't care. Anyways, i figure its best to just knock this out and get it over with.
-
-
-
-
-
-
-# Stay away from teh light!!!111111 - 10/12/2001 by PyroFragger
-
-
-Lol...............
-The long story is: I was drinking my tea and eating my crumpets when gooey purple aliens teleported into my room. I was like, WTF? and they said: "Come with us, we will teach you many alien bondage techniques." I forget how it ends.
-
-The short story is: I lost my homestead account and was too tired to reg a new account.
-
-
-
-
-
-
-
-# LOL - 10/11/2001 by PyroFragger
-
-
-Heh, got so busy yesterday completely forgot about yesterday's update. Anyways, later tonight, unless i forget again (j/k, i wont).
-
-
-
-
-
-
-# BOOJAH! - 10/08/2001 by PyroFragger
-
-
-K, im going to show you EVERYTHING ive done so far on wednesday. Unfortunately the contest winners and another 2 models i had going will not go into the initial release of BFP, i'm only allowed to have 1 go in as of now, due to the attack system. However, they will become available in a release sometime after the first release, sorry about that, but its out of my hands. So it'll either by the one i've already pimped on the forums or another model i made that you haven't seen yet, but none of the contest entries will go into the first release, not fair to the other winners. Anyways, check back on wednesday, i'll have some cool stuff.
-
-
-
-
-
-
-# Yadda Yadda Yadda - 09/22/2001 by PyroFragger
-
-
-No pics yet, sorry about that but if you've been following this project long enough you're probably used to it:) Anyways, other things have been occupying my time lately and i just haven't had much time/energy to sit there and work on the bfp models. We do have a deadline though, although i cant say what that is, and we are working towards it so the light at the end of the tunnel is within reach. Whenever i have pics ill show em here, not gonna say when because i dont like breaking promises and it'll be cooler as a surprise.
--P
-
-
-
-
-
-
-
-# New Characters, Sneak Peek - 09/14/2001 by PyroFragger
-
-
-These aren't finished, but i'm a little too burned out right now to continue modeling tonight, so for now you can see the bodies of the two male winners of the drawing contest. Tomorrow, i'll have the female winner as well as all three head models on, and sometime this upcoming week i'll have snazzy new renders of these three and 2 others of my own creation:) K, nuff stalling:
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Taipan_body.jpg)
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Tetsdeha_body.jpg)
-
-Enjoy.
-
-
-
-
-
-
-
-# The Winners!!!! - 09/07/2001 by PyroFragger
-
-Finally, I have sifted through all the contest winners, and here they are. First of all, let me thank everyone who set time aside for this contest. As an artist myself i know sometimes when you enter contests and don't win, it can be depressing, but I saw a lot of impressive drawings in that thread, a lot of you guys have a lot of talent, so keep at it. Without further adieu, here are the entries that made the first cut:
-
-- DR - page 1 http://darkbostudio.hypermart.net/gallery/darkrapsmall.jpg
-
-- Kyuusei - page 3 http://www.bidforpower.com/forum/attachment.php?s=&postid;=319192
-
-- Kaku - page 3 http://yosefselim.homestead.com/files/Kaku.jpg
-
-- MITSUKAI - page 4 http://www.bidforpower.com/forum/attachment.php?s=&postid;=319373
-
-- Gotrun - Page 8 http://www.bidforpower.com/forum/attachment.php?s=&postid;=321186
-
-- God's Entry - Page 8 http://www.bidforpower.com/forum/attachment.php?s=&postid;=321522
-
-- Zone - Page 8 http://zone-archive.com/temporary/zonebfp.jpg
-
-- Adult Chibi - Page 9 http://www.bidforpower.com/forum/attachment.php?s=&postid;=322672
-
-- Asura - Page 10 http://www.bidforpower.com/forum/attachment.php?s=&postid;=323126
-
-- Cpider's Entry - Page 11 http://www.bidforpower.com/forum/attachment.php?s=&postid;=323770
-
-- SSJ-5-Dan's Entry - Page 11 http://www.bidforpower.com/forum/attachment.php?s=&postid;=323994
-
-- Yutaka - Page 11 http://danznation.netfirms.com/scans/yutaka1.jpg
-
-- SSJ-5-Dan's Entry - Page 12 http://www.bidforpower.com/forum/attachment.php?s=&postid;=324903
-
-- Taipan - Page 13 http://www.bidforpower.com/forum/attachment.php?s=&postid;=325435
-
-- MutilatedPuppet's Entry - Page 14 http://www.bidforpower.com/forum/attachment.php?s=&postid;=326670
-http://www.bidforpower.com/forum/attachment.php?s=&postid;=327440
-
-- SSJ-5-Dan's Entry - Page 15 http://www.bidforpower.com/forum/attachment.php?s=&postid;=327420
-
-- Sakana - Page 15 http://members.aol.com/rikoblaze/images/sakana.jpg
-
-- Aenea - Page 15 http://www.bidforpower.com/forum/attachment.php?s=&postid;=327592
-
-- UF's Friend Entry 1 - Page 17 http://www.bidforpower.com/forum/attachment.php?s=&postid;=328948
-
-- UF's Friend Entry 2 - Page 17 http://www.bidforpower.com/forum/attachment.php?s=&postid;=328972
-
-- Genesite Entry 1 - Page 18 http://www.bidforpower.com/forum/attachment.php?s=&postid;=329352
-
-- Chinese Madman - Page 19 http://www.bidforpower.com/forum/attachment.php?s=&postid;=329793
-
-- Demian - Page 20 http://www.bidforpower.com/forum/attachment.php?s=&postid;=330408
-
-- Kuku247's Entry - Page 20 http://www.bidforpower.com/forum/attachment.php?s=&postid;=330494
-
-- Reinako - Page 21 http://home.cfl.rr.com/kashi/reinako.jpg
-
-- Darkido - Page 22 http://www.bidforpower.com/forum/attachment.php?s=&postid;=330874
-
-- Nomas - Page 22 http://www.bidforpower.com/forum/attachment.php?s=&postid;=331009
-
-- Lee - Page 23 http://members.aol.com/thecabreras/images/lee.jpg
-
-- Ree - Page 23 http://members.aol.com/thecabreras/images/ree.jpg
-
-- Cauli - Page 23 http://www.bidforpower.com/forum/attachment.php?s=&postid;=331908
-
-- Wylder - Page 28 http://www.bidforpower.com/forum/attachment.php?s=&postid;=332975
-
-Those were the drawings that I felt, after sifting through all 28 pages of the thread, would be the best concept art to base models on. I made this first cut basically by how well thought out the drawing was. I tried to avoid typical drawings, such as characters in ordinary gi's, with ordinary face and ordinary everything else.
-
-Now, from this list, I narrowed it down even further to a list of 5 males and 5 females. Those are........
-
-- Taipan
-- UF's Friend Entry 2
-- Nomas
-- Wylder
-- Adult Chibi
-
-- Lee
-- Ree
-- Reinako
-- Aenea
-- Sakana
-
-I made these lists by basically picking out the drawings that best conformed to the style of BFP. If this had been an art contest, MutilatedPuppet would have won by far, his pen style artwork is incredible. Had this been a CG contest, Zone would have won by far with his awesome solid-color shading technique. Unfortunately though, neither of these fit the style of BFP, and several others like it also fell through at this point. Great drawings none the less.
-
-Now, on to the grading.
-
-Originality - Does this drawing borrow bits and pieces from dbz characters, or other anime characters, or is it unique?
-
-- Taipan - 9 (Boots like trunks)
-- UF's Friend Entry 2 - 10
-- Nomas - 10
-- Wylder - 10
-- Adult Chibi - 8 (Piccolo's entire cape look)
-
-- Lee - 10
-- Ree - 10
-- Reinako - 10
-- Aenea - 8 (saiyan pads and boots)
-- Sakana - 10
-
-Not too much of a problem here, all pretty much original drawings for the most part.
-
-Design - Is the general design of the character executed well or is it plain?
-
-- Taipan - 10
-- UF's Friend Entry 2 - 10
-- Nomas - 9
-- Wylder - 9
-- Adult Chibi - 8
-
-- Lee - 9
-- Ree - 10
-- Reinako - 9
-- Aenea - 8
-- Sakana - 10
-
-Style - Does the character fit into the BFP/DBZ universe, or generally speaking, does it look good performing ki attacks and flying.
-
-- Taipan - 10
-- UF's Friend Entry 2 - 10
-- Nomas - 10
-- Wylder - 10
-- Adult Chibi - 10
-
-- Lee - 7
-- Ree - 7
-- Reinako - 7
-- Aenea - 9
-- Sakana - 10
-
-Effort - Is it evident that the person spent a lot of time on this drawing, or does it appear rushed.
-
-- Taipan - 10
-- UF's Friend Entry 2 - 10
-- Nomas - 9
-- Wylder - 10
-- Adult Chibi - 9
-
-- Lee - 9
-- Ree - 10
-- Reinako - 10
-- Aenea - 9
-- Sakana - 9
-
-EXTRA CREDIT - Not necessary, but rather a point extra added to the score for each thing i notice that grabs my eye about the drawing.
-
-- Taipan - Excellent work on the CG, +1
-- UF's Friend Entry 2 -
-- Nomas
-- Wylder
-- Adult Chibi - Nice CG, +1
-
-- Lee -
-- Ree -
-- Reinako - +1 for excellent CG work
-- Aenea - Beautiful style, color +2
-- Sakana - Nice job with extra attack drawings, plus CG +2
-
-FINAL SCORE
-
-- Taipan - 40
-- UF's Friend Entry 2 - 40
-- Nomas - 38
-- Wylder - 39
-- Adult Chibi - 36
-
-- Lee - 35
-- Ree - 37
-- Reinako - 37
-- Aenea - 36
-- Sakana - 41
-
-Now, since there is a tie in the male section, what i've decided to do is model BOTH, since each are extremely strong character designs and it'd be a waste to choose one over the other. That makes our winners Taipan, Unnamed(UF's friends entry 2), and Sakana. I will contact the winners tomorrow (a little late to do it tonight, dont you think, heh) and we'll work together to make the character like they envisioned it. Again, thanks to all who entered, it was a great contest and just because you didn't get picked is no reason to feel bad about your art, the only drawback of contests is there can only be so many winners. G'nite.
-
-
-
-
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://members.aol.com/rikoblaze/images/sakana.jpg)
-
-
--Pyro
-
-
-
-
-
-
-
-
-# Komasurao - 09/04/2001 by PyroFragger
-
-Click Here: https://web.archive.org/web/20020106152249/http://alieninstinct.homestead.com/files/Komasurao.jpg
-Its my first serious attempt at skinning an organic model. Hopefully i did ok. It doesn't have a face yet, yes i know, and the skin still needs touchups but atleast you can see what the character itself will look like. Give me suggestions. Komasurao is a combination of "ko"(great) and "masurao"(warrior). Im thinking of putting a japanese symbol on the back of his cape.
-
-BTW, im gonna send it to rodney so he can render it out like the others and make a character sheet for it, this is just a temporary render.
-
-
-
-
-
-
-
-# New Character Delay - 09/03/2001 by PyroFragger
-
-
-K, i do have them done, but they are still being worked on and I refuse to rush them. Tomorrow I'll have atleast one finished, SKINNED model rendered in the style as the others. Drawings look good btw, keep it up. Remember, you have till friday.
-
-
-
-
-
-
-# BFP Character Drawing Contest - 09/02/2001 by PyroFragger
-
-I'm sure most of you have seen this by now but if you haven't, for all you artistically inclined people, there's a drawing contest to determine the look of one new bfp character. Details can be found HERE: https://web.archive.org/web/20011031125532/http://www.thejasper.com/DBZ/CosDBZ.html
-Good luck.
-
-PS: HUMAN/SAIYAN!!! IF YOUR CHARACTER IS NOT HUMAN/SAIYAN IT WILL NOT BE CONSIDERED!! NUFF SAID.
-
-
-
-
-
-
-
-# Take me, I'm yours. - 06/06/2001 by PyroFragger
-
-It's been decided, the music video will be a long one involving all characters, transitions between each, and a short intro at the beginning. The reason for these videos is to show off all 5 attacks for each characters, as well as melee action and the latest effects, animated, to some of your favorite tunes, not only because we are bored and need something to stall with while we keep pressing for release:) Look for these in the next week (still editing them to fit the music).
-As of now, remisser (the music composer, for those that don't know) is finishing some ingame music and it is coming along really well. He'll have his own journal with the new layout, but for right now, he's sharing mine. Expect more from him soon.
-
--P
-
-
-
-
-
-
-
-# I stubbed my toenail and now it smells like monkey... - 05/20/2001 by PyroFragger
-
-I've been talking with a composer over the past couple of days and he is gonna do some music for bfp. I may be using some music from him to do some of the music videos. You can expect all of the music videos with our new layout, which will be coming soon. Thanks for your patience.
-P.S. If you get bored or tired of waiting, go to www.newgrounds.com:)
-
-
-
-
-
-
-
-# The little engine that can - 05/09/2001 by PyroFragger
-
-I know its been awhile since you've seen an update with new material, but thats only because we are spending more time trying to get this out to you guys. After a long standstill, we are finally getting moving again and BFP will be out before you know it. All we ask for is a little more patience. As for the music videos, I got 185 emails from fans requesting songs. Obviously i couldnt look at them all so Im going to weed through the topics and find the most popular songs for each character. If you want to get your voice in, I suggest replying in one of those topics in the next 30 mins. Later.
-
-
-
-
-
-
-# Look honey he plays hockey - 05/03/2001 by PyroFragger
-
-I just wanted to say this cause no one else is. A lot of people are probably frustrated that we haven't released yet. Well we are definately more anxious to release than any of you are. I can't go into details, but you have to believe that we are doing everything we can to get this out as soon as we can. Bare with us for a little bit. There are things preventing the release that cannot be publicly announced, but we are ready when it gets cleared up.
-Anyways, I may do some music videos or something promotional featuring all the characters, all 5 of their attacks, and a transformation. I was going to do this before, but never got around to it. Any song requests for certain people. Krillin automatically gets "Smack my Bitch Up" by prodigy, so thats not an opetion:) But I need song ideas for the rest.
-
-
-
-
-
-
-
-# ...Ick, no pooh title for this one... - 04/27/2001 by PyroFragger
-
-My internet went down yesterday. Im on a cable modem, so when they work on the servers, my internet is running at 14,4 baud modem speed at best. Its awful. Well right after i said they would be up and that i still had time on the forums, the cable stopped working. I ran the test on the modem and the cable light was out. Now homestead wont let me on because they are upgrading the server or something, so until then, they arent up. I did have them done yesterday, I swear:) Anyways, hopefully later today i can get into my homestead account. For your information they are pics of Majin Dabura and Chibi Pan.
-
-
-
-
-
-# ...And most of all whinnie the pooh.. - 04/27/2001 by PyroFragger
-
-'Bout time.
-(old lost image: https://web.archive.org/web/20011218203246/http://pyrofragger.homestead.com/files/p21.jpg)
-
-
-
-Enjoy. Still to come: Cell form 1 and cell jr.
-
-Funny quote of the day: "If a blonde throws a grenade at you, pull the pin and throw it back. If a blonde throws a pin at you, run like hell, she's got the grenade in her mouth!"
-
-
-
-
-
-
-
-# ...there's rabbit..and piglet....and there's owl..... - 04/23/2001 by PyroFragger
-
-This has nothing to do with BFP at all (except for the little blurb at the bottom) but I took the time to learn MAX modeling over the past couple of days. All high poly stuff, but the princliple of making the initial mesh is still low poly, so I may transcend from Raydream to MAX, I just hope it improves my quality. Here's the thing i made, anyways. My first mesh completely modeled in MAX:)
-
-
-BFP:
-Im gonna go against what I said last time just this once so that you can look forward to the part 2 model selection on Thursday.
-
-Funny Quote of the Day: "Never call Victoria's Secret and ask if something comes in childrens size"
-
-
-
-
-
-
-
-# ...and Tigger, and little Roo..... - 04/14/2001 by PyroFragger
-
-*BIG* update:
-First, I just want to say that the gi versions of Krillin and Gohan will not be part of the main download on account of some problems I was having with MAX (totally corrupted the file). They will be released later on as an addon. Im incredibly tired of physiquing and animating so today I took a break from all of that. Here is the result:
-
-
-
-Hope that tides you over for a while. I also want to say that I am no longer giving out public release dates because things tend to happen that are not in my favor, and I always end up with a lot of disgruntled people on my case.
-
-You can expect more models sometime soon, but like i said, im not going to say when:)
-
-As for why I'm working on models that fit in phase 3, it is because most of our phase 2 models are done and I was just pretty bored and tired of animating. Now I have them done, and don't have to worry about them when the time comes.
-
-Check out all the losers wearing dbz costumes (sorry if you're one of them): https://web.archive.org/web/20011031125532/http://www.thejasper.com/DBZ/CosDBZ.html
-
-Peace
-
--P
-
-
-
-
-
-
-
-
-# ...A donkey named Eyore is his friend... - 04/12/2001 by PyroFragger
-
-Good things and bad things.
-Good: I found an easy way around the deformation problems the two models have been having so I can finally get them working right. As of now, they deform like hell, but that will all be fixed. If all goes well, I will have in-game shots of them tomorrow.
-
-Bad: I have put my website on hiatus so those bitching about it will have to chill. Things come up, I'll finish it when I have nothing at all to work on. In the mean time, you can read other tutorials, but mine's gonna take longer than I thought because I have to give the same steps for two different modeling packages, so it takes twice as long. Check back Saturday for a status report, I *may* be finished the tut by then, but don't hold me to that (and you wonder why we don't give release dates when we don't know if we can keep them:) I have other stuff to show off too on Saturday. Peace.
-
--P
-
-
-
-
-
-
-
-# ...For most of his childhood days... - 04/05/2001 by PyroFragger
-
-Check for the skins in Ansel's journal in the next few minutes. I gotta roll out, so I dont have time to upload a shot. The torso is going to be seperated from the legs because i dont have tags on them yet. Tomorrow I can work the kinks out and maybe have an ingame screen for each of them.
--P
-
-
-
-
-
-
-
-# ..There lies an enchanted neighborhood - 04/03/2001 by PyroFragger
-
-I will be finished with everything relating to phase 1 by tomorrow, so my site will be finished soon. I had hoped to finish it much sooner but such is life. Check for screenshots either tomorrow or thursday of them skinned.
-
-
-
-
-
-
-# ...where Christopher Robin plays... - 03/27/2001 by PyroFragger
-
-Those expecting the tutorial will have to wait. BFP work comes first, tut comes second. If you didnt already know we are finishing things and I have to of course get my models done really soon. They are already attached to skeletons and are animated, in the process of getting skinned, so they will be done soon. I may have the tut up by this weekend. Ive got more work to show later, not for phase 1.
--P
-
-
-
-
-
-
-
-# Deep in the hundred acre woods..... - 03/16/2001 by PyroFragger
-
-Applied BFP standard animations and attack animations to the new krillin and gohan, ready for skinning soon. Also, I am designing a website for myself that will include shots of m bfp work and my first ever modeling tutorial that is nearing completion.
--P
-
-
-
-
-
-
-
-# New Krillin and Gohan - 02/24/2001 by PyroFragger
-
-Two reasons for this:
-1) tired of everyone telling me to change their outfits to Gi's (Your post clenched it Mr. Krillin ;)
-
-2) wasnt happy with the level of quality of the models compared to my newer models
-
-So......here they are, unskinned:
-
-Krillin: 790 polygons
-Gohan: 892 polygons
-
-
-
-FEEDBACK
-
-
-
-
-
-
-
-# Random Updates - 02/09/2001 by PyroFragger
-
-Been working on Cell perfect a tad bit more to get it ready for Roney's skin job. Cell form 1 is next. I also may be making a RoQ movie file for the ending of BFTP. Lastly, I've been working on a 32 person tourney/capture the dragonballs map. Until phase 2 is released, itll just be a big tourney map. Screenshots of that as well soon. Thats about it, later.
-
-
-
-
-
-
-# One more update from me today - 02/09/2001 by PyroFragger
-
-As I said, I've been working on cell. This is the result, a more stylized, sleek, and muscular Cell:
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://pyrofragger.homestead.com/files/Final_Cell.jpg)
-
-Feedback.
-
-
-
-
-
-
-
-# First Update - 12/17/2000 by PyroFragger
-
-My first dev journal on the new site. As of now, im tweaking Gohan and Krillin while the others finish up. Work continues on Cell as well:
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://pyrofragger.homestead.com/files/p_cell.jpg)
-
-Anyways, i dont have much to do right now for phase 1, so I will probably be starting some giant tourney maps due to the 1.27 patch.
-
-My first dev journal on the new site. As of now, im tweaking Gohan and Krillin while the others finish up. Work continues on Cell as well:
-
-(old lost image: https://web.archive.org/web/20011218203246im_/http://pyrofragger.homestead.com/files/p_cell.jpg)
-
+# Meteor - 12/16/2001 by PyroFragger
+
+I spent a couple hours today helping dash with his new map by creating this meteor map object that rests in the crater.
+
+
+
+It looks 10x as cool ingame.
+
+
+
+
+
+
+# Hehe, i bet you thought i forgot:) - 12/13/2001 by PyroFragger
+
+
+Trying to be a little more faithful to my promises, spent the last 4 hours working on this:
+
+
+
+Excuse the slow load times of the bfp server. This is Tetsedah, one of the bfp character drawing contest winners. I sat down and reconstructed the mesh using my basemodel instead of Rodney's. Here's a picture of the sketch that Jeff aka Dark Cloud drew:
+
+
+
+Also, that awkward shadow on the collar of the jacket is just a rendering artifact, no idea what causes this but it's not part of the model so don't worry.
+
+
+
+
+
+# Arggg - 12/12/2001 by PyroFragger
+
+
+My computer crashed and i lost some tweaking work i had done to the model. Not a problem, but i have to postpone the shots till tomorrow. Hopefully this won't happen once i have more RAM. Home stretch:) I aim to have both of the models ingame with temporary solid color skins by the end of this weekend, hopefully the skins will be done really soon.
+/me hands Ansel more gatorade
+
+Go Go Go!
+
+
+
+
+
+
+# Go baby Go - 12/12/2001 by PyroFragger
+
+
+I can have Ryuujin INGAME the day after i get the skin from Ansel. In the mean time, i work on the second model. It\'s close to done, i\'ll show it off later tonight around 10 oclock pm EST. The finish line is in sight, cheer ansel on!
+/me hands Ansel a gatorade
+
+
+
+
+
+
+
+# Ryuujin Model Finished - 12/04/2001 by PyroFragger
+
+
+Finished all the stuff i need to do for Ryuujin and last weekend sent the maps to Ansel for skinning. Here's a pic:
+
+
+
+It was gonna be in the monday update but there was no monday update so i'm posting it here.
+
+
+
+
+
+
+
+# Information - 11/22/2001 by PyroFragger
+
+
+This isn't an eyecandy update, just an informational one. As yrgol said, we can release BFP around a week after he has all the models completed. I have ryuujin completed and ready to go ingame as soon as i get a skin from ansel (who's on vacation at the moment) and will have my second character completed sometime next week after i get another concept from ansel. By that time Pyrate will most likely be ingame, so we should be seeing a release very shortly. In my next update i'll have snazzy posed renders of Ryuujin and Komasurao, not saying when though because there's too much that can happen that can delay a date.
+
+
+
+
+
+
+# Ryuujin Coming Soon - 11/17/2001 by PyroFragger
+
+
+Sometime this week i'll have pictures of Ryuujin for you guys. The model is done and uv mapped, but i have to redo much of the physiquing and ansel still has to skin it. You'll see skinned renders in here soon as well as ingame shots on the mainpage.
+
+
+
+
+
+
+# BaseModel Version 2.0 - 11/13/2001 by PyroFragger
+
+
+For those who care, i reuploaded the BaseModel zip with fixed weighting on the ass. It was collapsing during some of the animations, fixed that right up. Version 2.0, same link as below.
+Also, the biped is HIDDEN, not missing. Just go to the fifth tab and unhide all.
+
+
+
+
+
+
+
+# The Basemodel - 11/12/2001 by PyroFragger
+
+Here it is. It's fully UV Mapped and Physiqued , with all the tags correctly parented to the biped. For those of you without MAX, there is also the 3ds version that is simply uv mapped, you'll have to attach it to a skeleton in whatever program you use and parent the tags aswell. The uv mapping is done in such a way that all you need is ONE texture for the entire upper and lower body. The head has its own mapping (the head mapping is very very messy but it gets the job done, fix it if you feel like it). I recommend 1 128x128 texture for the head and a 512x512 for the body.
+*!*!* UPDATE *!*!*!*! I've ported it to MAX 4. If you've downloaded it already and use MAX4, redownload now!
+
+Download It Here (broken link): https://web.archive.org/web/20011218203246/http://www.bidforpower.com/Pyro/BaseModel.zip
+
+Enjoy. Remember, ALL that i ask is for a little credit, and please, if you're going to edit it, edit it enough that its a new character, don't just change his hair to give him a mohawk and release it. I want to see quality character models made from this.
+
+EDIT: Also, if (for whatever reason) you've seen any bfp skins at all, you'll notice that the uv mapping is set up in the same way as Rodney's. Again, this is NOT Rodney's basemodel, you can check the uv wireframes as well as the actual geometry yourself if you're skeptical.
+
+
+
+
+
+
+
+# Homestead blows - 11/10/2001 by PyroFragger
+
+
+Seems every time i use homestead to show pictures to you guys i exceed my monthly bandwidth in only one day, lol. If anyone has another free hosting site that allows img tagging (i.e. NOT GEOCITIES!), tell me.
+
+
+
+
+
+
+# BAMMMMMMMMMM! - 11/09/2001 by PyroFragger
+
+
+Heh, if you live in another timezone, sue me, its still 11 oclock over here. I still have some work to do on the model but i figure i'll show you what i have today. This is about 6 and a half hours worth of work:
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base1.jpg)
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base2.jpg)
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base3.jpg)
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Base4.jpg)
+
+This isn't really Komosurao, just threw his head on the basemodel i'll be releasing later on (most likely tomorrow, i spent more time on the model than i thought i would, but it payed off). The more experienced MAX users on the editing forum should find this useful as either a guide or as a base for their own models. Enjoy.
+
+
+
+
+
+
+
+
+# On Track - 11/08/2001 by PyroFragger
+
+
+All's in order for tomorrow, tomorrow i will be releasing the following to the budding designers in our community:
+A MAX file containing my new basemodel i am using to create my bfp characters. The model will be uv mapped and physiqued to a standard bfp skeleton
+
+A copy of the bfp animation list and an explanation for all of them
+
+I'll also have pics of my actual bfp models.
+
+
+
+
+
+
+
+# Friday Update - 11/06/2001 by PyroFragger
+
+Thank God i'm through with all this work, i'm home free for a bit. Look for an update friday with new stuff.
+
+
+
+
+
+
+# Info - 11/01/2001 by PyroFragger
+
+I don't really spend much time here anymore obviously, mostly because schoolwork this year really picked up. As it stands, i have two exams tomorrow, and two large reports due tuesday and wednesday, followed by some more exams. I'd say after next week, i'll be in the clear for a bit, but until then its tough to say how much i'll be able to do. I MAY be able to squeeze a couple hours of work into Saturday. What i've decided to do is make my own base model for all my characters. Until now, i'd been using Rodney's because it had been determined that the new bfp would just be different skins and head models on the same body. Well we've obviously strayed away from that to give the finished product more variety, so i'd like to make my own basemodel instead of simply just edit other people's work. The same head models will stay though, since those are mine. Once my basemodel is done i'll be able to more easily invent new characters off of it, and i will release the finished, uvmapped, and physiqued basemodel to developers in the community in the form of a bfpsdk to base their own custom bfp models on, provided that in the readme you mention the basemodel came from me.
+Most of you have probably already read yrgol's dev journal. That's exactly what i'll be doing with all of my custom models in the future. As he said, there's no way it will be in the first release (which is why i'll only be releasing one of my models for the first release), but eventually all models will have visually different attacks and auras.
+
+Here's some information on the characters that will eventually be in a future release of bfp:
+
+Komasurao - Decided to make him evil. He has a very dark, cruel energy. He's a very deceptive and ingenious fighter, he can trick other fighters with illusions and his attacks pack a punch.
+
+Ryuujin - Stands for Dragon God in japanese. He is, obviously, a god who has spent the past 3 millenia training his mind and his body. He rarely speaks, and is rarely seen, but he always shows up when there is trouble. His ki is "different," kind of mysterious and very powerful. He wields two scerpent-dragon swords whose blades are made of the same ki as a kienzan.
+
+Tetsedha - A very determined and confident, but not overconfident, fighter. He lives for the fight and trains continously. His ki is very cold and his attacks are slower than those of Taipan. He takes fighting very seriously and is easily a match for anyone.
+
+Taipan - A very aggressive, firey fighter who is easily ticked off and a force to be reckoned with. He is the rival of Tetsedha, and also lives for the fight. The only thing that keeps him going is the continous struggle to become better than him. This competition has made him a very talented and dangerous fighter. His ki is very firey and his attacks are quick.
+
+Drexx - As a teenager, he was very violent and irritable, so he was taken in by a master to try and turn his anger and rage into controllable energy. After 18 years of training, he has become one of the most elite fighters in the universe. He possesses a very electric ki, and he is scared of no one.
+
+Obviously i'm not the most reliable person in the world right now, so i can't make a prediction on when these will all be done, but i can they'll be done soon. I'll try and squeeze in time here and there inbetween working sessions.
+
+
+
+
+
+
+
+# ........ - 10/20/2001 by PyroFragger
+
+
+Well i'm getting over this motivational slump i've been in for the past month. A lot of things been going on where i just either couldn't find the time for BFP, or just didn't feel like working on it. For those that have seen office space, you know what im talking about. There was about a 3 week period where i just didn't care. Anyways, i figure its best to just knock this out and get it over with.
+
+
+
+
+
+
+# Stay away from teh light!!!111111 - 10/12/2001 by PyroFragger
+
+
+Lol...............
+The long story is: I was drinking my tea and eating my crumpets when gooey purple aliens teleported into my room. I was like, WTF? and they said: "Come with us, we will teach you many alien bondage techniques." I forget how it ends.
+
+The short story is: I lost my homestead account and was too tired to reg a new account.
+
+
+
+
+
+
+
+# LOL - 10/11/2001 by PyroFragger
+
+
+Heh, got so busy yesterday completely forgot about yesterday's update. Anyways, later tonight, unless i forget again (j/k, i wont).
+
+
+
+
+
+
+# BOOJAH! - 10/08/2001 by PyroFragger
+
+
+K, im going to show you EVERYTHING ive done so far on wednesday. Unfortunately the contest winners and another 2 models i had going will not go into the initial release of BFP, i'm only allowed to have 1 go in as of now, due to the attack system. However, they will become available in a release sometime after the first release, sorry about that, but its out of my hands. So it'll either by the one i've already pimped on the forums or another model i made that you haven't seen yet, but none of the contest entries will go into the first release, not fair to the other winners. Anyways, check back on wednesday, i'll have some cool stuff.
+
+
+
+
+
+
+# Yadda Yadda Yadda - 09/22/2001 by PyroFragger
+
+
+No pics yet, sorry about that but if you've been following this project long enough you're probably used to it:) Anyways, other things have been occupying my time lately and i just haven't had much time/energy to sit there and work on the bfp models. We do have a deadline though, although i cant say what that is, and we are working towards it so the light at the end of the tunnel is within reach. Whenever i have pics ill show em here, not gonna say when because i dont like breaking promises and it'll be cooler as a surprise.
+-P
+
+
+
+
+
+
+
+# New Characters, Sneak Peek - 09/14/2001 by PyroFragger
+
+
+These aren't finished, but i'm a little too burned out right now to continue modeling tonight, so for now you can see the bodies of the two male winners of the drawing contest. Tomorrow, i'll have the female winner as well as all three head models on, and sometime this upcoming week i'll have snazzy new renders of these three and 2 others of my own creation:) K, nuff stalling:
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Taipan_body.jpg)
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://alieninstinct.homestead.com/files/Tetsdeha_body.jpg)
+
+Enjoy.
+
+
+
+
+
+
+
+# The Winners!!!! - 09/07/2001 by PyroFragger
+
+Finally, I have sifted through all the contest winners, and here they are. First of all, let me thank everyone who set time aside for this contest. As an artist myself i know sometimes when you enter contests and don't win, it can be depressing, but I saw a lot of impressive drawings in that thread, a lot of you guys have a lot of talent, so keep at it. Without further adieu, here are the entries that made the first cut:
+
+- DR - page 1 http://darkbostudio.hypermart.net/gallery/darkrapsmall.jpg
+
+- Kyuusei - page 3 http://www.bidforpower.com/forum/attachment.php?s=&postid;=319192
+
+- Kaku - page 3 http://yosefselim.homestead.com/files/Kaku.jpg
+
+- MITSUKAI - page 4 http://www.bidforpower.com/forum/attachment.php?s=&postid;=319373
+
+- Gotrun - Page 8 http://www.bidforpower.com/forum/attachment.php?s=&postid;=321186
+
+- God's Entry - Page 8 http://www.bidforpower.com/forum/attachment.php?s=&postid;=321522
+
+- Zone - Page 8 http://zone-archive.com/temporary/zonebfp.jpg
+
+- Adult Chibi - Page 9 http://www.bidforpower.com/forum/attachment.php?s=&postid;=322672
+
+- Asura - Page 10 http://www.bidforpower.com/forum/attachment.php?s=&postid;=323126
+
+- Cpider's Entry - Page 11 http://www.bidforpower.com/forum/attachment.php?s=&postid;=323770
+
+- SSJ-5-Dan's Entry - Page 11 http://www.bidforpower.com/forum/attachment.php?s=&postid;=323994
+
+- Yutaka - Page 11 http://danznation.netfirms.com/scans/yutaka1.jpg
+
+- SSJ-5-Dan's Entry - Page 12 http://www.bidforpower.com/forum/attachment.php?s=&postid;=324903
+
+- Taipan - Page 13 http://www.bidforpower.com/forum/attachment.php?s=&postid;=325435
+
+- MutilatedPuppet's Entry - Page 14 http://www.bidforpower.com/forum/attachment.php?s=&postid;=326670
+http://www.bidforpower.com/forum/attachment.php?s=&postid;=327440
+
+- SSJ-5-Dan's Entry - Page 15 http://www.bidforpower.com/forum/attachment.php?s=&postid;=327420
+
+- Sakana - Page 15 http://members.aol.com/rikoblaze/images/sakana.jpg
+
+- Aenea - Page 15 http://www.bidforpower.com/forum/attachment.php?s=&postid;=327592
+
+- UF's Friend Entry 1 - Page 17 http://www.bidforpower.com/forum/attachment.php?s=&postid;=328948
+
+- UF's Friend Entry 2 - Page 17 http://www.bidforpower.com/forum/attachment.php?s=&postid;=328972
+
+- Genesite Entry 1 - Page 18 http://www.bidforpower.com/forum/attachment.php?s=&postid;=329352
+
+- Chinese Madman - Page 19 http://www.bidforpower.com/forum/attachment.php?s=&postid;=329793
+
+- Demian - Page 20 http://www.bidforpower.com/forum/attachment.php?s=&postid;=330408
+
+- Kuku247's Entry - Page 20 http://www.bidforpower.com/forum/attachment.php?s=&postid;=330494
+
+- Reinako - Page 21 http://home.cfl.rr.com/kashi/reinako.jpg
+
+- Darkido - Page 22 http://www.bidforpower.com/forum/attachment.php?s=&postid;=330874
+
+- Nomas - Page 22 http://www.bidforpower.com/forum/attachment.php?s=&postid;=331009
+
+- Lee - Page 23 http://members.aol.com/thecabreras/images/lee.jpg
+
+- Ree - Page 23 http://members.aol.com/thecabreras/images/ree.jpg
+
+- Cauli - Page 23 http://www.bidforpower.com/forum/attachment.php?s=&postid;=331908
+
+- Wylder - Page 28 http://www.bidforpower.com/forum/attachment.php?s=&postid;=332975
+
+Those were the drawings that I felt, after sifting through all 28 pages of the thread, would be the best concept art to base models on. I made this first cut basically by how well thought out the drawing was. I tried to avoid typical drawings, such as characters in ordinary gi's, with ordinary face and ordinary everything else.
+
+Now, from this list, I narrowed it down even further to a list of 5 males and 5 females. Those are........
+
+- Taipan
+- UF's Friend Entry 2
+- Nomas
+- Wylder
+- Adult Chibi
+
+- Lee
+- Ree
+- Reinako
+- Aenea
+- Sakana
+
+I made these lists by basically picking out the drawings that best conformed to the style of BFP. If this had been an art contest, MutilatedPuppet would have won by far, his pen style artwork is incredible. Had this been a CG contest, Zone would have won by far with his awesome solid-color shading technique. Unfortunately though, neither of these fit the style of BFP, and several others like it also fell through at this point. Great drawings none the less.
+
+Now, on to the grading.
+
+Originality - Does this drawing borrow bits and pieces from dbz characters, or other anime characters, or is it unique?
+
+- Taipan - 9 (Boots like trunks)
+- UF's Friend Entry 2 - 10
+- Nomas - 10
+- Wylder - 10
+- Adult Chibi - 8 (Piccolo's entire cape look)
+
+- Lee - 10
+- Ree - 10
+- Reinako - 10
+- Aenea - 8 (saiyan pads and boots)
+- Sakana - 10
+
+Not too much of a problem here, all pretty much original drawings for the most part.
+
+Design - Is the general design of the character executed well or is it plain?
+
+- Taipan - 10
+- UF's Friend Entry 2 - 10
+- Nomas - 9
+- Wylder - 9
+- Adult Chibi - 8
+
+- Lee - 9
+- Ree - 10
+- Reinako - 9
+- Aenea - 8
+- Sakana - 10
+
+Style - Does the character fit into the BFP/DBZ universe, or generally speaking, does it look good performing ki attacks and flying.
+
+- Taipan - 10
+- UF's Friend Entry 2 - 10
+- Nomas - 10
+- Wylder - 10
+- Adult Chibi - 10
+
+- Lee - 7
+- Ree - 7
+- Reinako - 7
+- Aenea - 9
+- Sakana - 10
+
+Effort - Is it evident that the person spent a lot of time on this drawing, or does it appear rushed.
+
+- Taipan - 10
+- UF's Friend Entry 2 - 10
+- Nomas - 9
+- Wylder - 10
+- Adult Chibi - 9
+
+- Lee - 9
+- Ree - 10
+- Reinako - 10
+- Aenea - 9
+- Sakana - 9
+
+EXTRA CREDIT - Not necessary, but rather a point extra added to the score for each thing i notice that grabs my eye about the drawing.
+
+- Taipan - Excellent work on the CG, +1
+- UF's Friend Entry 2 -
+- Nomas
+- Wylder
+- Adult Chibi - Nice CG, +1
+
+- Lee -
+- Ree -
+- Reinako - +1 for excellent CG work
+- Aenea - Beautiful style, color +2
+- Sakana - Nice job with extra attack drawings, plus CG +2
+
+FINAL SCORE
+
+- Taipan - 40
+- UF's Friend Entry 2 - 40
+- Nomas - 38
+- Wylder - 39
+- Adult Chibi - 36
+
+- Lee - 35
+- Ree - 37
+- Reinako - 37
+- Aenea - 36
+- Sakana - 41
+
+Now, since there is a tie in the male section, what i've decided to do is model BOTH, since each are extremely strong character designs and it'd be a waste to choose one over the other. That makes our winners Taipan, Unnamed(UF's friends entry 2), and Sakana. I will contact the winners tomorrow (a little late to do it tonight, dont you think, heh) and we'll work together to make the character like they envisioned it. Again, thanks to all who entered, it was a great contest and just because you didn't get picked is no reason to feel bad about your art, the only drawback of contests is there can only be so many winners. G'nite.
+
+
+
+
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://members.aol.com/rikoblaze/images/sakana.jpg)
+
+
+-Pyro
+
+
+
+
+
+
+
+
+# Komasurao - 09/04/2001 by PyroFragger
+
+
+
+Its my first serious attempt at skinning an organic model. Hopefully i did ok. It doesn't have a face yet, yes i know, and the skin still needs touchups but atleast you can see what the character itself will look like. Give me suggestions. Komasurao is a combination of "ko"(great) and "masurao"(warrior). Im thinking of putting a japanese symbol on the back of his cape.
+
+BTW, im gonna send it to rodney so he can render it out like the others and make a character sheet for it, this is just a temporary render.
+
+
+
+
+
+
+
+# New Character Delay - 09/03/2001 by PyroFragger
+
+
+K, i do have them done, but they are still being worked on and I refuse to rush them. Tomorrow I'll have atleast one finished, SKINNED model rendered in the style as the others. Drawings look good btw, keep it up. Remember, you have till friday.
+
+
+
+
+
+
+# BFP Character Drawing Contest - 09/02/2001 by PyroFragger
+
+I'm sure most of you have seen this by now but if you haven't, for all you artistically inclined people, there's a drawing contest to determine the look of one new bfp character. Details can be found HERE: https://web.archive.org/web/20011031125532/http://www.thejasper.com/DBZ/CosDBZ.html
+Good luck.
+
+PS: HUMAN/SAIYAN!!! IF YOUR CHARACTER IS NOT HUMAN/SAIYAN IT WILL NOT BE CONSIDERED!! NUFF SAID.
+
+
+
+
+
+
+
+# Take me, I'm yours. - 06/06/2001 by PyroFragger
+
+It's been decided, the music video will be a long one involving all characters, transitions between each, and a short intro at the beginning. The reason for these videos is to show off all 5 attacks for each characters, as well as melee action and the latest effects, animated, to some of your favorite tunes, not only because we are bored and need something to stall with while we keep pressing for release:) Look for these in the next week (still editing them to fit the music).
+As of now, remisser (the music composer, for those that don't know) is finishing some ingame music and it is coming along really well. He'll have his own journal with the new layout, but for right now, he's sharing mine. Expect more from him soon.
+
+-P
+
+
+
+
+
+
+
+# I stubbed my toenail and now it smells like monkey... - 05/20/2001 by PyroFragger
+
+I've been talking with a composer over the past couple of days and he is gonna do some music for bfp. I may be using some music from him to do some of the music videos. You can expect all of the music videos with our new layout, which will be coming soon. Thanks for your patience.
+P.S. If you get bored or tired of waiting, go to www.newgrounds.com:)
+
+
+
+
+
+
+
+# The little engine that can - 05/09/2001 by PyroFragger
+
+I know its been awhile since you've seen an update with new material, but thats only because we are spending more time trying to get this out to you guys. After a long standstill, we are finally getting moving again and BFP will be out before you know it. All we ask for is a little more patience. As for the music videos, I got 185 emails from fans requesting songs. Obviously i couldnt look at them all so Im going to weed through the topics and find the most popular songs for each character. If you want to get your voice in, I suggest replying in one of those topics in the next 30 mins. Later.
+
+
+
+
+
+
+# Look honey he plays hockey - 05/03/2001 by PyroFragger
+
+I just wanted to say this cause no one else is. A lot of people are probably frustrated that we haven't released yet. Well we are definately more anxious to release than any of you are. I can't go into details, but you have to believe that we are doing everything we can to get this out as soon as we can. Bare with us for a little bit. There are things preventing the release that cannot be publicly announced, but we are ready when it gets cleared up.
+Anyways, I may do some music videos or something promotional featuring all the characters, all 5 of their attacks, and a transformation. I was going to do this before, but never got around to it. Any song requests for certain people. Krillin automatically gets "Smack my Bitch Up" by prodigy, so thats not an opetion:) But I need song ideas for the rest.
+
+
+
+
+
+
+
+# ...Ick, no pooh title for this one... - 04/27/2001 by PyroFragger
+
+My internet went down yesterday. Im on a cable modem, so when they work on the servers, my internet is running at 14,4 baud modem speed at best. Its awful. Well right after i said they would be up and that i still had time on the forums, the cable stopped working. I ran the test on the modem and the cable light was out. Now homestead wont let me on because they are upgrading the server or something, so until then, they arent up. I did have them done yesterday, I swear:) Anyways, hopefully later today i can get into my homestead account. For your information they are pics of Majin Dabura and Chibi Pan.
+
+
+
+
+
+# ...And most of all whinnie the pooh.. - 04/27/2001 by PyroFragger
+
+'Bout time.
+(old lost image: https://web.archive.org/web/20011218203246/http://pyrofragger.homestead.com/files/p21.jpg)
+
+
+
+Enjoy. Still to come: Cell form 1 and cell jr.
+
+Funny quote of the day: "If a blonde throws a grenade at you, pull the pin and throw it back. If a blonde throws a pin at you, run like hell, she's got the grenade in her mouth!"
+
+
+
+
+
+
+
+# ...there's rabbit..and piglet....and there's owl..... - 04/23/2001 by PyroFragger
+
+This has nothing to do with BFP at all (except for the little blurb at the bottom) but I took the time to learn MAX modeling over the past couple of days. All high poly stuff, but the princliple of making the initial mesh is still low poly, so I may transcend from Raydream to MAX, I just hope it improves my quality. Here's the thing i made, anyways. My first mesh completely modeled in MAX:)
+
+
+BFP:
+Im gonna go against what I said last time just this once so that you can look forward to the part 2 model selection on Thursday.
+
+Funny Quote of the Day: "Never call Victoria's Secret and ask if something comes in childrens size"
+
+
+
+
+
+
+
+# ...and Tigger, and little Roo..... - 04/14/2001 by PyroFragger
+
+*BIG* update:
+First, I just want to say that the gi versions of Krillin and Gohan will not be part of the main download on account of some problems I was having with MAX (totally corrupted the file). They will be released later on as an addon. Im incredibly tired of physiquing and animating so today I took a break from all of that. Here is the result:
+
+
+
+Hope that tides you over for a while. I also want to say that I am no longer giving out public release dates because things tend to happen that are not in my favor, and I always end up with a lot of disgruntled people on my case.
+
+You can expect more models sometime soon, but like i said, im not going to say when:)
+
+As for why I'm working on models that fit in phase 3, it is because most of our phase 2 models are done and I was just pretty bored and tired of animating. Now I have them done, and don't have to worry about them when the time comes.
+
+Check out all the losers wearing dbz costumes (sorry if you're one of them): https://web.archive.org/web/20011031125532/http://www.thejasper.com/DBZ/CosDBZ.html
+
+Peace
+
+-P
+
+
+
+
+
+
+
+
+# ...A donkey named Eyore is his friend... - 04/12/2001 by PyroFragger
+
+Good things and bad things.
+Good: I found an easy way around the deformation problems the two models have been having so I can finally get them working right. As of now, they deform like hell, but that will all be fixed. If all goes well, I will have in-game shots of them tomorrow.
+
+Bad: I have put my website on hiatus so those bitching about it will have to chill. Things come up, I'll finish it when I have nothing at all to work on. In the mean time, you can read other tutorials, but mine's gonna take longer than I thought because I have to give the same steps for two different modeling packages, so it takes twice as long. Check back Saturday for a status report, I *may* be finished the tut by then, but don't hold me to that (and you wonder why we don't give release dates when we don't know if we can keep them:) I have other stuff to show off too on Saturday. Peace.
+
+-P
+
+
+
+
+
+
+
+# ...For most of his childhood days... - 04/05/2001 by PyroFragger
+
+Check for the skins in Ansel's journal in the next few minutes. I gotta roll out, so I dont have time to upload a shot. The torso is going to be seperated from the legs because i dont have tags on them yet. Tomorrow I can work the kinks out and maybe have an ingame screen for each of them.
+-P
+
+
+
+
+
+
+
+# ..There lies an enchanted neighborhood - 04/03/2001 by PyroFragger
+
+I will be finished with everything relating to phase 1 by tomorrow, so my site will be finished soon. I had hoped to finish it much sooner but such is life. Check for screenshots either tomorrow or thursday of them skinned.
+
+
+
+
+
+
+# ...where Christopher Robin plays... - 03/27/2001 by PyroFragger
+
+Those expecting the tutorial will have to wait. BFP work comes first, tut comes second. If you didnt already know we are finishing things and I have to of course get my models done really soon. They are already attached to skeletons and are animated, in the process of getting skinned, so they will be done soon. I may have the tut up by this weekend. Ive got more work to show later, not for phase 1.
+-P
+
+
+
+
+
+
+
+# Deep in the hundred acre woods..... - 03/16/2001 by PyroFragger
+
+Applied BFP standard animations and attack animations to the new krillin and gohan, ready for skinning soon. Also, I am designing a website for myself that will include shots of m bfp work and my first ever modeling tutorial that is nearing completion.
+-P
+
+
+
+
+
+
+
+# New Krillin and Gohan - 02/24/2001 by PyroFragger
+
+Two reasons for this:
+1) tired of everyone telling me to change their outfits to Gi's (Your post clenched it Mr. Krillin ;)
+
+2) wasnt happy with the level of quality of the models compared to my newer models
+
+So......here they are, unskinned:
+
+Krillin: 790 polygons
+Gohan: 892 polygons
+
+
+
+FEEDBACK
+
+
+
+
+
+
+
+# Random Updates - 02/09/2001 by PyroFragger
+
+Been working on Cell perfect a tad bit more to get it ready for Roney's skin job. Cell form 1 is next. I also may be making a RoQ movie file for the ending of BFTP. Lastly, I've been working on a 32 person tourney/capture the dragonballs map. Until phase 2 is released, itll just be a big tourney map. Screenshots of that as well soon. Thats about it, later.
+
+
+
+
+
+
+# One more update from me today - 02/09/2001 by PyroFragger
+
+As I said, I've been working on cell. This is the result, a more stylized, sleek, and muscular Cell:
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://pyrofragger.homestead.com/files/Final_Cell.jpg)
+
+Feedback.
+
+
+
+
+
+
+
+# First Update - 12/17/2000 by PyroFragger
+
+My first dev journal on the new site. As of now, im tweaking Gohan and Krillin while the others finish up. Work continues on Cell as well:
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://pyrofragger.homestead.com/files/p_cell.jpg)
+
+Anyways, i dont have much to do right now for phase 1, so I will probably be starting some giant tourney maps due to the 1.27 patch.
+
+My first dev journal on the new site. As of now, im tweaking Gohan and Krillin while the others finish up. Work continues on Cell as well:
+
+(old lost image: https://web.archive.org/web/20011218203246im_/http://pyrofragger.homestead.com/files/p_cell.jpg)
+
Anyways, i dont have much to do right now for phase 1, so I will probably be starting some giant tourney maps due to the 1.27 patch.
\ No newline at end of file
diff --git a/docs/old_dev_journals/remisser_dev_journal.md b/docs/old_dev_journals/remisser_dev_journal.md
new file mode 100644
index 0000000..cc975c1
--- /dev/null
+++ b/docs/old_dev_journals/remisser_dev_journal.md
@@ -0,0 +1,502 @@
+# patience - 01/18/2002 by Remisser
+
+a lot of people are sending emails and other such things frantically asking about the release. Please have patience. It's coming. Chris never said the time was precise. Patience is a virtue. Don't go downloading the supposed leaks. There's a bug with the leaks, and the latest version was uploaded _last night_, by the amazing YRGOL. I recommend you start sending him complimentary notes of thanks for dealing with the tedious coding. :)
+pyrofragger on the other hand is a beligerent drunkard. :)
+
+
+
+
+# to tide you over - 01/18/2002 by Remisser
+
+To keep you busy while you wait the next few hours out:
+[www.fark.com](https://web.archive.org/web/20020210152114/http://www.fark.com/) (fun news)
+[www.seanbaby.com](https://web.archive.org/web/20020123041221/http://seanbaby.com/) (funny stuff)
+[www.incisionstudios.com](https://web.archive.org/web/20011130114126/http://www.incisionstudios.com/) (good music - some from the next release of BFP [after this one coming out])
+
+
+
+
+# BFP CHAT - 01/18/2002 by Remisser
+
+So the game will be released today around 9PM EST (I say EST because so many people keep asking me because they can't convert pacific time)
+Exciting? Yes.
+
+So you may want to chat about it. Go to www.mirc.com and download a chat client called MIRC. Then come on into irc.ENTERTHEGAME.com in the channel #FunTimes * and chat with us. (also, #HHMod is a fun channel. :))
+
+
+
+*not actual channel.
+
+
+
+
+# d b z - 01/15/2002 by Remisser
+
+
+DBZ SUCKS!!!
+I'm glad this mod isn't tied to DBZ anymore. It'll be interesting when the side packs come out with the DBZ characters in it, but that cartoon is so tired now.
+
+
+----
+
+My music will unfortunately not be in the release this month due to miscommunication and time restraints. But hopefully with the next RC. It'll give you another reason to come back for more. :)
+
+
+
+
+# merry - 12/25/2001 by Remisser
+
+Merry Christmas. I've been away for a bit from everything because of major problems, but I hope to fulfill all the commitments I had. This Sunday I will make all the music I have done for Bid For Power downloadable. Along with that I will have a journal entry that goes in depths explaining how I create music, with pointers, using example from the music that I've made for Bid For Power. People can always learn how to model, code, map and everything else quite easily, but it's always hard to get pointers on music without getting the runaround. I can't promise the music pointers by sunday, but I'll try.
+I also want to thank everyone for their dedication to the game. Do you want to know HOW dedicated some of you people are? [Click here to read a fanfic for Bid For Power/Ranma/Tenchi/Slayers/Project A-Ko/Sailor Moon/Dragon Ball Z](https://web.archive.org/web/20020811050438/http://www.cse.unl.edu/~jwessels/fanfics.html) - It's pretty cool, and is still under development. I found it while searching for sites about BFP. It uses the new characters (like Gothax and such)
+
+Also for your enjoyment:
+[A seasons greeting from Pancake Bunny.](https://web.archive.org/web/20011203120347/http://www.selfabort.com/)
+
+[My crappy life, as it unfolds.](https://web.archive.org/web/20011116022908/http://www.livejournal.com/users/missing/)
+
+
+
+
+# update shmupdate - 11/29/2001 by Remisser
+
+So far I've gotten some good leads from coders (I still have more to email for a followup) and all is going well.
+Sometime this week (when I find that darn password) I will try to fix anything wrong with the PHP of the site. If any of you notice anything wrong with the php, email me so I can try to fix it.
+
+Lastly, I've been getting forwarded emails. One such email (which was sent to me by a few different people) was about how AOL and Microsoft were teaming up to beta test some new email software and if you forwarded it to one person you got $202 dollars, and more for each other person you sent it to.
+
+Now, I hate having to be the partypooper, but, the email is fake. First of all there is no way in the entire universe that they would be able to track all the mail sent around the internet from the numerous millions of people in the populace. Secondly, if they were even to ADMIT to being able to doing that, they would get sued for invasion of privacy. Thirdly, if they were to pay off every shmuck and loser who actually BELIEVED that it was possible, it would amount in the high TRILLIONS of dollars. So, yeah, right... AOL/Microsoft have this high spy secret software that can track any mail I send through an Apple IIe computer. Did I also mention they put a microchip in my brain and visit me at night through my aluminum foil thinking cap? It's true. I have to listen to Barney and Friends Super Duper CD just to stop their transmissions from coming in at night.
+Just because the devil forged an unholy alliance between Microsoft and AOL doesn't mean they have this ludicrous tracking power. You ever read those emails about how 'if you forward this, 3 cents will be donated to the cancer society of america' type emails? Guess what. They can't track it. Ever get 'sign this petition and forward it' emails? Guess what, those petitions, even IF they could track it and ORGANIZE it, it would be rejected because a computer could, just the same, scan a phonebook and come up with a random list of names for the petition.
+
+Chainmails are dumb. Don't send me any. I will not scroll to the bottom and make a wish. It won't come true unless I work for it. I will not be cursed if I don't forward an email. I just broke 15 mirrors, had 20 black cats cross my path, and I walk under ladders on a regular basis. If I'm going to die this gruesome death, I'll do it on my own terms. Think you've got this really funny email? Keep it to yourself, if I want humor I'll visit seanbaby.com, or fatchicksinpartyhats.com - I don't need your 'a priest and a rabbi' jokes that make your Moms and Dads laugh.
+
+I hate forwarded emails.
+
+-Trake Adams ( www.incisionstudios.com - I've got some BFP music on the radio station there :))
+
+PS - Forward this journal entry. ;)
+
+
+
+
+# code - 11/27/2001 by Remisser
+
+Anyone who can code well for Quake 3, send along a message to me. I need as much input as I can about quake 3 music related things. I'm trying to make a .pk file and... well, without the specifics, just send me an email if you're a good quake 3 coder.
+remisser@selfabort.com
+remisser@postmark.net
+
+
+
+
+# darn dirty journal - 11/07/2001 by Remisser
+
+
+If you are good with FLASH sites and/or animations, read the end of this.
+----
+
+
+I had this wonderfully long journal entry I made. Opera made it disappear. Oh well. The basics of what I said was 'Problem solved with devenum.dll - I actually had to boot up in safe mode, then shell to DOS and delete the devenum.dll and replace it with the devenum.dll file I downloaded weeks ago. Thanks for all your responses, but none of you came up with that suggestion. :) You see, I had to shell to DOS because if I didn't, the /windows/system/ registry automatically replaces the devenum.dll file with an exact replica of the file, no matter if you DELETE IT, RENAME IT, OR EVEN TRY TO EDIT THE RAW CODE. Booting up in safe mode allows it so that 99% of your DLL's never get used, so therefore the registry has no reason to automatically replace them. Thank you for being patient with me, to-nite I'll be installing Windows 2000 Professional to get rid of some of my woes. I also plan on finishing up the projects I've been working on (BFP, GundamUniverse, Heroes in a Halfshell, et cetera.) Music for Bid For Power is done, on my part, and I'm pretty sure GPoodle is almost done with his music. As I stated with a problem early, the music will probably just be an addon pack unless I can find some way of making the music files smaller. I got a lot of people suggested converting to .mp3 format. Now, really... I'm a musician. If I knew a way of making a .pk file for quake 3 arena that allowed the playing of MP3's, I would use it, any person knows mp3 files are much smaller than wav files. And .ogg files as well. But I believe you can only .pk wave files. Correct me if I'm wrong. Well that's about it. Thanks for your patience and keep up the good work in helping us out.'
+^^^ That's basically what I said...
+
+Now as I began to mention at the top, I need a Flash person. I run www.selfabort.com - a site that currently will only be in regards to my music. I could use someone's help in designing a flash website (something dark and ominous) and in return I can either make some small bits of music for any projects you may have, or the sound effects, or I can give you a pag redirect. like www.selfabort.com/yournamehere/ -- Or you could just help me out of the kindness of your heart. If you have a friend that does flash, get HIM/HER to email me. :) They may not even be interested and I'd hate to go asking out of the blue. If you can include some urls or samples to your work,it'd be appreciated. I'm desperate. :)
+
+Thanks again,
+Remisser
+
+
+
+
+# HELLLLLLLLLP!!! - 10/30/2001 by Remisser
+
+I need help!
+No matter what audio program I try to use I get the following error everytime:
+
+[Program name I'm using] has caused an error in
+DEVENUM.DLL
+[Program name] will now close.
+
+Email me at remisser@postmark.net
+
+
+
+
+# [music] - 10/26/2001 by Remisser
+
+I compiled the music and it seems it takes up a lot of space... if anyone has any clue as to how to make these files smaller, tell me... .wav is large...
+
+
+
+
+# [video] - 10/26/2001 by Remisser
+
+I'm going to see of a possibility of whether or not we can get a music video made with the new characters.
+Seeing as how things are moving along at a rapid pace though, the game would probably be done by the time we'd finish a video.
+
+
+
+
+# [last entry] - 09/05/2001 by Remisser
+
+To clarify on my last joural entry:
+1. I like GPoodles new song.
+2. The sound quality of the mp3 download below this journal entry is a bit compromised....
+
+Now for some news. For those concerned with the size of our ingame music will add to the actual BFP download size, we'll probably release the music as a seperate download. I'd rather it be combined, but for the sake of those with 2400 baud modems, I think it's for the better.
+
+remisser@postmark.net
+
+
+
+
+# music / voices - 09/04/2001 by Remisser
+
+I gave Gpoodle a new music program, I hope he's using it. Apparently, he wants his music in the game too... while this is ultimately up to Yrgol and Chris, I might as well try and help him out and get better, right? Hey GP, if you're reading this, I have some plugins for fruityloops for yah! :)
+Moving along, I should have some music uploaded either tonight or tomorrow... the new characters inspired me.
+
+If you couldn't read Number 17's new journal, it basically said all his sound fx will be used, except for the DBZ voices (like the angry Gohan scream)... I'm going to see about actually implimenting some anime type screams and yells for the game, with my vast array of microphones and vocal effects modules.
+
+That's about it. Go to www.hhmod.com for my ninja turtles TC, if you're bored. :) We still need mappaers and such.
+
+REMISSER@SELFABORT.COM
+
+
+
+
+# DOWNLOAD! W00t! - 09/04/2001 by Remisser
+
+remisser@postmark.net
+A download should be available (courtesy of Bandit- from [blink.net](https://web.archive.org/web/20020119214720/http://www.blinkk.net/) who graciously let me upload it) of a song in progress... the mp3 encoding is screwed up, so you'll hear little glorps and scratches... email me at remisser@postmark.net - maybe you can tell me some japanese phrases i should use to make a demo of new character voices for the team to hear. :) ANYHOW, [CLICK HERE TO DOWNLOAD](https://web.archive.org/web/20020210152114/http://www.blinkk.net/~remisser/sweetbfp.mp3) - I incorporated some of the stuff you people suggested (faster beats, more of a punch) - I collaborated on this song with a friend and I've added much more that will be uploaded later.
+
+I still need MAPPERS for my mod. :) [www.hhmod.com](https://web.archive.org/web/20020210152114/http://www.hhmod.com/) :)
+
+
+
+
+# [c0ntr0v3rZy] - 08/17/2001 by Remisser
+
+My modem didn't fare well last night. So, I'm updating from work. No music will be available until I get my shiny new cable modem. This should be soon.
+Moving on,
+Some people [who we will call 'minorities'] don't like my updates. They think they're useless. Other people [we'll call 'the majority'] like the fact that someone posts often. I do only what the MAJORITY cares for, not the MINORITIES. If you dislike my updates, ignore my journal. It's going to be the same drivel, basically.
+If you don't like the humor I display, or if you think I'm on some high-horse, then don't read this journal. Only strange people seem to get my humor, and strange is good. I'm not trying to appeal to a broad audience. I'm just trying to give what little info I have and give a smile.
+
+As for my public beratement of 'MikeGohan', it's dropped the negative mail I get drastically. It's my perrogative. Someone sent a link to a timeline and said I follow it, I don't. I've always been like this. And I am a rockstar, so excuse my rockstar attitude. I like DBZ, I hardly watch it (time constraints... and lots of La Blue Girl tapes I haven't seen yet) - but I know that this mod will be just as good without the trademarked characters.
+
+People who come up with timelines stating how standard other people are and how they follow the timeline they made are following another timeline:
+
+Join an interesting and popular community >> Make a website to add to that community >> Draw visitors by making fun of that very community you secretly admire >> Always be opposed to how things are run for the sake of having a differing opinion and getting the visitors who are sick of the site you bash
+
+I don't follow timelines.
+
+Sorry if this update seems ambiguous.
+
+I'll try to get the music up ASAP.
+
+
+
+
+# Song - 08/16/2001 by Remisser
+
+Song download available tonight...
+crappy modem pending. :)
+
+
+
+
+
+Try to cut down on the IMing of me. :) #HHMod on irc.enterthegame.com is a better alternative than email and AIM, since I respond there.
+
+
+
+
+# w00t w00t - 08/15/2001 by Remisser
+
+Well, I have a deadline of 'by this Friday' to have the music for BFP done.
+So...
+
+Don't know what that means...
+
+The music video I promised before could not be completed for the obvious trademark reasons... BUT... look forward to one soon with the new models (hopefully.)
+
+Here's a conversation I had online with that HTML hax0r:
+[http://www.selfabort.com/idiocy.htm](https://web.archive.org/web/20011204130005/http://www.selfabort.com/idiocy.htm)
+
+And, lastly, thank you for the unwarrented 'mod app' submissions... I do need help for the new one, but wasn't expecting people to pick that up from my last post. :)
+
+
+
+
+# Mods / Posting / Updates - oh my - 08/14/2001 by Remisser
+
+Since people seem to like it when an update happens, I offer you this update:
+More common questions I get asked -
+Question: The links to your songs don't work. Why?
+Answer: Well, bfpta's site has moved, among other things... [http://www.bfpta.net/remsongs/](https://web.archive.org/web/20020416021046/http://www.bfpta.net/remsongs/) Go there, click on a song.
+
+Question: What other things are you working on?
+Answer: Numerous mods... Mainly sound effects for most of the side mods... Most of the mods are so secretive, I can't even tell you what they're about! The mod I'm running is taking up a lot of my time as I'm still looking for coders, mappers, skinners, et cetera... making it impossible for me to work on music at the moment. I am almost finished with the BFP music anyhow... Look for more downloads when my cable modem comes.
+
+Question: How long does it take you to make a song?
+Answer: It varies. Shortest time was one hour. Longest was 2 months.
+Question: I listened to your songs... they sound staticy and horrible in winamp... why's that?
+Answer: Winamp isn't that great... depending on the version you have... try using a different MP3 media... and don't listen to it on small $5 speakers made of tinfoil. The sound quality will be compromised... plug your headphones into your line in jack. That may help.
+
+Question: Who invented the cotton gin?
+Answer: African American slave Eli Whitney invented the cotton gin.
+
+There's five questions answered. Got a comment on any of them? Email me. remisser@postmark.net -- Can't promise I'll reply, but I will at least read it.
+
+remi ([also remisser@selfabort.com](mailto:remisser@selfabort.com))
+
+=====EDIT!
+It appears many people were quick to point out the fallacy of my calling Eli Whitney an African American slave. While this was a joke meant to disillusion some people, I would not have edited this entry had user "Pat" not sent me this alluring photo of Mr. Whitney:
+
+
+Mr. Whitney is a SWM from the south who enjoys long leisurely walks along the beach, candle lit dinners, and water sports. Mr. Whitney is dynamite with the ladies and wants you to know how sexy you really are in your corset.
+Mr. Whitney is also in a band called "The Filibusters" and their new album "Rabblerousing" should be released posthaste... once vinyl records are invented. Mr. Whitney is quoted as saying his favorite pickup line is "Why dost thou not place your gluteous maximus upon my lap, so that we shall talk about the first thing that pops up."
+
+
+
+
+# [The answers] - 08/12/2001 by Remisser
+
+Questions I've been getting recently:
+Question: When will Bid For Power be released?
+Answer: This is a direct quote from Chris off the main page "Bid For Power is close to being released (about a months time)"
+
+Question: What will the characters look like?
+Answer: I don't know. I do the music. They're going to release pictures as soon as possible... that's the whole FUN of going to a site... seeing new pictures... Being there when it first comes out... Will they look like DBZ characters? Well, a lot of anime tends to look similar... big eyes, small mouth, funky hair that could impale a normal human being. So, stay tuned.
+
+Question: What about the moves and the maps?
+Answer: Beats me. Though I think there should be a character that throws squirrels. I don't think Chris would like that... maybe we could change all the characters to Tyler Durden, Narrator, Bob, and Marla from Fight Club. Just turn it into boxing... hey, remember that mod for Doom II where it was called boxing, and they wanted it to be only boxing, but you got to start with the basic gun? Well, maybe that was just me and my friends...
+
+Anyways...
+
+I don't know about the moves and maps. I don't ask because it's more fun to wait... but I haven't gotten any requests for new sounds for new moves... so... hmmmm...
+
+Question: What do you use to make music?
+Answer: Lots of basic instruments (guitar, bass), and lots of computer programs. FruityLoops 3.0, Cool Edit Pro and 2000. Cubase when I'm frisky.
+Some free programs you might want to try: www.brambos.com Tuareg, excellent program for drumloops and random cuts. thrax.box.sk Thrax... an underground community, lists a lot of pay and free programs. Go there.
+
+Question: I love you remisser! Can I have your children?
+Answer: No. Chances are you're a guy. And you're only 14. That's sick. Stop following me.
+
+
+
+
+# cornflower blue ties - 08/10/2001 by Remisser
+
+Just so you know, this is an email I got, and my public response:
+QUOTE:
+..what..you think ill fucking waste my time downloading bfp w/o the dbz charecters??fuck no..if i hear theres a person making a mod for the bfp mod..then maybe..im a big fan of dbz..dont tell us ..hype us on about something you said you were unsure of..AND 1 MORE THING...TAKE DOWN ANY AND ALL DBZ CHARECTER PICS ON YOUR SITE..ITS FALSE ADVERTISING AND WE CAN TAKE MONEY FROM YOU FOR THAT..hey look..a bfp site..ohh cool ..its a mod for q3..oh waite..no..its a fucked up..just maps and sound dbz game..ok..why the pics of the charecters..if there are non?..hmm.. "calls lawyer"..dont fuck with a dbz maniac..just get us the charecters..or have some one secertetly make them..then release a link to the download..not the offiacial download..well..you know..if im disapionted..i have your e-mail addy..and whats that..its a site e-mail addy??..then it will be nice to hack into that html..knowwhatimsayin
+
+^^^QUOTE ENDS...
+
+My Response:
+Really. REALLY now? I get these types of emails on a daily basis. On an hourly basis, even. What are you trying to do? Instill the fear of God into me? You know what I'm afraid of? That you'll breed. That people like you actually end up having children. But let's take a heartwarming look at your email.
+
+"it will be nice to hack into that html" - that is the most dumb thing I've heard all day. Hack into my html? What are you going to do, add a few A HREF and IMG SRC tags? How lame can you get? Maybe if you said 'hack into your servers', I might get scared, but I'm not. Y'see, you only know my postmark.net email address... I doubt you'd be able to 'hack' their 'html'. I have another email address, too. remisser@selfabort.com - but guess what, directnic.com owns that, and if you can 'hack' their 'html', you get a cookie! But, judging by your email address "Mikegohan07@aol.com", the only 'hacking' you know about are scripts you download.
+
+You don't even make them.
+
+That's what makes me sick. If you want to be known as a hacker, learn how to code, and say "here's a custom hack I made called Brute Force." If you want to put on a tinfoil hat and complain about the aliens, that doesn't make you a hacker. Sticking feathers up your butt does not make you a duck.
+
+Not only that you go on to try to force me, the one who makes the music, to take down 'any dbz likenesses', or, as you so elloquently put it: "TAKE DOWN ANY AND ALL DBZ CHARECTER PICS ON YOUR SITE..ITS FALSE ADVERTISING AND WE CAN TAKE MONEY FROM YOU FOR THAT.." -- AOL has a spell check. Have you seen this amazing utility? Maybe you can use it to better 'hack' into the 'html' of your email messages. But, as I was saying... False advertising? Take money from us? Tell me, how are you entitled to money when we are a free entity? What goods and services have you purchased from us? Nothing. You have about as much chance of getting money from me as the bum that holds open the doors of the 7-11 and asks for 'spare change on your way out'... and he probably has better breath than you! As for removing dbz likenesses?
+
+(old lost image: https://web.archive.org/web/20020210152114im_/http://venus.spaceports.com/~kakarot/images/sexykrillin.jpg)
+
+(old lost image: https://web.archive.org/web/20020210152114im_/http://venus.spaceports.com/~kakarot/images/sexychichi.gif)
+
+
+How's that? We won't use DBZ characters... we'll use real people that look slightly like them. How's that? Huh?! HOW'S THAT?
+
+So, go ahead and call your lawyer on your bright red Tonka Toys Playhouse Happy Fun Fone that your mommie gave you, because you're wasting my time.
+
+[think before you speak]
+
+.remisser (missingfear on aim)
+Special thanks to Spaceports (http://venus.spaceports.com/~kakarot/special.htm) for my unlicensed use of their unlicensed use of licensed Dragonball Z characters. Hope you don't ge sued!
+
+
+
+
+# oh, one more thing... - 08/09/2001 by Remisser
+
+an addition to what I said below, I'll try to get a new song for you to listen to within a few days... I'll be changing the name of the song "Destruction Of Namek" to "Obliteration Of Anime World unrelated to DBZ"
+
+
+
+
+# What this all means - 08/09/2001 by Remisser
+
+This is the third day in a row I've written something, so, I'll try to make it somewhat daily... which will eventually fall through since I'll never have something to say every day...
+So. A new revelation you are all in a quandy/uproar about.
+
+Bid For Power without the DBZ related/copywritten/prostituted characters.
+
+Well, it seemed bound to happen for a while now, and, while most of you are preaching doom/gloom, it's not as bad as you think.
+
+First off you've been given a release date of sorts - about a month from now. Maybe it'll come sooner. Who knows.
+
+Some people are as disappointed as I was when I found out they were lipsyncing, and look at the similarities: there are a lot of tongues wagging in BFP land, but not a lot of sound coming out. You're all getting worried over nothing. The members will be removing the dbz likenesses, but keeping the anime feel. I know what you're thinking... you're thinking "What if we decide to come out with an addon pack for BFP that had DBZ characters?" -- Hey, what you do when we release the game is in your hands... We're going to be legally clean... This just means I won't be starting that La Blue Girl mod after all.
+
+So, in closing, calm down, lighten up... it's not the end of the world.
+
+remisser@postmark.net
+
+
+
+
+# Quick Note - 08/08/2001 by Remisser
+
+I'll make this as short and sweet as possible.
+In response to some comments made about my last update:
+First off, most of the leaks were .exe files. A sort of setup. .exe's aren't pure files. I don't care if you have a virus scanner endorsed by Billy Bob Thorton, or TV's "ALF", some virus' or trojans can get past your rinky-dink Norton.
+Secondly, .pk files are as well UNPURE. A file must be pure to not be infected. DOC files can be infected with trojans just via macros alone. Don't dispute this with me, I really don't care what you think. No amount of self assurance will allow you to hide away from something that can and may destroy your computer.
+Third, anyone know a good graphic artist? I can't come up with a logo for my site for crap. Blech.
+
+Thanks for all responses that were sent to remisser@postmark.net -- even if they were negative.
+
+
+
+
+# Rebels, all of you... - 08/07/2001 by Remisser
+
+I'm guilty. I admit it. I've dealt warez and, quite frankly, I'm ashamed and apalled at myself. But those days are over now. I pay for my software. I am now an upstanding citizen of the United States of America.
+Why am I saying all of this? The point, you ask?
+The point is that once the Cease and Desist order (which, by the way, if it was a legal cease and desist order it would have to be mailed, not emailed, and drafted by a lawyer...) was issued to us, everyone scrambled, people leaked, tongues wagged, and 12 year olds across the globe decided to swamp Infogrames message boards with some ugly feeling of justice pumping through their prebubescent veins. Now, I said I USED to trade warez, I learned the err of my ways. Now, if Funimation claims it's illegal for US to distribute Bid For Power, what makes it right for those who did leak it, to do so? Rule of thumb says never download something not from the official site. If you have BFP on your computers, I say, delete it. Most of them are bound with Back Orafice, some of them have bastardized versions of Sub7 (still functional with the new settings) bound with the exe, and some have trojans and virus' with it. You may have, on your hard drive, the best DBZ game to date, but you may also have something that would allow your Gym Teacher to log remotely into your computer and access ANYTHING on your hard drive. Yes, even those hentai porn pics of Goku and Bulma doing the nasty. So delete it. Use your better judgement. Wait for Chris to peacefully resolve the matter. Have faith. Even if you don't think there's a virus on your computer and you somehow got BFP, delete it. Say to yourself "I don't have the final copy with Remisser's music." -- Say it deep in your throat and make it shout out at the top of your lungs "I'm mad as hell and I'm not going to download anymore!"
+For those of you with patience, who didn't download a leak, congratulations. Greg Brady loves you.
+
+Now a note to funimation. Can't you buy see the potential for funimation sales of funimation merchandise products? I mean, with subliminal advertising alone, you could make an extra fortune. You'll have stacks of unmarked bills you an sit on. Think about it. Think of how well (not at all) the other dbz video games did. Now start to think "Gee... 6 million plus hits to this bfp site... that's about 5,999,998 more than an OFFICAL BFP video game site would get. Hmmm... is there any way we can capitalize on millions of page views?"
+
+And another thing I want you kind folks at Infogrames and Funi to know. Think this game will damage profits? Hardly. Anyone who wants BFP has to have Quake 3 Arena knowledge. They also have to purches Q3A before they can play the game. Also they have to have a computer capable of playing it. Let me tell you, anyone who was ever going to get q3a has already purchased it. We're not going to increase their revenue. And, not everyone wants to download a file as large as BFP. So, here are a few ideas and thoughts... 1) Your profits won't decrease, in fact, they might just get better because the forums here are a breeding ground for talk about the new video game you guys are making and how excited everyone is... it's adding to your sales. 2) I hate to say this, but, the game IS far superior to all the other dbz games made, and just gives a benchmark for your team of 'video game designers' to surpass. 3) Would you really sue us? Think of the costs (court, lawyers, otherwise) -- That'll cost about as much as you'll make from a below average DBZ game. At best you'll break even. And most of us are poor and on welfare. So if you sue us, you won't get money, and we'll get book deals and movie-of-the-week deals, and anytime there's a television show about video games, we'll get paid to tell how you molested us with your lawyers.
+
+Really, all we want is a middleground. Somewhere to meet and discuss the pros and cons.
+
+We're on your side. The fans are too.
+
+If you're a fan, take the time out to write to Funimation. Tell them you'll preorder the game. Tell them how good they look in a suit. Kind words go a long way. Stop downloading the virus filled warez bfp leak, stop flooding message boards with negativity, and send a good word or two.
+
+Oh... and I'm almost finished with the music for BFP. Downloads to come, weather pending.
+
+email: remisser@postmark.net
+
+
+
+
+# invisible monster - 06/26/2001 by Remisser
+
+Within the next few weeks a music video should be available. And, you know, when I promise something it comes true.
+The video will be about 2 minutes and 30 seconds in length. I have a song I've done for it and might make it available before the music video. If possible, I'll try to make the music video available with the next couple of days. This is unlikely, but is a possibility.
+
+Another thing, if you have a Bid For Power Fan or Clan site, I'm always curious and love to see new BFP related sites. For no particular reason... so email me at remisser@postmark.net with your site URL.
+
+Don't all these devjournal updates just get you giddy?
+
+
+
+# Your birth is a mistake you'll spend your whole life trying to correct. - 06/21/2001 by Remisser
+
+[Download the End Credits](https://web.archive.org/web/20020210152114/http://insite.i700.net/mp3/Remisser_-_End_Credits.mp3)
+Okay, so it's not that big of a download, but it's something to tide you over. Thanks again to bfpta for hosting this stuff. Thanks to people like Bid For Power Nation who are accepting applications for members who are essential to the fabric of the BFP reality, by updating faithfully, and, without thanks, mirroring files. It's people like axsdeny who made an Android 16 wallpaper that bring a tear to my eye, because it's all without thanks. But thank you, the fans.
+
+As far as everything else is concerned, I am throughly working on more and more level music. There will be about 5-7 songs (more than likely 7), so as to not add a crap load of megs to a future bfp download. So, don't complain. I hope to finish this all up soon, but keep voting (see prev. entry for the poll), and keep emailing. remisser@postmark.net
+
+
+
+
+# bitch tits - 06/21/2001 by Remisser
+
+
+
+The above is the new poll for the two songs (which you can download and are linked below) for you to vote. Much more organized.
+
+Later tonight I will try to have another song up for download, and I will put up more questions and answers. Any questions regarding the music aspect of Bid For Power, please email them to remisser@postmark.net
+
+
+
+
+# Nothing of me is original. I am the combined effort of everybody I've ever known. - 06/18/2001 by Remisser
+
+A few questions have arisen since my induction into the ranks of team member. Here are the most popular:
+
+_Who are you?_
+
+I'm the new Bid For Power music composer/whipping boy.
+
+_Really? Cool! Can you put an MP3 player in the game?_
+
+No. Good God, no. Go to winamp.com and get an mp3 player and use it in the background. I'm going through the hoops to bring you music, don't ask me how to mute it.
+
+_Put Slipknot music in the game! Put Rob Zombie music in the game! Please?_
+
+OK. All the music for the ingame part of BFP will be original as to not incur the wrath of record companies, and, as well, not to be a proverbial legal landmine. Plus, nobody likes Slipknot. (Oh great, now I'm going to get 80 slipknot fans giving crap)
+
+Okay, I'm not trying to be vicious here, but these are the questions I've been getting. I love all of the input I've been receiving, but I've gotten enough for now... but I still need your help. You want to hear music being done for BFP at the moment?
+
+[The First Version of Destruction Of Namek](https://web.archive.org/web/20020210152114/http://insite.i700.net/mp3/Remisser_-_Destruction_Of_Namek_V1.mp3)
+This is the slower more anthemic version of a song I'm working on...
+
+[The Second Version of Destruction Of Namek (clip)](https://web.archive.org/web/20020210152114/http://insite.i700.net/mp3/Remisser_-_Destruction_Of_Namek_(fast).mp3)
+This is basically a clip that culminates previous fan suggestions... one of which was 'speed it up'. So it's +50 BPM faster than version one.
+
+DOWNLOADS were made available by the folks over at [BFPTA](https://web.archive.org/web/20020210152114/http://www.bfpta.cjb.net/). Bfpta is a website that in the very near future will showcase tips, tricks, tactics, and techniques designed to assist any and all Bid for power players with one goal: Victory. It will house a numerous amount of tips developed by themselves, and sent in by others, From Novice, to Nightmare. The general public can also comment on each tip, to show others what they think, and how it would/did benefit them. -- One of the many BFP sites that are growing on the web... I might feature more in the future, but if I mention them, it doesn't mean BFP endorses them in any manner. :)
+
+The next time I update, I should have viewer response on which version they liked best, and what direction I'll be going in. As well as what equipment I use, et cetera. (The only comments I want are which version you like better, I don't really need input on how to make music :))
+
+Happy downloading.
+
+
+
+
+# when did the future change from being a promise, to a threat? - 06/15/2001 by Remisser
+
+This being my first post in my journal, I'd like you all to
+know who I am. I am the new Bid For Power composer. I will be
+delivering some nice crafted songs for the ingame atmosphere. I will
+give you all links to clips of some of the songs in the next few days.
+I know I can't please all of you, but I'll try to please some of you.
+
+Any suggestions such as songs you think would be good to cover for
+the ingame part of BFP, please either suggest them to me in an email,
+or address the ideas in the forums... thank you, and have a nice day.
diff --git a/docs/rodney_dev_journal.md b/docs/old_dev_journals/rodney_dev_journal.md
similarity index 97%
rename from docs/rodney_dev_journal.md
rename to docs/old_dev_journals/rodney_dev_journal.md
index c97bf18..e492608 100644
--- a/docs/rodney_dev_journal.md
+++ b/docs/old_dev_journals/rodney_dev_journal.md
@@ -1,99 +1,99 @@
-# Q3 models get ready to download - 11/13/2001 by Rodney
-
-my personal website is done, I haven't uploaded it yet, cuz Im trying to find someone to host it for a good deal, if you know of a good deals with unlimited bandwidth, or no charge for bandwidth that would be great. im shooting to get a host by this saturday, so expect the site up in about 2-3 weeks from now. The BFP models will not available yet, cuz of the new way the code is been implemented. So we have to wait til yrgol gives us the go =).
-My site is gonna be at www.RodneyOlmos.com so you can try checking everynow and then and youll see junk poping in and out =).
-
-
-
-
-
-
-
-# Let me rephrase this - 10/05/2001 by Rodney
-
-Im also looking at the possibilities of making all the characters I made capable of including multiple animations built on the models so if other people want to use them for their mods or anything else related they will also be able to add them to their mod or game using the characters I built, with my permission or requests to use them, and you may not alter them and claim them yours.
-
-
-
-
-
-
-# Sound Help - 10/01/2001 by Rodney
-
-Thanx to all the people that emailed me and offered their help on sound. Im going to be emailing a list of things that "need to be done", the peeps that emailed me, so we can get this ball rolling and get all the characters over with.
-We have enough people helping with sounds now=), so thats good. and I think that might be enough
-
-
-
-
-
-
-
-
-# Characters for Quake arena done - 09/26/2001 by Rodney
-
-Hey all, I update like once every 6 months =)
-things that are new are this.
-- I just finished paking all the characters for quake3 arena, this includes all the phase 2 characters, the extra dbz characters like yamcha, the chibis, ussj trunsk,etc, and the new bfp characters. In gonna be releasing them fairly soon or as soon as I get all the sounds for each one of the characters, I would really like a hand on sounds, If you would like to help on that please email me at nameck@networld.com the quake3 characters will get released on sets of 5 characters at the time.
-
-- New BFP characters are also getting paked for release, all the original DBZ phase 1 characters are pretty much done, and I will be releasing them a lil after BFP is released, it will be on paks of 6 characters at the time so you can add them to the new BFP.
-
-- The other dbz characters will also get released soon after the first pak. like tien yamcha the chibis ussj trunks, gohan and stuff. but when they get released they will be using some one else's attack. example, chibi goten will use goku's attack and such, yamcha will may be use krillins.
-
-Reasons for this is game balancing. so far there are 6 attack systems that yrgol coded and by adding new attacks for other characters might unbalance the game. hopefully after release we will get feedback from everybody on how the game plays so we can tweak it for balancing and may be adding more set of attacks for other characters, besides the dbz phase 2 needs to be tackled different since we are not gonna use dbz stuff any more.
-
-
-Im also looking at the possibilities of making all the characters I made capable of including multiple animations built on the models so if other people want to use them for their mods or anything else related they will also be able to add them to their mod or game using the characters I built with my permission or request to use them..
-
-for now thats it, Ill try to keep updating often til release.
-
-lates all
-
--Rodney
-www.RodneyOlmos.com
-
-
-
-
-
-
-
-# .. Finallly Updating =) - 04/03/2001 by Rodney
-
-Ok.. since phase 1 is officially done, and there isn't much else to do on it, Im gonna update my journal, and proceed to do so this whole rest of the week,Im gonna post all the work I've ever done for BFP here. That includes the BFP logo, 2d menu art, characters and new upcoming characters and so on.
-Here is a list of things of work that I've done and some images that should get posted during the week,
-
-- 16
-- Goku (3 dif versions of it)
-- New Vegeta
-- USSJ Trunks
-- SSJ Gohan
-- Piccolo
-- Tien
-- Freeza
-- Yamcha?
-- New Characters?
-- Chibi Boys, Trunks and Goten
-- Standard BFP Animations
-- Logo
-- 2d Menu art (buttons and such)
-- Anthony did the cartoon renders
-- Hud
-- Avi Renders, Roll Animations
-
-I think thats it....
-
-so I'll post all my work this week.
-
-c ya
-
-
-
-
-
-
-
- Test - 12/04/2000 by Rodney
-
-alright, my first dev update on this new webpage=)
+# Q3 models get ready to download - 11/13/2001 by Rodney
+
+my personal website is done, I haven't uploaded it yet, cuz Im trying to find someone to host it for a good deal, if you know of a good deals with unlimited bandwidth, or no charge for bandwidth that would be great. im shooting to get a host by this saturday, so expect the site up in about 2-3 weeks from now. The BFP models will not available yet, cuz of the new way the code is been implemented. So we have to wait til yrgol gives us the go =).
+My site is gonna be at www.RodneyOlmos.com so you can try checking everynow and then and youll see junk poping in and out =).
+
+
+
+
+
+
+
+# Let me rephrase this - 10/05/2001 by Rodney
+
+Im also looking at the possibilities of making all the characters I made capable of including multiple animations built on the models so if other people want to use them for their mods or anything else related they will also be able to add them to their mod or game using the characters I built, with my permission or requests to use them, and you may not alter them and claim them yours.
+
+
+
+
+
+
+# Sound Help - 10/01/2001 by Rodney
+
+Thanx to all the people that emailed me and offered their help on sound. Im going to be emailing a list of things that "need to be done", the peeps that emailed me, so we can get this ball rolling and get all the characters over with.
+We have enough people helping with sounds now=), so thats good. and I think that might be enough
+
+
+
+
+
+
+
+
+# Characters for Quake arena done - 09/26/2001 by Rodney
+
+Hey all, I update like once every 6 months =)
+things that are new are this.
+- I just finished paking all the characters for quake3 arena, this includes all the phase 2 characters, the extra dbz characters like yamcha, the chibis, ussj trunsk,etc, and the new bfp characters. In gonna be releasing them fairly soon or as soon as I get all the sounds for each one of the characters, I would really like a hand on sounds, If you would like to help on that please email me at nameck@networld.com the quake3 characters will get released on sets of 5 characters at the time.
+
+- New BFP characters are also getting paked for release, all the original DBZ phase 1 characters are pretty much done, and I will be releasing them a lil after BFP is released, it will be on paks of 6 characters at the time so you can add them to the new BFP.
+
+- The other dbz characters will also get released soon after the first pak. like tien yamcha the chibis ussj trunks, gohan and stuff. but when they get released they will be using some one else's attack. example, chibi goten will use goku's attack and such, yamcha will may be use krillins.
+
+Reasons for this is game balancing. so far there are 6 attack systems that yrgol coded and by adding new attacks for other characters might unbalance the game. hopefully after release we will get feedback from everybody on how the game plays so we can tweak it for balancing and may be adding more set of attacks for other characters, besides the dbz phase 2 needs to be tackled different since we are not gonna use dbz stuff any more.
+
+
+Im also looking at the possibilities of making all the characters I made capable of including multiple animations built on the models so if other people want to use them for their mods or anything else related they will also be able to add them to their mod or game using the characters I built with my permission or request to use them..
+
+for now thats it, Ill try to keep updating often til release.
+
+lates all
+
+-Rodney
+www.RodneyOlmos.com
+
+
+
+
+
+
+
+# .. Finallly Updating =) - 04/03/2001 by Rodney
+
+Ok.. since phase 1 is officially done, and there isn't much else to do on it, Im gonna update my journal, and proceed to do so this whole rest of the week,Im gonna post all the work I've ever done for BFP here. That includes the BFP logo, 2d menu art, characters and new upcoming characters and so on.
+Here is a list of things of work that I've done and some images that should get posted during the week,
+
+- 16
+- Goku (3 dif versions of it)
+- New Vegeta
+- USSJ Trunks
+- SSJ Gohan
+- Piccolo
+- Tien
+- Freeza
+- Yamcha?
+- New Characters?
+- Chibi Boys, Trunks and Goten
+- Standard BFP Animations
+- Logo
+- 2d Menu art (buttons and such)
+- Anthony did the cartoon renders
+- Hud
+- Avi Renders, Roll Animations
+
+I think thats it....
+
+so I'll post all my work this week.
+
+c ya
+
+
+
+
+
+
+
+# Test - 12/04/2000 by Rodney
+
+alright, my first dev update on this new webpage=)
Ok lets see, characters are practically ready for phase1, I got new characters done, but they will not be out til phase 2 comes out The nice thing is once going through the whole learning process of phase1, phase 2 is gonna go by fast, I got 3 new versions of Goku with 3 dif outfits I wont show anything til the characters are done. Oh yea.. I talked to Chris, Im gonna get some renders done of each character so you can use as wallpapers, and also some character roll avis. And Ill just do that every time i get a character completely done. thats it for now.
\ No newline at end of file
diff --git a/docs/yrgol_dev_journal.md b/docs/old_dev_journals/yrgol_dev_journal.md
similarity index 97%
rename from docs/yrgol_dev_journal.md
rename to docs/old_dev_journals/yrgol_dev_journal.md
index 9878dbb..d3fda93 100644
--- a/docs/yrgol_dev_journal.md
+++ b/docs/old_dev_journals/yrgol_dev_journal.md
@@ -1,919 +1,950 @@
-
-# BFP 1.1 Update Information - 02/04/2002 by Yrgol
-
-The grand list of my updates to the latest build of Bid For Power. Enjoy.
-
-BFP 1.1 Changes
-
-features:
-
-* added a bfp_server.cfg file, which lets server ops control:
-
-- flight cost, boost cost, ki regen, and ki charge regen.
-
-* aura color is now forced to red or blue in team games depending on team.
-
-* new console.
-
-* tweaked attack shaders for lighting values.
-
-* new fingerblast look and sound.
-
-* lighter versions of the maps gptourney1 and gpctf1.
-
-* added cone of fire support to the attack scripting.
-
-balance:
-
-* lowered boost cost significantly
-
-* upped ki regen rate so that flying at max pl costs nothing.
-
-* fixed the extra knockback on attacks that use that feature. tornado blast uses this.
-
-* rebalance of most attacks.
-
-bugs:
-
-* fixed the 1v1 server crash. 1v1 servers should be as stable as the other game types now.
-
-* fixed handling of plugin player models on clients that don't have the model.
-
-* fixed a "weapon number out of range" message followed by a crash when playing with bots.
-
-* fixed the appearance of explosion spawn attacks (homing special multi-beam bug).
-
-* made suicide take away a kill.
-
-* fixed losses being assigned to the wrong player in 1v1 games.
-
-* ctf servers show up in 1.0 as lms servers. fixed.
-
-* survival servers show up in 1.0 as single player servers. fixed.
-
-* gametype in 1.0 gets cut off in the servers list. fixed.
-
-* fixed missing friend shader for team games.
-
-* added dummy player sounds for tetsedah, ryuujin, and pyrate to get rid of some warning messages.
-
-* fixed the small own aura setting.
-
-* switched the missile dlights of the two razor disks.
-
-* fixed tornado blast catching attacks that have higher priority.
-
-* fixed ki use toggle.
-
-* fixed position of sparks in power struggles.
-
-
-
-
-# more stuff for the patch - 01/23/2002 by Yrgol
-
-* fixed the 1v1 server crash. 1v1 servers should be as stable as the other game types now.
-* lowered boost cost significantly
-* upped ki regen rate so that flying at max pl costs nothing.
-* fixed a "weapon number out of range" message followed by a crash when playing with bots.
-* fixed the extra knockback on attacks that use that feature. tornado blast uses this.
-* fixed losses being assigned to the wrong player in 1v1 games.
-
-
-
-# bugs so far - 01/20/2002 by Yrgol
-
-I'm waiting for a decent amount of bugs to show themselves before doing a patch.
-
-fixed:
-* added a bfp_server.cfg file, which lets server ops control: flight cost, boost cost, ki regen, and ki charge regen.
-* fixed the appearance of explosion spawn attacks (homing special).
-* aura color is now forced to red or blue in team games depending on team.
-* ctf servers show up in 1.0 as lms servers. fixed.
-* survival servers show up in 1.0 as single player servers. fixed.
-* gametype in 1.0 gets cut off in the servers list. fixed.
-* fixed missing friend shader for team games.
-* added dummy player sounds for tetsedah, ryuujin, and pyrate to get rid of some warning messages.
-
-known and not fixed yet:
-* there seems to be some stability issues with 1v1 servers. this mainly seems to happen on map changes.
-
-
-
-
-# patch - 01/19/2002 by Yrgol
-
-The next thing released by bfp will be a dbz plugin media pack or two. This will be an optional download that includes the old models in plugin form and some new maps.
-
-After that, the next releae of bfp will be a patch that will arrive in about two weeks to a month or so from now. This patch will have a decently small filesize, and it's for fixing whatever bugs show up. If you encounter a bug, please visit the bugs forum to post a message so I can fix it. Once most of the bugs are gone I'll start working on new attacks with some real variety now that my hands aren't tied by dbz.
-
-The homing special has a graphical glitch. Once it spawns the multiple homing missiles, those missiles appear to each player as his own 4th tier attack instead of appearing as the attack that it is supposed to. This has no effect on how the attack actually operates and does damage, but it can get confusing. This is already fixed for the next version.
-
-
-
-
-# Modding BFP - 12/13/2001 by Yrgol
-
-It is possible to modify bfp in a number of ways. Some of them are compatible with the base bfp, and others are destructive to the base bfp. The only type of bfp modding that I will support is the compatible type.
-Multiple groups have tried to make their own versions of various hacked copies of bfp, so in order to prevent players from having to choose between versions, I'm posting a little guide on how to make mods of bfp coexist with each other. This is not supposed to be a comprehensive guide on how to mod bfp, just some dos and don'ts if you decide to modify it.
-
-Model and Map Packs:
-If you only want to create some new models that use existing attack sets, or new maps to play on, create model and map pk3s and release them together so the players can install them in their base bfp directory. These models and maps will be selectable in the menu just like any other model or map. BFP allows for a significant amount of variation in plugin models.
-
-Game Balance:
-If you are unhappy with the balance of the game and run a server, just modify the script file bfp_weapons.cfg to suit your tastes. You can then start up your server and people using the base bfp will be able to play there. I will be adding more ability to do this in the future, but what already exists is pretty significant.
-
-New Attacks or Attack Sets:
-If you wish to add new attacks or attack sets to the game, you will have to create a full mod of bfp. This is because otherwise, players will accidentally connect to your bfp mod using the base bfp, and their game is likely to crash. More importantly, I don't want players to have to randomly try bfp servers until they can find one that is compatible with the version they are using.
-
-The server-finding code in the game will find any server running any mod that lives in a directory that starts with "bfp", and display the full name of that mod. The official bfp files will be in the "bfpq3" directory. Unnoficial mods of bfp can go in directories like "bfpmyversion" or "bfpcool". Players will be able to see "bfpcool" or "bfpq3" in the menu, so that they can find a server compatible with the version of bfp they are running.
-
-Rule #1:
-Never add or remove anything to any file named similarly to bfp0.pk3 in the base bfp directory. This will invalidate sv_pure, and it will prevent me from patching the game! You can use quake 3's pk3 indexing to override files in bfp0.pk3 without touching that file.
-
-Rule #2:
-Nobody is getting ahold of my source code for quite a long time, so don't bother asking. If you wish to add new code to bfp, you will have to code the entire game from scratch.
-
-Rule #3:
-Do not include any official bfp model, map, or image files with your mod. Free game or not, enforcable or not, doing so is a violation of copyrights and is also totally unecessary. Make use of quake3's base mod ability. Quake 3 allows you to create a mod that uses files from another mod's directory. If you do not want your players to have to download the official bfp, you will have to create all new maps and models to use for your game.
-
-
-
-
-# weapon limit - 12/07/2001 by Yrgol
-
-Quake3 has a limit of 16 different weapons. BFP used to have a limit of 31 total attacks, with 21 of those being used. Thanks to about 2000 lines of code changes, BFP now has a practically unlimited max number of attacks.
-I currently have slots for 99 different attacks that can be added without any code, but I could easily double or triple that if bfp ever comes close to filling up all 99 attacks.
-
-
-
-
-# mp3 support - 11/30/2001 by Yrgol
-
-I added Tim "Timbo" Angus' mp3 to wav converter into bfp. It can be found at http://tremulous.sourceforge.net/junk/cg_mp3decoder/
-
-This will allow us to include more music in the download, since each music file will have a smaller filesize.
-
-This will not allow you to play your own mp3's. It is not an mp3 player, it is an in-game utility to convert mp3's into a q3-playable format.
-
-
-
-
-# Client-side scripting - 11/29/2001 by Yrgol
-
-I've finished the client side weapon scripting, and I've posted a doc in the editing and modifications forum describing how to use the scripting in order to create plugin models for BFP.
-You can find the post here:
-http://www.bidforpower.com/forum/showthread.php?s=dc60acd92fe3f101aff5ad112f78d25d&postid;=508160#post508160
-
-
-
-
-# 1.30 and bots - 11/19/2001 by Yrgol
-
-Oddly enough, the 1.30 point release fixed bot flight, which the 1.17 point release broke. The bots have no problem flying up or down anymore. The bots switch weapons now as well, and I added a few other bot tweaks.
-Bfp bots are pretty good for deathmatch practice. They're working much better than I expected. I'm sending a big woot out to the 1.30 point release for q3.
-
-
-
-
-# - 11/17/2001 by Yrgol
-
-What we are currently working on is equivalent to beta 2 for most mods, or in bfp jargon phase 1. We had phase 1 completed for dbz quite a long time ago, and then the funimation/infogrames stuff happened. Most of the time since then has been used for converting bfp to not use dragonball z copyrights.
-
-The code is ready to go, the ui images have been changed to be legal, and we have several non-dbz maps finished. I currently have 3 of the 6 necessary default models. When I have all 6 default models, I will create a full build for the team to test. If nothing horrible goes wrong we'll probably release about a week after that.
-
-I've been working on scripting much of bfp. When this is complete, new characters and attacks will not require any additional code. This will allow team members and the community to create new unique characters for future releases. There will be a second release which will likely include more maps and models, some more in-depth gameplay modes, more attacks, and a completed attack scripting system. I will also likely be replacing the melee system with something different.
-
-I've added much more powerful custom plugin model support. The old dbz models will be able to perform identical to what they did before, provided they are converted to the new format. I have repackaged the old bfp models and included the necessary script files for them to work. I will be working on making the support for customizations much more significant, but that will be for future releases.
-
-I added server side weapon scripting. It allows a large amount of control over the game balance by the server operators, and makes my job of balancing much easier. It can not yet be used to create new attacks, but it will eventually. While doing this I fixed up a few serious gameplay complaints.
-
-Direct hit detection has been made more reliable. This makes the kienzan equivalent and the deathball easier to use.
-
-Explosion damage is no longer all or nothing. The damage decreases as distance from the explosion increases like in baseq3.
-
-Attack radius has been separated from explosion radius. The attack radius and explosion radius for each of the attacks have been tweaked.
-
-The knockback physics for attacks and explosions has been seriously tweaked. Attacks can now be used to rocket jump.
-
-Boost jumping has been toned down. It was simply too excessive before.
-
-Another total rebalance of all attacks happened.
-
-
-
-
-# scripting - 10/26/2001 by Yrgol
-
-There's no way I can get all of this done before release, but this is the direction I'm going in.
-
-I'm working on attack scripting for servers only. This will allow new attacks of any rough type that bfp already contains to be added to the game with little or no code. I have a script file set up and partially implemented that has like 30 variables for each of the attacks. This will not allow models to define new attacks, but it will allow team members to work on attacks without me, or mods of bfp. It also lets server ops who hate the game balance to tweak it significantly.
-
-I have plans to add a server script file to define what the attack sets are, and even how many different attack sets there are. This config file will make adding new attack sets for existing attacks a trivial task. The file will look something like this:
-
-```
-attackset 1
-attack1 15
-attack2 20
-attack3 21
-attack4 2
-attack5 30
-defaultmodel models/players/goku
-```
-
-Per-skin config files have been added that allow things like attack names and voices to be defined by the skin. I have plans to move a lot of stuff into these config files, including attack shaders, sounds, and models. The individual models will never be able to define how the attacks operate, only how they are displayed to the user.
-
-I will eventually create a separate server config file that defines all the ki costs for flight and things along those lines.
-
-I've been working on the server attack config scripting for about a week, and it's a huge job. I can not finish all of this in a reasonable time frame, but eventually bfp will be close to just being an engine with the game all defined by runtime scripts. Once that point is reached I'll no longer be the bottleneck for a lot of the future work, and I can concentrate on gametypes and random stuff.
-
-I do not expect much of this to be implemented enough to matter before we get to release though. I'd much rather release asap and then improve.
-
-
-
-
-# Conversion - 09/10/2001 by Yrgol
-
-With the exception of the names, the attacks are not changing. Most of the maps are not changing either.
-The new thrust of development for me is moving as many things as reasonable into the player models. Custom player models can be made for any of the sets of attacks, and I'm working on a way for skins to define the names of the attacks. I don't want to let each skin pick the attacks to use for balance, memory, and implementation reasons. The voices that get played when using certain attacks have been moved into the player model.
-
-The bfp leak is bfp's biggest competition right now. The release has to be better than the previous stuff. There are a few annoying bugs to work out, and some of the top tier attacks throw balance way off.
-
-My job has been sapping most of my time and motivation to program lately.
-
-
-
-
-# HoverQ3 - 08/17/2001 by Yrgol
-
-I released a little mod called Hover Q3. It includes some art from Pyrofragger, Kit Carson, and Sole. You can find it at http://www.bidforpower.com/yrgol/hoverq3
-
-It's a 3 meg download and is a lightweight but fun mod. Don't expect anything like bfp from this, but it's well worth checking out.
-
-
-
-
-# destructable objects - 07/08/2001 by Yrgol
-
-Breakable map entities are in, with multiple stages of breaking. I havn't done anything fancy with particles and destructable objects yet. None of the maps currently use this feature.
-
-
-
-# stuff - 07/06/2001 by Yrgol
-
-custom models are fully supported now. if you create a hulk hogan model and put it in /models/players/vegetahogan/, then the hulk hogan model will show up in the ui as a selectable skin for vegeta, brotha. I'm gonna hit you with a final flash in a steel cage.
-
-beams are bendy now. beams are not guided missiles, they are a beam of energy that gets longer quickly. when you turn left while firing, the beam bends to the right briefly before straightening out again.
-
-other than that, some random bugfixes in the animation playing and 1.29 fixes.
-
-
-
-
-# <-- that way - 06/21/2001 by Yrgol
-
-* fixed animation jitter caused by jump anims that shouldn't be played.
-* made it play the run anim faster during boost run.
-* fixed the bug that made the ki trail show up behind objects.
-* gave the accurate third person crosshair some smoothing to prevent it jumping around.
-* added a toggle to use a stable crosshair instead of the accurate one.
-* fixed a bug that made killing yourself give you a PL boost.
-* made renzoku cost a ki pctage to prevent link overflow
-* fixed a cheat that let people get vegeta's attacks with a piccolo model.
-* enabled custom player models.
-
-I have a real job, and right now it's very demanding. When I have time and energy for bfp, I'm trying to polish as much as I can.
-
-The custom player models are weakly supported. Just for example, if you wanted to make a gundam and put it in bfp with vegeta's attacks, you'd have to create a model and skin in models/players/vegetagundam. Once there, it acts as a vegeta skin, even though it's actually a skin and a model. If you were playing with someone who didn't have your gundam model, they'd see the default vegeta.
-
-I'll release a far more descriptive doc on how to make a custom bfp model later on when it matters. BFP uses a lot of animations.
-
-
-
-
-# random update - 05/29/2001 by Yrgol
-
-had a somewhat productive week. bfp comes in spurts.
-
-updates:
-* accurate 3rd person crosshair.
-* particle dust trail for boost running or flying close to the ground.
-* particle bubble effects that float to the surface and gather there.
-* particle splash + bubbles for entering water.
-* particle bubble trail for boost swimming:
-
-
-* particle bubble trail for boost flying close to water.
-* particle bubble effects for charging underwater:
-
-
-* fixed the waterdance bug.
-* lowered zanzoken cost.
-* added antigrav rock particles for charging:
-
-
-* the antigrav rocks fall to the ground when you stop charging.
-* gave rock particles collision detection and bouncing properties
-
-
-
-
-# survival mode - 04/08/2001 by Yrgol
-
-I added (past tense) survival mode to the game. This might morph into budokai mode when mappers and I both get some time. The name comes from the mode found in a lot of fighting games.
-
-Survival mode is basically 1v1 where if you die once, you go to the end of the line. The winner does not get healed in between matches, and people spawning in get power level based on the average like in FFA mode. Spectator scores are saved and visible. The winner is the first person to reach the frag limit.
-
-1v1 mode is meant for challenge matches. It only works well with about a 20 frag limit. Survival mode is intended to give a 1v1 option where you don't have to wait around for 20 minutes before playing. I hope it finds a home on some servers, because it's the mode I most want to play.
-
-
-
-
-# sticky keys and fps dependencies - 04/07/2001 by Yrgol
-
-Melee, block, ki boost, and ki charge had a tendency to get "stuck" in online play when the wrong packet got lost. I changed these keys so that they communicate with the server in the same way firing a weapon does. They shouldn't get stuck anymore even when there's a lot of packet loss. This is a bug that's been present since build 1. I'm glad to be rid of it.
-Flight speed was highly dependent on FPS as of rc2. Ki boost cost was highly dependent on FPS since build 1. Both of these are fixed now also.
-
-
-
-
-# subject? - 04/02/2001 by Yrgol
-
-From memory since I havn't been keeping close logs lately.
-
-* got a new job
-* moved from pittsburgh to boston
-* added particle rocks and smoke to explosions.
-* added a particle trail to the particle sparks.
-* added particle bubbles to attacks underwater.
-* added some particles to power struggles.
-* redid the shaders so they don't get overbright.
-* simplified the bfp options menu by making explosion effects have a selecter between 4 different levels of intensity.
-* made kakusan fire a variable number of balls depending on the amount of time spent charging it. fires from 2 to 6 homing balls. slowed the charge time, fixed the angle of fire on the homing balls.
-* made ki drain a percentage for eyebeam, shyogeki ha, and kiaho.
-* made kiaho a constant fire attack. decreased the range massively. decreased the knockback somewhat. made it bounce all missile attacks except for deathball and kienzan.
-* eyebeam is NOT underbalanced in the slightest in its current form.
-* increased the speed of all beam attacks.
-* made the menu look prettier.
-* added 2 new maps, a giant sound pack from 17, a new console font from gp, a few random things from dash.
-* fixed body transformations.
-* tried out bfp at 640x480 with the options all turned up on a p3 450 running a TNT ONE 16 MEG VIDEO CARD and got 20-30 fps on danorthern against 3 bots.
-* create an installer exe for bfp.
-
-
-
-
-# particles - 02/16/2001 by Yrgol
-
-I added some particle effects to the game. I havn't really figured out what to do with them yet, but they are there. Just as a start I added the generic explosion sparks. As with every other effect, if you don't like it you can turn it off in the menu.
-
-
-
-
-
-
-# Old style update - 02/08/2001 by Yrgol
-
-changes since the thing we sent to the news sites:
-* lowered renzoku cost to 100
-* increased renzoku missile speed to 8000.
-* increased renzoku radius to 120.
-* lowered renzoku firing speed to 200ms.
-* put in a 1 second delay before you can zanzoken out of stun.
-* increased SBC radius to 150.
-* increased SBC damage from 35%*mult to 40%*mult.
-* increased mouthbeam damage from 20% to 30% (effective 60% vs same PL).
-* lowered final flash damage from 40%*mult to 25%*mult.
-* increased the angry gohan range from 500 to 1000.
-* tweaked the angry gohan aura scaling.
-* lowered angry gohan ki cost from 200 to 150.
-* fixed a bug that made other people's charging flashes not show up.
-* fixed the lagometer position
-* made an exception so the death ball can compete with beams in collisions.
-* fixed a bug that made third person extend into spectator free mode.
-* fixed a horrible bug that made players not cycle in 1v1.
-* tweaked some LMS code.
-* fixed a bug that made the blocking anim play a bit too long.
-* fixed the problem with the angry gohan shader.
-* added basic power struggles.
-* redid kiaiho.
-__
-* took away boost speed bonus while _firing_ a beam.
-* lowered kamehameha damage from 40%*mult to 30%*mult.
-* upped final flash damage from 25%*mult to 30%*mult.
-* made deathball work in power struggles.
-* put the angry gohan cost back the way it was.
-* lowered angry gohan range to 800 from 1000.
-* slowed recharge time to 6 seconds.
-__
-* added the new gohan and krillin.
-* added some attack charge sounds.
-* redid the timing on masenko and gave it a HA sound.
-* fixed a bunch of post-death bugs.
-* removed the generic flash sounds for kamehameha and masenko.
-* added a new sprite for final flash.
-__
-* added sprites for the big bang, sokidan, masenko, kamehameha, and renzoku.
-* made oozaru picking more random.
-* fixed the oozaru extendo penis.
-* totally redid flight physics.
-* changed up and down orientation while flying to be relative to the player's axis.
-* added cg_flytilt for tilting the viewpoint when boost flying.
-* fixed a bug with zanzoken going through clip brushes.
-* increased the size of the smoke ring around explosions.
-* added cg_explosionSmoke to turn off the smoke ring.
-* redid jump.
-* added wall jumps.
-* added a short delay between jumps excluding wall jumps. avoiding people looking like bunnies.
-* fixed the "slightly sticky ground" flight bug.
-* added ctf, but it's not selectable from the menu.
-* added more dramatic transformations. they are 5 seconds long and you are invuln during it.
-
-
-
-
-# Random updates - 02/04/2001 by Yrgol
-
-Ansel's been making a lot of sprites to replace some of our attack models. Flight physics was totally redone to include momentum.
-
-CTF mode was added back in, but I don't know if it's going to be a fully supported mode or not. CTF is kinda goofy when you can fly fast, and we don't have any maps for it. CTF should not be confused with capture the dragonballs, which still isn't in phase 1.
-
-
-
-
-# Power Struggles - 01/10/2001 by Yrgol
-
-Power struggles are in phase 1 now. Ki recharge time has been increased to 6 seconds for now, subject to change at any given moment. We had it take about 20-30 seconds a long time ago and it really killed the gameplay. Lots of attacks have gone through some rebalancing.
-
-
-
-# Yrgol’s Important Bid For Power Phase 1 README!!! - 01/10/2001 by Yrgol
-
-(complete with 3 different exclamation points!)
-A wise man once said: “Those who do not read this document will suck at BFP.”
-
-What is Bid For Power?
-Bid For Power (BFP) is a quake 3 total conversion. We have taken quake 3 and used it to create a new game. In order to play BFP, you will need the full retail version of Quake 3 Arena by ID Software, but once you have that, our game is a free downloadable add-on.
-
-In phase 1 of BFP, we place 6 of the fighters from the Freeza saga in an arena in order to fight. Power is gained over time, but the way to get power quickly is to kill other players. Obviously in the show, Krillin would not be able to fight Freeza, but if we followed that rule who would play Krillin? In BFP, the only difference between the characters is the set of attacks available.
-
-Features:
-- 6 different selectable characters, each with 5 attacks. Goku, Piccolo, Vegeta, Freeza, Gohan, and Krillin.
-- 21 different ki attacks including controllable attacks, homing attacks, and chargeable attacks (no guns!).
-- Toggleable flight.
-- offhand melee combat
-- Re-"charge"able ki energy for ammo, flight, and speed/power boost.
-- Power level determines the strength of attacks, speed of movement, max health, max ki, ki charge rate, and what attacks are available.
-- 5 different game modes: FFA, 1v1, Team DM, Last Man Standing Team DM, and Oozaru mode.
-- Numerous new player animations.
-- Plenty of cool effects: head and full body transformations, selectable aura, ki trail, scalable attacks, big explosions, playable third person mode, first person vis mode.
-
-Installation (windows):
-- Install the full retail version of Quake 3 Arena. The demo version will NOT work with BFP.
-- Install the 1.27 point release for Quake 3 Arena, found at http://www.quake3world.com/files/patch127g.html
-- Unzip the bfprc1.zip file into your quake 3 arena directory. Make sure to use folder names when extracting the files.
-- The directory "bfp" will be created as a subdirectory of the quake 3 directory.
-- To start the game, either use the "Mods" tab in quake 3, or start the game with command line args “+set fs_game bfp”. Creating a shortcut to quake 3 with that argument is the recommended way of starting BFP.
-
-Starting BFP:
-There are two ways to start BFP. The easiest way is to use the mods menu in Quake 3. From the main Quake 3 menu, click "mods". Then find "Dragon Ball Z: Bid For Power" on the list and highlight it. If you have a lot of mods installed, you may need to scroll the list in order to make BFP visible. Once you have BFP highlighted, click on the load button and BFP will start.
-
-The other way to start BFP is to use the command line arguments `+set fs_game bfp`. An easy way to do this under Windows is to create a shortcut. Highlight the quake3 icon in whatever directory you installed Quake 3 into, and press ctrl-c to copy the link. Press ctrl-v to create a shortcut to quake3. Right click on the new shortcut and go into the properties menu. Under the `shortcut` tab of the properties menu, change the target to `C:Quake 3 Arenaquake3.exe +set fs_game bfp` if you installed Quake 3 into C:Quake 3 Arena. Use this new shortcut to start BFP.
-
-BFP loads its own config file called 'bfp.cfg'. This allows either way of starting BFP to work without any config file problems.
-
-What is Phase 1?
-Phase 1 is the first release of BFP. It covers important characters and places from the Freeza saga of DBZ.
-
-What is Phase 2?
-Phase 2 will be the second major release of BFP. It will be released when it is done, and will cover important characters and places from the Android/Cell saga. Phase 2 will also likely have updated effects and game play. There will more than likely be a few patches between phase 1 and phase 2.
-
-How do I play a multi-player game?
-This game is designed to be played multiplayer. From the main menu, click on the "multiplayer" button. BFP will scan all of the quake 3 arena servers and display any BFP servers that it finds. Once a BFP server is found, highlight it, click accept and the game will connect to the server.
-
-Is there a single-player game?
-Just barely. This game is intended to be played multi-player. A single player game can be played with the bots, but we recommend playing against real people.
-
-Viewpoint
-Viewpoint is how you see the game while you play it. You have your choice of 3 different viewpoints for playing BFP. To switch between the view, use the BFP Options menu.
-
-- Third Person: The model is seen from the outside. Unlike the standard quake 3 third person view, in BFP the crosshair is visible, and the angle is changed to create less blocking of the view. Cvars cg_thirdPersonAngle and cg_thirdPersonRange have had their cheat protection removed, and cg_thirdPersonHeight was added for additional customization.
-
-- First Person Vis: First person where your own model is drawn. Your viewpoint is positioned on the model's eyes, and you can see your own arms and legs while fighting.
-
-- First Person: The standard first person quake 3 view.
-
-Power Level
-
-Power level is how strong you are. Power level determines max health, max ki, movement speed, damage caused by attacks, and which attacks are available to use. The only way to get more power is to kill someone. When you die (in most game modes), your power level is reset to the average of all current power levels. When the game ends or the server restarts, the power level of the players is reset to the default.
-
-Power Tiers
-
-Power level is divided into tiers. While most power level effects are based on the power level itself, aura color and attacks available is determined by the tier.
-
-- Tier 1: < 100,000 PL. Blue aura, only one ki attack available.
-- Tier 2: 100,000 - 250,000 PL. Red aura, two ki attacks selectable.
-- Tier 3: 250,000 - 500,000 PL. Red aura, three ki attacks selectable.
-- Tier 4: 500,000 - 999,000 PL. Red aura, four ki attacks selectable.
-- Ultimate Tier: 1 mil PL. Yellow aura, all attacks selectable.
-
-Transformations
-
-At 1 million power level, the transformation happens. The aura color turns yellow, and if the character has a transformation it occurs. Saiyajin characters turn into Super Saiyajins. Other than the benefits from increased power level, there is no additional benefit of the transformation.
-
-Ki Energy
-
-Ki is your stamina. Ki is used as ammunition for the ki attacks. Additionally, ki can be used to increase power, speed, jumping height, and special attacks by using the boost key. Ki can be charged in order to replenish it at any time by holding down the charge key. Beware, while charging you can not move or attack, and are highly visible to other players.
-
-Flight
-
-BFP has a toggle able flight button. The default is "f" but it can be set in the controls menu. To begin flying, simply press f once, and once again to stop flying. While flying, you are able to move to wherever you look. The jump key will make you move up, and the crouch key will make you move down. Your ki drains at a steady rate. At low power levels you will need to limit your flight, while at high power levels you can fly all day. A large speed boost is gained by holding down the boost key.
-
-Melee Combat
-
-To engage in melee combat, hold down the melee key and put the crosshairs over your opponent. If you are close range, then you will start beating on him. If you are long range, you will dive at the bad guy for a strike. To add extra knock back and stun your opponent, hold down the boost key while melee fighting.
-
-Hit Stun
-
-While in hit stun, the player can not move, attack, block, or charge. 1 second of hit stun is added when the player runs out of ki. 3 seconds of hit stun is added when melee is used in combination with the boost key. Do not overlook this feature. Being able to knock people into hit stun with ki-boosted melee is one of the keys to being good at BFP.
-*The melee stun can be avoided by holding melee while you are attacked.
-
-Zanzoken
-
-Zanzoken is a short, extremely fast movement. In BFP, zanzoken is a controlled short-range teleportation that is performed by double-tapping right or left movement keys. Zanzoken is useful for dodging attacks, and can be used to break out of hit stun after 1 second.
-
-Blocking
-
-Pressing block once grants the player between one and two seconds of blocking. Blocking drains ki quickly, but transfers all damage to ki instead of health. Blocking can also be used to deflect missile attacks. There is a one second delay between when the player stops blocking and when the player can block again.
-
-Ki Attacks
-
-Each character has 5 attacks. At the lowest power tier, the player only has one selectable attack. Each time the player advances to a new power tier, he gets a new selectable attack. By default, each of the numeric keys, 1-5, choose a different attack. Only one attack can be selected at a time, but switching between attacks is instant.
-
-- standard attacks: hold down fire and you fire.
-- charge-up attacks: hold down fire to start charging the attack, release fire to fire.
-- minimum charge-up attacks: same as charge-up attacks, but with a minimum charge-up time. The ball of ki will appear in the player's hands once he is prepared to fire.
-- beam attacks: a type of charge-up attack. Can only fire one beam at a time. After firing, you can aim the attack by moving the mouse, or detonate it by hitting fire again.
-- homing attacks: these attacks will home in on the nearest opponent.
-
-Attack Collisions
-
-Unlike quake 3 attacks, BFP attacks are more than just a trajectory. Attacks have actual dimensions, and will explode on contact with players. If an attack looks 50’ wide, it will hit any players who come within 25’ of the center of the attack. Mid-air attack collisions also happen. Two attacks that collide will damage each other, and the stronger attack will cause the other to explode. The exception is any beam attack, which will destroy any other attack that it makes contact with.
-
-Power Struggles
-
-When two beam attacks collide, a power struggle will happen. Instead of the stronger beam blowing up the weaker beam, the stronger beam will push back the weaker beam until it hits the other player. Ki boost can be used to up the power of your beam if you are losing a power struggle. The Death Ball is an exception among the missile attacks, and can be used in a power struggle.
-
-Krillin
-
-- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
-- Taiyoken: the solar flare. Hold down fire until charged, then release to blind people around you.
-- Sokidan: homing ball. Hold down fire until charged, then release to fire.
-- Kakusan: a missile that spawns 4 homing balls when detonated. Hold down fire until charged, and release to fire the missile. Press fire again to detonate the missile into 4 homing balls.
-- Kienzan: the destructo disk. Hold down fire until charged, and release to fire. This attack goes through walls and does massive damage.
-
-Vegeta
-
-- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
-- Bakuhatsuha: larger ki blast. Press or hold fire to fire a blast.
-- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
-- Big Bang Attack: a beam that has a massive explosion. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate into a massive big bang explosion.
-- Final Flash: a large and powerful beam attack. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
-
-Gohan
-
-- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
-- Sokidan: homing ball. Hold down fire until charged, then release to fire.
-- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
-- Masenko: beam attack. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
-- Energy Dan (Angry Gohan Attack): Gohan creates a massive and continuous explosion around himself. Press and hold fire. After a few seconds the explosion will appear. Gohan can not move while performing this attack.
-
-Piccolo
-
-- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
-- Eye Beam: a continuous blast of power from the eyes. Press and hold fire.
-- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
-- Chobakuretsumaha: a fast and powerful homing attack. Hold down fire until charged, and release to fire.
-- Special Beam Cannon: a fast and narrow but extremely powerful spiraling beam. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
-
-Goku
-
-- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
-- Kiaiho: An invisible blast of energy from the hands with a massive knockback. Press or hold fire to attack.
-- Sokidan: homing ball. Hold down fire until charged, then release to fire.
-- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
-- Kamehameha: beam attack. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
-
-Freeza
-
-- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
-- Shyogeki Ha: fast firing, extremely fast moving ki blasts from the fingers. Hold fire to attack.
-- Freeza Beam: an extremely fast ki attack fired from the finger. Press or hold fire to fire a blast.
-- Tsuibi Kienzan: Freeza’s homing version of the kienzan. Hold down fire until charged, and release to fire. This attack goes through walls.
-- Death Ball: a gigantic ball of ki. Hold down fire until charged, and release to fire. This attack is huge in size and does massive damage.
-
-Game Types:
-
-- Death Match: Players start at ~50,000 PL. Additional PL is gained for each kill. On death, the PL of the player resets to the average of all other PLs.
-- Tourney: Same rules as DM, but with only two players at a time.
-- Team DM: Same rules as DM, but with teams.
-- Team LMS: Last Man Standing. If you’ve ever played clan arena you should know this mode. Players start at 1 mil PL, and players who die can not respawn until the next round.
-- Oozaru: This game works as regular FFA, except one player is chosen to be the ~50’ tall Oozaru. The player who kills the Oozaru gets to be the Oozaru next. The Oozaru has extra power and speed, and is also a giant compared to the other players.
-
-CVARS:
-
-If you don't already know what a cvar is you should probably ignore this section. These can be used from the q3 console to access features. Most of these features can be set from within the menu.
-
-* g_basepl [1-999]: set the starting power level from one thousand to one mil.
-* cg_kitrail [0-99]: set the length of the ki trail. 0 turns it off.
-* cg_crosshairhealth [0/1]: turn on or off the feature that makes the crosshair turn red when you score a hit.
-* cg_lightauras [0/1]: turn on or off the aura dynamic lights.
-* cg_lightexplosions [0/1]: turn on or off the explosion dynamic lights.
-* cg_drawKIWarning [0/1]: turn on or off the low ki warning.
-* cg_chargeupAlert [0/1]: turn on or off the "ready" message when charging attacks.
-* cg_stfu [0/1]: disable character voices when firing attacks.
-* cg_lowpolysphere [0/1]: force the use of a lower polycount sphere.
-* cg_explosionShell [0/1]: turn on or off the explosion shell.
-* cg_drawOwnModel [0/1]: toggle first person between traditional and vis modes.
-
-Help my computer can’t handle BFP!
-
-With all of the graphics options turned on, BFP takes a much beefier system than standard Quake 3. If your computer sucks, don’t despair. We’re looking out for you. BFP is meant to push a p3 500 with 128 megs of memory and a GeForce video card, but it is designed to be able to run playably on a celeron 300 with 128 megs of memory and a TNT2. However, with a much weaker system than that, you are pushing your luck with BFP.
-
-Here are some things you can do to try to boost your performance.
-
-- The first thing to try is turning off dynamic lights in the game options menu. Quake 3 can be slow when it comes to handling dynamic lights, and there are a lot of them in BFP.
-- Go into the BFP options menu and use the ki trail slider bar to lower the length of the ki trail. Few systems can handle the ki trail at its max length without framerate loss. With the slider bar all the way to the left, the ki trail will have 0 length. With the slider bar all the way to the right, the ki trail will almost stretch the length of our maps.
-- Go under the systems setting menu and lower the resolution to 640x480. Don’t gag, most Quake 3 competition players seem to use this resolution.
-- Go into the BFP options menu and set the aura type to “polygonal aura”. This is the least costly aura type. Using the BFP options menu, turn transformation aura off, dynamic aura lights off, dynamic explosion lights off, ki trail off, small own aura on, and ssj perma glow off. With these settings BFP should run about as well as quake 3 does on your computer.
-- If you are really having trouble go into the video set-up menu and change the lighting from lightmap to vertex. Everything will look crappy, but the game will run faster.
-- If your system is running low on memory, use the BFP options menu to turn Force Default Skins on. This is the BFP equivalent of cg_forceModels in Quake 3. Force Default Skins will disable all custom skins for the models. With this option on, there will only be one skin shared between all Vegetas.
-
-Who made this game, and why is it free?
-
-Bid For Power was made by a group of volunteers. We’re all DBZ fans and most of us have watched all or nearly all the Japanese episodes. I can’t speak for all the reasons of the individual team members, but we all thought the idea of a first person DragonBall Z game was cool enough to want to get involved. It’s free because we are not a company, and don’t have the money to pay off people like Id. This game was created by people, for fun, in their spare time. Everyone on the team contributed to the game design and art style.
-
-- Chris James: project lead, coordinator, original concept, PR.
-- Yrgol: programming, game design.
-- Rodney: Goku and Piccolo models, animation, additional model tweaking, 2d art, skins.
-- Pyrofragger: Gohan, Krillin, and SSJ Goku models, animation, DNamek map, aura and attack models.
-- NilreMK: Phase 2 and “secret character” models, animation, attack models, Q3 editing guru.
-- Mooky: Vegeta and Freeza models.
-- Ansel: lead skin artist, 2d art
-- Dethayngel: skins.
-- Ebola: skins.
-- Anthony: 2d art, char select art.
-- Badhead: attack and aura shaders.
-- Number17: sounds.
-- Yngwei: Snake Way map, skyboxes, performed all music used in BFP.
-- GangstaPoodle: GPCity, GPNamek, GPCityDark, and GPCave maps and textures, 2d art.
-- Kit Carson: Earth and NamekXL maps and textures.
-- $onik: Kami’s Lookout map textures.
-- Perfect Chaos: additional map textures.
-- Nat: webmaster, additional programming.
-- Disco Stu: web design
-
-
-
-
-# zanzoken - 12/17/2000 by Yrgol
-
-Zanzoken is in now. I'm doing it as a short range controlled-direction teleport. Double tap right or left in combination with other movement in order to zanzoken. I don't want to get into specifics but yes there are restrictions and costs to prevent this from being used all the time.
-
-
-
-# stuff - 12/14/2000 by Yrgol
-
-As you can tell I'm still making the mod. The bots own now. If all that mattered were code, bfp would be ready to release. Everything that I said was going to be in phase 1 is in there now and then some, and the bugs are minimal. A few of our models still need some tweaking before the whole is releasable.
-
-Funimation did indeed send us a cease and decist letter within days of selling dbz game rights to Infogrames. However, they have decided to take a closer look at the situation. We did not want to jump the gun on being foxed, because things are still fairly up in the air. We did not want the slashdot post to happen.
-
-Bid For Power will be released. It will most likely have Dragon Ball Z characters in it when it is released.
-
-17-3 (not finished):
-* made bots aim beams.
-* made bots charge attacks randomly above the minimum.
-* made bots melee.
-* made bots act blind when hit with the taiyoken.
-* made bots aware of splash damage on charge-up attacks.
-* lowered shyogeki ha damage from 20 to 5.
-* lowered shyogeki ha range from 8192 to 1200.
-* lowered shyogeki ha spread.
-* made homing attacks easier to dodge.
-* lowered kiaiho range from 8192 to 1000.
-* made kiaiho do a stable 20% damage.
-* removed the damage boost for ki boosting.
-* got rid of the character change messages because they never worked right.
-* shrunk the pre-fire deathball a bit so it doesn't clip freeza's head.
-* made blindness not last through death.
-* got rid of the melee axehandle smash direction.
-* fixed a bug in melee that caused a crash.
-* lowered hit-stun time from 3 seconds to 2.
-* capped self-splash at 20%.
-* added in the separate kienzan attack shader.
-* shrunk freeza's kienzan.
-* fixed a bug that made the kienzans stay around far longer than they should.
-* fixed a bug that made kienzans hit way too fast and too often.
-* gave the kienzans a max number of hits, which fixes a memory leak.
-* fixed a bug with the animation timers.
-* fixed a bug with switching weapons while firing a beam.
-* fixed a bug with switching weapons while charging up an attack.
-* made charging only prevent intentional movement, not glue you in place.
-
-17-2:
-* made bots be 100% agressive all the time.
-* made bots fly a better.
-* taught bots how to use charge-up attacks and beams.
-* fixed bot aim.
-* upped the charge time for the chobakuretsumaha and sokidan.
-* scaled down the pre-fire flashes of most attacks.
-* fixed the spelling and grammer in the bot files and fixed the error messages.
-
-17-1:
-* redid third person view.
-* got around the gimble lock for flight so you can go upside down.
-* changed default cg_thirdpersonheight to -50.
-
-
-
-
-# more sutff than you wanted - 11/29/2000 by Yrgol
-
-16-3/17:
-* fixed the timing of the "ready" message with the kamehameha.
-* made a bfp.cfg file so that bfp will work fine by using the mods menu.
-* fixed a bug that caused untextured weapon flashes to appear.
-* fixed a bug that caused attack voices to sometimes come out at the wrong time.
-* added cg_stfu to turn the voices off.
-* fixed a bug that made melee mess up charge-up attacks.
-* fixed a bug that caused a lot of problems in the char select screen.
-* fixed a bug that made melee screw up the weapon time and hit way too fast.
-* fixed a bug that made the charge-o-meter misread the sokidan/chobakuretsumaha power.
-* fixed another bug with the ready message.
-* added cull disable to all the explosion shaders so they don't disappear when you get too close.
-* added an explosion shell/shockwave. can be turned off with cg_explosionShell 0.
-* added cg_lowpolysphere to toggle between a low and a high polycount sphere model.
-* fixed the infamous death-spawn bug.
-* fixed the sizing of the accept button in the video setup menu.
-* seemingly accidentally fixed the problem of ki trails showing through walls.
-* made cg_thirdpersonrange and cg_thirdpersonheight get saved in bfp.cfg.
-* gave cg_thirdpersonheight a default of 20.
-* increased freezabeam explosion radius.
-16-2:
-* added g_basePL with a slider in the server startup to define starting PL.
-* gave the renzoku an alternating firing offset.
-* cut chobakuretsumaha damage in half.
-* upped the ki cost of the chobakuretsumaha.
-* lowered freezabeam damage.
-* made melee hit stun not happen when both people are holding melee.
-* made homing missiles select a target based on angle instead of distance.
-* refined homing angle selection somewhat.
-* fixed the sbc spiral so it disappears less often.
-* fixed jump (detection and animation).
-* made the kienzan stop disappearing when it hits other missiles.
-* added self splash damage back in.
-* made blocking deflect missiles.
-* added in 17's explosion sounds.
-* added in character sounds for sbc, kamehameha, final flash, bb attack, masenko.
-* hopefully fixed the ride me big boy bug.
-
-16-1:
-* made the spiral not disappear when two people sbc at the same time
-* made ki trails a little more robust
-* forced r_railCoreWidth to 15 on startup.
-* removed a bug with the ssjhead getting an aura when it shouldn't.
-* fixed a bug that caused the char select screen to crash the game.
-* resized images so they were powers of 2.
-* got rid of "unknown gametype" when connecting to a oozaru or lms server.
-* homing attacks don't turn all the way around anymore.
-* homing attacks no longer lose their velocity when homing.
-* tweaked the various homing missile speeds to account for the new code.
-* made taiyoken based on FOV, meaning you have to see it to go blind.
-* set the size of menu buttons to 80x80 and made those values
-settable through one file instead of 30 different files.
-* made the eyebeam visible from all perspectives.
-* upped the eyebeam damage.
-* made the eyebeam not draw when holding melee.
-* fixed a rotation problem on the auras.
-* fixed a problem with showing the wrong gametype name when starting a server.
-
-16:
-* decreased melee dive range from 2000 to 700.
-* hopefully fixed the problem of melee dive teleporting you into objects.
-* new menu.
-* new attack icons.
-* new hud.
-* new ki trail.
-* fixed the servers menu to differentiate between bfp gametypes.
-* removed the "impressive" for the freezabeam.
-* lowered the ki cost of the freezabeam from 1000 to 500.
-* made the freezabeam explode when it hits a player instead of going through them.
-* gave the freezabeam explosion some damage.
-* changed the sound played for the freezabeam.
-* fixed a bug in the eyebeam that made the beam not show up.
-* changed the name of the sbc to makankosappo.
-* removed drowning.
-* made the speed boost depend on PL instead of power tier.
-* made cg_kitrail be a slider var from 0 to 99 for ki trail length.
-
-15-8:
-* removed heals from spawning on maps.
-* increased visual explosion size.
-* made charging a lot more effective.
-* cut damage in half.
-* made melee dive a lot more solid.
-* gave the melee hit sound an origin.
-
-15-7:
-* made the chargeup alert display in third person
-* moved the chargeup alert to the bottom of the screen.
-* plays the pain sounds based on percent of max health.
-* upped masenko damage.
-* made the hud player counter not include dead people.
-* removed gibs for killing someone. you can still gib a corpse.
-* made the prepare anim play for kakusan.
-* fixed the timing on at least some charge up attack anims.
-* added a flash model to firing beams.
-* added KamehamehaFlashShader etc for the beam attacks.
-this shader needs to be opaque enough to hide the beam origin
-* scaled down the pre-fire ball for beam attacks.
-* removed the standard flash entirely.
-
-15-6:
-* added a "ready to fire" message for charge-up attacks.
-* added cg_chargeupAlert to turn off that message.
-* fixed a bug that would let attacks get kienzan properties.
-* upped kakusan damage some.
-* lowered taiyoken damage to 1%.
-* added a visibility check on taiyoken. only checks barriers, not direction.
-* increased taiyoken chargeup time.
-* shortened blindness time some.
-* made the melee_axehandle animation run, but it's not very noticable.
-* toned down the angry gohan.
-* made the prepare anim play for the angry gohan.
-* changed the scoreboard name of the angry gohan to the Angry Gohan Attack.
-* fixed "the incredible eternal kienzan" bug.
-
-15-5:
-* first person vis mode works. it works best with models that have tag_eyes (gohan/krillin).
-* angry gohan no longer draws while meleeing or charging.
-* took out the announcer sounds for player names.
-* blindness no longer stays with you through death.
-* took out the ring model for explosions on players.
-* fixed a scaling problem in the explosion ring that made it go black.
-* removed the eject brass option from the game options menu.
-* individual score now survives death in LMS mode.
-* following in LMS spectator mode no longer fucks the next round.
-* got rid of some red in the menu.
-* gave explosions and missiles dynamic lights.
-* added cg_lightExplosions to turn off dynamic explosion lights.
-* removed the chargeup anim for taiyoken.
-
-15-4: lost all code since 15, had to redo stuff.
---SHADERS--
-* added shaders for kamehameha and masenko.
-* added separate beam shaders.
-* added separate shaders for explosions.
-* added a shader for the freezabeam explosion
---WEAPONS--
-* freeza's second attack no longer ejects brass.
-* fixed manual beam detonation.
-* missiles should collide with each other a bit better.
-* fixed homing attack collisions.
-* increased eye beam range.
-* removed self-damage.
-* gave deathball a min charge up time (it was supposed to have one).
-* gave masenko a min charge up time (it was supposed to have one).
-* decreased the chargeup speed of sokidan/chobakuretsumaha.
-* made all weapons explode when they hit a player.
-* put the BIG BANG in the BIG BANG attack
-* pumped up the deathball.
-* can't continue firing a beam while charging, blocking,
-melee fighting, or in hit-stun.
-* beam attacks use the sphere as the pre-fire flash.
-* beams now trump missiles for mid-air collisions.
-* lowered renzoku damage.
-* made taiyoken work.
-* gave the SBC a spiral trail.
-* weapon firing sound plays while firing beams instead of
-while charging them.
-* made the freeza beam red and gave it an explosion.
-* gave explosions a smoke ring.
---OTHER--
-* custom skins now work fine. if you use a custom
-vegeta skin that I don't have, instead of showing
-up as sarge you will show up as vegeta/default on
-my screen.
-* somewhat improved bots.
-* cg_forceModel removed.
-* added cg_forceSkin to force the default skin for each model.
-* changed obituary messages.
-* beam attacks no longer blind you in first person view.
-* switched from the nuke model to the sphere model for explosions.
-* tweaked explosions a lot.
-* picking up a normal health after a mega health won't set
-you back to 100% health anymore.
-* no longer plays the melee sound for missiles.
-* unfucked spectator mode.
-* switching chars while in LMS mode causes a suicide like in
-any other game mode.
+
+# BFP MAKES YOU TALK IN CAPS - Yrgol - 05/12/2002
+
+For those that were wondering who these guys are, the about section has been updated to include character descriptions and a little backstory for all the fighters.
+
+
+
+# 1.2 100,000 - Yrgol - 05/10/2002
+
+Version 1.2 of Bid For Power has now been downloaded over 100,000 times. I'd like to give a big thanks out to everyone who stayed with us.
+
+
+
+# 1.2 Tourney - Yrgol - 05/08/2002
+
+With the demise of the XSL there's another BFP tournament forming. They are having a 1v1, 2v2, and 3v3 tournament with signups going on now.
+Visit their page at https://web.archive.org/web/20020810122128/http://bfp-allstars.us4n6.org/
+
+
+
+# Its True, Its True! - Yrgol - 05/06/2002
+
+1.2 is now ready for download. The files section of this page has been updated. I will be adding more links as the night goes on.
+
+
+
+# 28 hours until 1.2 - Yrgol - 05/05/2002
+
+1.2 will be released tomorrow at around 9pm eastern. I added a few images to the action section. When the big fuzzy focus on planetquake gets updated they'll have another 8 screenshots which combined with the new images on this page will cover all the new attacks.
+
+
+
+# BFP 1.1 Update Information - 02/04/2002 by Yrgol
+
+The grand list of my updates to the latest build of Bid For Power. Enjoy.
+
+BFP 1.1 Changes
+
+features:
+
+* added a bfp_server.cfg file, which lets server ops control:
+
+- flight cost, boost cost, ki regen, and ki charge regen.
+
+* aura color is now forced to red or blue in team games depending on team.
+
+* new console.
+
+* tweaked attack shaders for lighting values.
+
+* new fingerblast look and sound.
+
+* lighter versions of the maps gptourney1 and gpctf1.
+
+* added cone of fire support to the attack scripting.
+
+balance:
+
+* lowered boost cost significantly
+
+* upped ki regen rate so that flying at max pl costs nothing.
+
+* fixed the extra knockback on attacks that use that feature. tornado blast uses this.
+
+* rebalance of most attacks.
+
+bugs:
+
+* fixed the 1v1 server crash. 1v1 servers should be as stable as the other game types now.
+
+* fixed handling of plugin player models on clients that don't have the model.
+
+* fixed a "weapon number out of range" message followed by a crash when playing with bots.
+
+* fixed the appearance of explosion spawn attacks (homing special multi-beam bug).
+
+* made suicide take away a kill.
+
+* fixed losses being assigned to the wrong player in 1v1 games.
+
+* ctf servers show up in 1.0 as lms servers. fixed.
+
+* survival servers show up in 1.0 as single player servers. fixed.
+
+* gametype in 1.0 gets cut off in the servers list. fixed.
+
+* fixed missing friend shader for team games.
+
+* added dummy player sounds for tetsedah, ryuujin, and pyrate to get rid of some warning messages.
+
+* fixed the small own aura setting.
+
+* switched the missile dlights of the two razor disks.
+
+* fixed tornado blast catching attacks that have higher priority.
+
+* fixed ki use toggle.
+
+* fixed position of sparks in power struggles.
+
+
+
+
+# more stuff for the patch - 01/23/2002 by Yrgol
+
+* fixed the 1v1 server crash. 1v1 servers should be as stable as the other game types now.
+* lowered boost cost significantly
+* upped ki regen rate so that flying at max pl costs nothing.
+* fixed a "weapon number out of range" message followed by a crash when playing with bots.
+* fixed the extra knockback on attacks that use that feature. tornado blast uses this.
+* fixed losses being assigned to the wrong player in 1v1 games.
+
+
+
+# bugs so far - 01/20/2002 by Yrgol
+
+I'm waiting for a decent amount of bugs to show themselves before doing a patch.
+
+fixed:
+* added a bfp_server.cfg file, which lets server ops control: flight cost, boost cost, ki regen, and ki charge regen.
+* fixed the appearance of explosion spawn attacks (homing special).
+* aura color is now forced to red or blue in team games depending on team.
+* ctf servers show up in 1.0 as lms servers. fixed.
+* survival servers show up in 1.0 as single player servers. fixed.
+* gametype in 1.0 gets cut off in the servers list. fixed.
+* fixed missing friend shader for team games.
+* added dummy player sounds for tetsedah, ryuujin, and pyrate to get rid of some warning messages.
+
+known and not fixed yet:
+* there seems to be some stability issues with 1v1 servers. this mainly seems to happen on map changes.
+
+
+
+
+# patch - 01/19/2002 by Yrgol
+
+The next thing released by bfp will be a dbz plugin media pack or two. This will be an optional download that includes the old models in plugin form and some new maps.
+
+After that, the next releae of bfp will be a patch that will arrive in about two weeks to a month or so from now. This patch will have a decently small filesize, and it's for fixing whatever bugs show up. If you encounter a bug, please visit the bugs forum to post a message so I can fix it. Once most of the bugs are gone I'll start working on new attacks with some real variety now that my hands aren't tied by dbz.
+
+The homing special has a graphical glitch. Once it spawns the multiple homing missiles, those missiles appear to each player as his own 4th tier attack instead of appearing as the attack that it is supposed to. This has no effect on how the attack actually operates and does damage, but it can get confusing. This is already fixed for the next version.
+
+
+
+
+# Modding BFP - 12/13/2001 by Yrgol
+
+It is possible to modify bfp in a number of ways. Some of them are compatible with the base bfp, and others are destructive to the base bfp. The only type of bfp modding that I will support is the compatible type.
+Multiple groups have tried to make their own versions of various hacked copies of bfp, so in order to prevent players from having to choose between versions, I'm posting a little guide on how to make mods of bfp coexist with each other. This is not supposed to be a comprehensive guide on how to mod bfp, just some dos and don'ts if you decide to modify it.
+
+Model and Map Packs:
+If you only want to create some new models that use existing attack sets, or new maps to play on, create model and map pk3s and release them together so the players can install them in their base bfp directory. These models and maps will be selectable in the menu just like any other model or map. BFP allows for a significant amount of variation in plugin models.
+
+Game Balance:
+If you are unhappy with the balance of the game and run a server, just modify the script file bfp_weapons.cfg to suit your tastes. You can then start up your server and people using the base bfp will be able to play there. I will be adding more ability to do this in the future, but what already exists is pretty significant.
+
+New Attacks or Attack Sets:
+If you wish to add new attacks or attack sets to the game, you will have to create a full mod of bfp. This is because otherwise, players will accidentally connect to your bfp mod using the base bfp, and their game is likely to crash. More importantly, I don't want players to have to randomly try bfp servers until they can find one that is compatible with the version they are using.
+
+The server-finding code in the game will find any server running any mod that lives in a directory that starts with "bfp", and display the full name of that mod. The official bfp files will be in the "bfpq3" directory. Unnoficial mods of bfp can go in directories like "bfpmyversion" or "bfpcool". Players will be able to see "bfpcool" or "bfpq3" in the menu, so that they can find a server compatible with the version of bfp they are running.
+
+Rule #1:
+Never add or remove anything to any file named similarly to bfp0.pk3 in the base bfp directory. This will invalidate sv_pure, and it will prevent me from patching the game! You can use quake 3's pk3 indexing to override files in bfp0.pk3 without touching that file.
+
+Rule #2:
+Nobody is getting ahold of my source code for quite a long time, so don't bother asking. If you wish to add new code to bfp, you will have to code the entire game from scratch.
+
+Rule #3:
+Do not include any official bfp model, map, or image files with your mod. Free game or not, enforcable or not, doing so is a violation of copyrights and is also totally unecessary. Make use of quake3's base mod ability. Quake 3 allows you to create a mod that uses files from another mod's directory. If you do not want your players to have to download the official bfp, you will have to create all new maps and models to use for your game.
+
+
+
+
+# weapon limit - 12/07/2001 by Yrgol
+
+Quake3 has a limit of 16 different weapons. BFP used to have a limit of 31 total attacks, with 21 of those being used. Thanks to about 2000 lines of code changes, BFP now has a practically unlimited max number of attacks.
+I currently have slots for 99 different attacks that can be added without any code, but I could easily double or triple that if bfp ever comes close to filling up all 99 attacks.
+
+
+
+
+# mp3 support - 11/30/2001 by Yrgol
+
+I added Tim "Timbo" Angus' mp3 to wav converter into bfp. It can be found at http://tremulous.sourceforge.net/junk/cg_mp3decoder/
+
+This will allow us to include more music in the download, since each music file will have a smaller filesize.
+
+This will not allow you to play your own mp3's. It is not an mp3 player, it is an in-game utility to convert mp3's into a q3-playable format.
+
+
+
+
+# Client-side scripting - 11/29/2001 by Yrgol
+
+I've finished the client side weapon scripting, and I've posted a doc in the editing and modifications forum describing how to use the scripting in order to create plugin models for BFP.
+You can find the post here:
+http://www.bidforpower.com/forum/showthread.php?s=dc60acd92fe3f101aff5ad112f78d25d&postid;=508160#post508160
+
+
+
+
+# 1.30 and bots - 11/19/2001 by Yrgol
+
+Oddly enough, the 1.30 point release fixed bot flight, which the 1.17 point release broke. The bots have no problem flying up or down anymore. The bots switch weapons now as well, and I added a few other bot tweaks.
+Bfp bots are pretty good for deathmatch practice. They're working much better than I expected. I'm sending a big woot out to the 1.30 point release for q3.
+
+
+
+
+# - 11/17/2001 by Yrgol
+
+What we are currently working on is equivalent to beta 2 for most mods, or in bfp jargon phase 1. We had phase 1 completed for dbz quite a long time ago, and then the funimation/infogrames stuff happened. Most of the time since then has been used for converting bfp to not use dragonball z copyrights.
+
+The code is ready to go, the ui images have been changed to be legal, and we have several non-dbz maps finished. I currently have 3 of the 6 necessary default models. When I have all 6 default models, I will create a full build for the team to test. If nothing horrible goes wrong we'll probably release about a week after that.
+
+I've been working on scripting much of bfp. When this is complete, new characters and attacks will not require any additional code. This will allow team members and the community to create new unique characters for future releases. There will be a second release which will likely include more maps and models, some more in-depth gameplay modes, more attacks, and a completed attack scripting system. I will also likely be replacing the melee system with something different.
+
+I've added much more powerful custom plugin model support. The old dbz models will be able to perform identical to what they did before, provided they are converted to the new format. I have repackaged the old bfp models and included the necessary script files for them to work. I will be working on making the support for customizations much more significant, but that will be for future releases.
+
+I added server side weapon scripting. It allows a large amount of control over the game balance by the server operators, and makes my job of balancing much easier. It can not yet be used to create new attacks, but it will eventually. While doing this I fixed up a few serious gameplay complaints.
+
+Direct hit detection has been made more reliable. This makes the kienzan equivalent and the deathball easier to use.
+
+Explosion damage is no longer all or nothing. The damage decreases as distance from the explosion increases like in baseq3.
+
+Attack radius has been separated from explosion radius. The attack radius and explosion radius for each of the attacks have been tweaked.
+
+The knockback physics for attacks and explosions has been seriously tweaked. Attacks can now be used to rocket jump.
+
+Boost jumping has been toned down. It was simply too excessive before.
+
+Another total rebalance of all attacks happened.
+
+
+
+
+# scripting - 10/26/2001 by Yrgol
+
+There's no way I can get all of this done before release, but this is the direction I'm going in.
+
+I'm working on attack scripting for servers only. This will allow new attacks of any rough type that bfp already contains to be added to the game with little or no code. I have a script file set up and partially implemented that has like 30 variables for each of the attacks. This will not allow models to define new attacks, but it will allow team members to work on attacks without me, or mods of bfp. It also lets server ops who hate the game balance to tweak it significantly.
+
+I have plans to add a server script file to define what the attack sets are, and even how many different attack sets there are. This config file will make adding new attack sets for existing attacks a trivial task. The file will look something like this:
+
+```
+attackset 1
+attack1 15
+attack2 20
+attack3 21
+attack4 2
+attack5 30
+defaultmodel models/players/goku
+```
+
+Per-skin config files have been added that allow things like attack names and voices to be defined by the skin. I have plans to move a lot of stuff into these config files, including attack shaders, sounds, and models. The individual models will never be able to define how the attacks operate, only how they are displayed to the user.
+
+I will eventually create a separate server config file that defines all the ki costs for flight and things along those lines.
+
+I've been working on the server attack config scripting for about a week, and it's a huge job. I can not finish all of this in a reasonable time frame, but eventually bfp will be close to just being an engine with the game all defined by runtime scripts. Once that point is reached I'll no longer be the bottleneck for a lot of the future work, and I can concentrate on gametypes and random stuff.
+
+I do not expect much of this to be implemented enough to matter before we get to release though. I'd much rather release asap and then improve.
+
+
+
+
+# Conversion - 09/10/2001 by Yrgol
+
+With the exception of the names, the attacks are not changing. Most of the maps are not changing either.
+The new thrust of development for me is moving as many things as reasonable into the player models. Custom player models can be made for any of the sets of attacks, and I'm working on a way for skins to define the names of the attacks. I don't want to let each skin pick the attacks to use for balance, memory, and implementation reasons. The voices that get played when using certain attacks have been moved into the player model.
+
+The bfp leak is bfp's biggest competition right now. The release has to be better than the previous stuff. There are a few annoying bugs to work out, and some of the top tier attacks throw balance way off.
+
+My job has been sapping most of my time and motivation to program lately.
+
+
+
+
+# HoverQ3 - 08/17/2001 by Yrgol
+
+I released a little mod called Hover Q3. It includes some art from Pyrofragger, Kit Carson, and Sole. You can find it at http://www.bidforpower.com/yrgol/hoverq3
+
+It's a 3 meg download and is a lightweight but fun mod. Don't expect anything like bfp from this, but it's well worth checking out.
+
+
+
+
+# destructable objects - 07/08/2001 by Yrgol
+
+Breakable map entities are in, with multiple stages of breaking. I havn't done anything fancy with particles and destructable objects yet. None of the maps currently use this feature.
+
+
+
+# stuff - 07/06/2001 by Yrgol
+
+custom models are fully supported now. if you create a hulk hogan model and put it in /models/players/vegetahogan/, then the hulk hogan model will show up in the ui as a selectable skin for vegeta, brotha. I'm gonna hit you with a final flash in a steel cage.
+
+beams are bendy now. beams are not guided missiles, they are a beam of energy that gets longer quickly. when you turn left while firing, the beam bends to the right briefly before straightening out again.
+
+other than that, some random bugfixes in the animation playing and 1.29 fixes.
+
+
+
+
+# <-- that way - 06/21/2001 by Yrgol
+
+* fixed animation jitter caused by jump anims that shouldn't be played.
+* made it play the run anim faster during boost run.
+* fixed the bug that made the ki trail show up behind objects.
+* gave the accurate third person crosshair some smoothing to prevent it jumping around.
+* added a toggle to use a stable crosshair instead of the accurate one.
+* fixed a bug that made killing yourself give you a PL boost.
+* made renzoku cost a ki pctage to prevent link overflow
+* fixed a cheat that let people get vegeta's attacks with a piccolo model.
+* enabled custom player models.
+
+I have a real job, and right now it's very demanding. When I have time and energy for bfp, I'm trying to polish as much as I can.
+
+The custom player models are weakly supported. Just for example, if you wanted to make a gundam and put it in bfp with vegeta's attacks, you'd have to create a model and skin in models/players/vegetagundam. Once there, it acts as a vegeta skin, even though it's actually a skin and a model. If you were playing with someone who didn't have your gundam model, they'd see the default vegeta.
+
+I'll release a far more descriptive doc on how to make a custom bfp model later on when it matters. BFP uses a lot of animations.
+
+
+
+
+# random update - 05/29/2001 by Yrgol
+
+had a somewhat productive week. bfp comes in spurts.
+
+updates:
+* accurate 3rd person crosshair.
+* particle dust trail for boost running or flying close to the ground.
+* particle bubble effects that float to the surface and gather there.
+* particle splash + bubbles for entering water.
+* particle bubble trail for boost swimming:
+
+
+* particle bubble trail for boost flying close to water.
+* particle bubble effects for charging underwater:
+
+
+* fixed the waterdance bug.
+* lowered zanzoken cost.
+* added antigrav rock particles for charging:
+
+
+* the antigrav rocks fall to the ground when you stop charging.
+* gave rock particles collision detection and bouncing properties
+
+
+
+
+# survival mode - 04/08/2001 by Yrgol
+
+I added (past tense) survival mode to the game. This might morph into budokai mode when mappers and I both get some time. The name comes from the mode found in a lot of fighting games.
+
+Survival mode is basically 1v1 where if you die once, you go to the end of the line. The winner does not get healed in between matches, and people spawning in get power level based on the average like in FFA mode. Spectator scores are saved and visible. The winner is the first person to reach the frag limit.
+
+1v1 mode is meant for challenge matches. It only works well with about a 20 frag limit. Survival mode is intended to give a 1v1 option where you don't have to wait around for 20 minutes before playing. I hope it finds a home on some servers, because it's the mode I most want to play.
+
+
+
+
+# sticky keys and fps dependencies - 04/07/2001 by Yrgol
+
+Melee, block, ki boost, and ki charge had a tendency to get "stuck" in online play when the wrong packet got lost. I changed these keys so that they communicate with the server in the same way firing a weapon does. They shouldn't get stuck anymore even when there's a lot of packet loss. This is a bug that's been present since build 1. I'm glad to be rid of it.
+Flight speed was highly dependent on FPS as of rc2. Ki boost cost was highly dependent on FPS since build 1. Both of these are fixed now also.
+
+
+
+
+# subject? - 04/02/2001 by Yrgol
+
+From memory since I havn't been keeping close logs lately.
+
+* got a new job
+* moved from pittsburgh to boston
+* added particle rocks and smoke to explosions.
+* added a particle trail to the particle sparks.
+* added particle bubbles to attacks underwater.
+* added some particles to power struggles.
+* redid the shaders so they don't get overbright.
+* simplified the bfp options menu by making explosion effects have a selecter between 4 different levels of intensity.
+* made kakusan fire a variable number of balls depending on the amount of time spent charging it. fires from 2 to 6 homing balls. slowed the charge time, fixed the angle of fire on the homing balls.
+* made ki drain a percentage for eyebeam, shyogeki ha, and kiaho.
+* made kiaho a constant fire attack. decreased the range massively. decreased the knockback somewhat. made it bounce all missile attacks except for deathball and kienzan.
+* eyebeam is NOT underbalanced in the slightest in its current form.
+* increased the speed of all beam attacks.
+* made the menu look prettier.
+* added 2 new maps, a giant sound pack from 17, a new console font from gp, a few random things from dash.
+* fixed body transformations.
+* tried out bfp at 640x480 with the options all turned up on a p3 450 running a TNT ONE 16 MEG VIDEO CARD and got 20-30 fps on danorthern against 3 bots.
+* create an installer exe for bfp.
+
+
+
+
+# particles - 02/16/2001 by Yrgol
+
+I added some particle effects to the game. I havn't really figured out what to do with them yet, but they are there. Just as a start I added the generic explosion sparks. As with every other effect, if you don't like it you can turn it off in the menu.
+
+
+
+
+
+
+# Old style update - 02/08/2001 by Yrgol
+
+changes since the thing we sent to the news sites:
+* lowered renzoku cost to 100
+* increased renzoku missile speed to 8000.
+* increased renzoku radius to 120.
+* lowered renzoku firing speed to 200ms.
+* put in a 1 second delay before you can zanzoken out of stun.
+* increased SBC radius to 150.
+* increased SBC damage from 35%*mult to 40%*mult.
+* increased mouthbeam damage from 20% to 30% (effective 60% vs same PL).
+* lowered final flash damage from 40%*mult to 25%*mult.
+* increased the angry gohan range from 500 to 1000.
+* tweaked the angry gohan aura scaling.
+* lowered angry gohan ki cost from 200 to 150.
+* fixed a bug that made other people's charging flashes not show up.
+* fixed the lagometer position
+* made an exception so the death ball can compete with beams in collisions.
+* fixed a bug that made third person extend into spectator free mode.
+* fixed a horrible bug that made players not cycle in 1v1.
+* tweaked some LMS code.
+* fixed a bug that made the blocking anim play a bit too long.
+* fixed the problem with the angry gohan shader.
+* added basic power struggles.
+* redid kiaiho.
+__
+* took away boost speed bonus while _firing_ a beam.
+* lowered kamehameha damage from 40%*mult to 30%*mult.
+* upped final flash damage from 25%*mult to 30%*mult.
+* made deathball work in power struggles.
+* put the angry gohan cost back the way it was.
+* lowered angry gohan range to 800 from 1000.
+* slowed recharge time to 6 seconds.
+__
+* added the new gohan and krillin.
+* added some attack charge sounds.
+* redid the timing on masenko and gave it a HA sound.
+* fixed a bunch of post-death bugs.
+* removed the generic flash sounds for kamehameha and masenko.
+* added a new sprite for final flash.
+__
+* added sprites for the big bang, sokidan, masenko, kamehameha, and renzoku.
+* made oozaru picking more random.
+* fixed the oozaru extendo penis.
+* totally redid flight physics.
+* changed up and down orientation while flying to be relative to the player's axis.
+* added cg_flytilt for tilting the viewpoint when boost flying.
+* fixed a bug with zanzoken going through clip brushes.
+* increased the size of the smoke ring around explosions.
+* added cg_explosionSmoke to turn off the smoke ring.
+* redid jump.
+* added wall jumps.
+* added a short delay between jumps excluding wall jumps. avoiding people looking like bunnies.
+* fixed the "slightly sticky ground" flight bug.
+* added ctf, but it's not selectable from the menu.
+* added more dramatic transformations. they are 5 seconds long and you are invuln during it.
+
+
+
+
+# Random updates - 02/04/2001 by Yrgol
+
+Ansel's been making a lot of sprites to replace some of our attack models. Flight physics was totally redone to include momentum.
+
+CTF mode was added back in, but I don't know if it's going to be a fully supported mode or not. CTF is kinda goofy when you can fly fast, and we don't have any maps for it. CTF should not be confused with capture the dragonballs, which still isn't in phase 1.
+
+
+
+
+# Power Struggles - 01/10/2001 by Yrgol
+
+Power struggles are in phase 1 now. Ki recharge time has been increased to 6 seconds for now, subject to change at any given moment. We had it take about 20-30 seconds a long time ago and it really killed the gameplay. Lots of attacks have gone through some rebalancing.
+
+
+
+# Yrgol’s Important Bid For Power Phase 1 README!!! - 01/10/2001 by Yrgol
+
+(complete with 3 different exclamation points!)
+A wise man once said: “Those who do not read this document will suck at BFP.”
+
+What is Bid For Power?
+Bid For Power (BFP) is a quake 3 total conversion. We have taken quake 3 and used it to create a new game. In order to play BFP, you will need the full retail version of Quake 3 Arena by ID Software, but once you have that, our game is a free downloadable add-on.
+
+In phase 1 of BFP, we place 6 of the fighters from the Freeza saga in an arena in order to fight. Power is gained over time, but the way to get power quickly is to kill other players. Obviously in the show, Krillin would not be able to fight Freeza, but if we followed that rule who would play Krillin? In BFP, the only difference between the characters is the set of attacks available.
+
+Features:
+- 6 different selectable characters, each with 5 attacks. Goku, Piccolo, Vegeta, Freeza, Gohan, and Krillin.
+- 21 different ki attacks including controllable attacks, homing attacks, and chargeable attacks (no guns!).
+- Toggleable flight.
+- offhand melee combat
+- Re-"charge"able ki energy for ammo, flight, and speed/power boost.
+- Power level determines the strength of attacks, speed of movement, max health, max ki, ki charge rate, and what attacks are available.
+- 5 different game modes: FFA, 1v1, Team DM, Last Man Standing Team DM, and Oozaru mode.
+- Numerous new player animations.
+- Plenty of cool effects: head and full body transformations, selectable aura, ki trail, scalable attacks, big explosions, playable third person mode, first person vis mode.
+
+Installation (windows):
+- Install the full retail version of Quake 3 Arena. The demo version will NOT work with BFP.
+- Install the 1.27 point release for Quake 3 Arena, found at http://www.quake3world.com/files/patch127g.html
+- Unzip the bfprc1.zip file into your quake 3 arena directory. Make sure to use folder names when extracting the files.
+- The directory "bfp" will be created as a subdirectory of the quake 3 directory.
+- To start the game, either use the "Mods" tab in quake 3, or start the game with command line args “+set fs_game bfp”. Creating a shortcut to quake 3 with that argument is the recommended way of starting BFP.
+
+Starting BFP:
+There are two ways to start BFP. The easiest way is to use the mods menu in Quake 3. From the main Quake 3 menu, click "mods". Then find "Dragon Ball Z: Bid For Power" on the list and highlight it. If you have a lot of mods installed, you may need to scroll the list in order to make BFP visible. Once you have BFP highlighted, click on the load button and BFP will start.
+
+The other way to start BFP is to use the command line arguments `+set fs_game bfp`. An easy way to do this under Windows is to create a shortcut. Highlight the quake3 icon in whatever directory you installed Quake 3 into, and press ctrl-c to copy the link. Press ctrl-v to create a shortcut to quake3. Right click on the new shortcut and go into the properties menu. Under the `shortcut` tab of the properties menu, change the target to `C:\Quake 3 Arena\quake3.exe +set fs_game bfp` if you installed Quake 3 into C:\Quake 3 Arena. Use this new shortcut to start BFP.
+
+BFP loads its own config file called 'bfp.cfg'. This allows either way of starting BFP to work without any config file problems.
+
+What is Phase 1?
+Phase 1 is the first release of BFP. It covers important characters and places from the Freeza saga of DBZ.
+
+What is Phase 2?
+Phase 2 will be the second major release of BFP. It will be released when it is done, and will cover important characters and places from the Android/Cell saga. Phase 2 will also likely have updated effects and game play. There will more than likely be a few patches between phase 1 and phase 2.
+
+How do I play a multi-player game?
+This game is designed to be played multiplayer. From the main menu, click on the "multiplayer" button. BFP will scan all of the quake 3 arena servers and display any BFP servers that it finds. Once a BFP server is found, highlight it, click accept and the game will connect to the server.
+
+Is there a single-player game?
+Just barely. This game is intended to be played multi-player. A single player game can be played with the bots, but we recommend playing against real people.
+
+Viewpoint
+Viewpoint is how you see the game while you play it. You have your choice of 3 different viewpoints for playing BFP. To switch between the view, use the BFP Options menu.
+
+- Third Person: The model is seen from the outside. Unlike the standard quake 3 third person view, in BFP the crosshair is visible, and the angle is changed to create less blocking of the view. Cvars cg_thirdPersonAngle and cg_thirdPersonRange have had their cheat protection removed, and cg_thirdPersonHeight was added for additional customization.
+
+- First Person Vis: First person where your own model is drawn. Your viewpoint is positioned on the model's eyes, and you can see your own arms and legs while fighting.
+
+- First Person: The standard first person quake 3 view.
+
+Power Level
+
+Power level is how strong you are. Power level determines max health, max ki, movement speed, damage caused by attacks, and which attacks are available to use. The only way to get more power is to kill someone. When you die (in most game modes), your power level is reset to the average of all current power levels. When the game ends or the server restarts, the power level of the players is reset to the default.
+
+Power Tiers
+
+Power level is divided into tiers. While most power level effects are based on the power level itself, aura color and attacks available is determined by the tier.
+
+- Tier 1: < 100,000 PL. Blue aura, only one ki attack available.
+- Tier 2: 100,000 - 250,000 PL. Red aura, two ki attacks selectable.
+- Tier 3: 250,000 - 500,000 PL. Red aura, three ki attacks selectable.
+- Tier 4: 500,000 - 999,000 PL. Red aura, four ki attacks selectable.
+- Ultimate Tier: 1 mil PL. Yellow aura, all attacks selectable.
+
+Transformations
+
+At 1 million power level, the transformation happens. The aura color turns yellow, and if the character has a transformation it occurs. Saiyajin characters turn into Super Saiyajins. Other than the benefits from increased power level, there is no additional benefit of the transformation.
+
+Ki Energy
+
+Ki is your stamina. Ki is used as ammunition for the ki attacks. Additionally, ki can be used to increase power, speed, jumping height, and special attacks by using the boost key. Ki can be charged in order to replenish it at any time by holding down the charge key. Beware, while charging you can not move or attack, and are highly visible to other players.
+
+Flight
+
+BFP has a toggle able flight button. The default is "f" but it can be set in the controls menu. To begin flying, simply press f once, and once again to stop flying. While flying, you are able to move to wherever you look. The jump key will make you move up, and the crouch key will make you move down. Your ki drains at a steady rate. At low power levels you will need to limit your flight, while at high power levels you can fly all day. A large speed boost is gained by holding down the boost key.
+
+Melee Combat
+
+To engage in melee combat, hold down the melee key and put the crosshairs over your opponent. If you are close range, then you will start beating on him. If you are long range, you will dive at the bad guy for a strike. To add extra knock back and stun your opponent, hold down the boost key while melee fighting.
+
+Hit Stun
+
+While in hit stun, the player can not move, attack, block, or charge. 1 second of hit stun is added when the player runs out of ki. 3 seconds of hit stun is added when melee is used in combination with the boost key. Do not overlook this feature. Being able to knock people into hit stun with ki-boosted melee is one of the keys to being good at BFP.
+*The melee stun can be avoided by holding melee while you are attacked.
+
+Zanzoken
+
+Zanzoken is a short, extremely fast movement. In BFP, zanzoken is a controlled short-range teleportation that is performed by double-tapping right or left movement keys. Zanzoken is useful for dodging attacks, and can be used to break out of hit stun after 1 second.
+
+Blocking
+
+Pressing block once grants the player between one and two seconds of blocking. Blocking drains ki quickly, but transfers all damage to ki instead of health. Blocking can also be used to deflect missile attacks. There is a one second delay between when the player stops blocking and when the player can block again.
+
+Ki Attacks
+
+Each character has 5 attacks. At the lowest power tier, the player only has one selectable attack. Each time the player advances to a new power tier, he gets a new selectable attack. By default, each of the numeric keys, 1-5, choose a different attack. Only one attack can be selected at a time, but switching between attacks is instant.
+
+- standard attacks: hold down fire and you fire.
+- charge-up attacks: hold down fire to start charging the attack, release fire to fire.
+- minimum charge-up attacks: same as charge-up attacks, but with a minimum charge-up time. The ball of ki will appear in the player's hands once he is prepared to fire.
+- beam attacks: a type of charge-up attack. Can only fire one beam at a time. After firing, you can aim the attack by moving the mouse, or detonate it by hitting fire again.
+- homing attacks: these attacks will home in on the nearest opponent.
+
+Attack Collisions
+
+Unlike quake 3 attacks, BFP attacks are more than just a trajectory. Attacks have actual dimensions, and will explode on contact with players. If an attack looks 50’ wide, it will hit any players who come within 25’ of the center of the attack. Mid-air attack collisions also happen. Two attacks that collide will damage each other, and the stronger attack will cause the other to explode. The exception is any beam attack, which will destroy any other attack that it makes contact with.
+
+Power Struggles
+
+When two beam attacks collide, a power struggle will happen. Instead of the stronger beam blowing up the weaker beam, the stronger beam will push back the weaker beam until it hits the other player. Ki boost can be used to up the power of your beam if you are losing a power struggle. The Death Ball is an exception among the missile attacks, and can be used in a power struggle.
+
+Krillin
+
+- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
+- Taiyoken: the solar flare. Hold down fire until charged, then release to blind people around you.
+- Sokidan: homing ball. Hold down fire until charged, then release to fire.
+- Kakusan: a missile that spawns 4 homing balls when detonated. Hold down fire until charged, and release to fire the missile. Press fire again to detonate the missile into 4 homing balls.
+- Kienzan: the destructo disk. Hold down fire until charged, and release to fire. This attack goes through walls and does massive damage.
+
+Vegeta
+
+- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
+- Bakuhatsuha: larger ki blast. Press or hold fire to fire a blast.
+- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
+- Big Bang Attack: a beam that has a massive explosion. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate into a massive big bang explosion.
+- Final Flash: a large and powerful beam attack. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
+
+Gohan
+
+- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
+- Sokidan: homing ball. Hold down fire until charged, then release to fire.
+- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
+- Masenko: beam attack. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
+- Energy Dan (Angry Gohan Attack): Gohan creates a massive and continuous explosion around himself. Press and hold fire. After a few seconds the explosion will appear. Gohan can not move while performing this attack.
+
+Piccolo
+
+- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
+- Eye Beam: a continuous blast of power from the eyes. Press and hold fire.
+- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
+- Chobakuretsumaha: a fast and powerful homing attack. Hold down fire until charged, and release to fire.
+- Special Beam Cannon: a fast and narrow but extremely powerful spiraling beam. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
+
+Goku
+
+- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
+- Kiaiho: An invisible blast of energy from the hands with a massive knockback. Press or hold fire to attack.
+- Sokidan: homing ball. Hold down fire until charged, then release to fire.
+- Renzoku Energy Dan: multiple fast-firing ki blasts. Hold down fire to attack.
+- Kamehameha: beam attack. Hold down fire until charged, release to fire. While firing, use the cursor to aim the attack. Press fire again to detonate the attack.
+
+Freeza
+
+- Kikou: standard weak ki blast. Press or hold fire to fire a blast.
+- Shyogeki Ha: fast firing, extremely fast moving ki blasts from the fingers. Hold fire to attack.
+- Freeza Beam: an extremely fast ki attack fired from the finger. Press or hold fire to fire a blast.
+- Tsuibi Kienzan: Freeza’s homing version of the kienzan. Hold down fire until charged, and release to fire. This attack goes through walls.
+- Death Ball: a gigantic ball of ki. Hold down fire until charged, and release to fire. This attack is huge in size and does massive damage.
+
+Game Types:
+
+- Death Match: Players start at ~50,000 PL. Additional PL is gained for each kill. On death, the PL of the player resets to the average of all other PLs.
+- Tourney: Same rules as DM, but with only two players at a time.
+- Team DM: Same rules as DM, but with teams.
+- Team LMS: Last Man Standing. If you’ve ever played clan arena you should know this mode. Players start at 1 mil PL, and players who die can not respawn until the next round.
+- Oozaru: This game works as regular FFA, except one player is chosen to be the ~50’ tall Oozaru. The player who kills the Oozaru gets to be the Oozaru next. The Oozaru has extra power and speed, and is also a giant compared to the other players.
+
+CVARS:
+
+If you don't already know what a cvar is you should probably ignore this section. These can be used from the q3 console to access features. Most of these features can be set from within the menu.
+
+* g_basepl [1-999]: set the starting power level from one thousand to one mil.
+* cg_kitrail [0-99]: set the length of the ki trail. 0 turns it off.
+* cg_crosshairhealth [0/1]: turn on or off the feature that makes the crosshair turn red when you score a hit.
+* cg_lightauras [0/1]: turn on or off the aura dynamic lights.
+* cg_lightexplosions [0/1]: turn on or off the explosion dynamic lights.
+* cg_drawKIWarning [0/1]: turn on or off the low ki warning.
+* cg_chargeupAlert [0/1]: turn on or off the "ready" message when charging attacks.
+* cg_stfu [0/1]: disable character voices when firing attacks.
+* cg_lowpolysphere [0/1]: force the use of a lower polycount sphere.
+* cg_explosionShell [0/1]: turn on or off the explosion shell.
+* cg_drawOwnModel [0/1]: toggle first person between traditional and vis modes.
+
+Help my computer can’t handle BFP!
+
+With all of the graphics options turned on, BFP takes a much beefier system than standard Quake 3. If your computer sucks, don’t despair. We’re looking out for you. BFP is meant to push a p3 500 with 128 megs of memory and a GeForce video card, but it is designed to be able to run playably on a celeron 300 with 128 megs of memory and a TNT2. However, with a much weaker system than that, you are pushing your luck with BFP.
+
+Here are some things you can do to try to boost your performance.
+
+- The first thing to try is turning off dynamic lights in the game options menu. Quake 3 can be slow when it comes to handling dynamic lights, and there are a lot of them in BFP.
+- Go into the BFP options menu and use the ki trail slider bar to lower the length of the ki trail. Few systems can handle the ki trail at its max length without framerate loss. With the slider bar all the way to the left, the ki trail will have 0 length. With the slider bar all the way to the right, the ki trail will almost stretch the length of our maps.
+- Go under the systems setting menu and lower the resolution to 640x480. Don’t gag, most Quake 3 competition players seem to use this resolution.
+- Go into the BFP options menu and set the aura type to “polygonal aura”. This is the least costly aura type. Using the BFP options menu, turn transformation aura off, dynamic aura lights off, dynamic explosion lights off, ki trail off, small own aura on, and ssj perma glow off. With these settings BFP should run about as well as quake 3 does on your computer.
+- If you are really having trouble go into the video set-up menu and change the lighting from lightmap to vertex. Everything will look crappy, but the game will run faster.
+- If your system is running low on memory, use the BFP options menu to turn Force Default Skins on. This is the BFP equivalent of cg_forceModels in Quake 3. Force Default Skins will disable all custom skins for the models. With this option on, there will only be one skin shared between all Vegetas.
+
+Who made this game, and why is it free?
+
+Bid For Power was made by a group of volunteers. We’re all DBZ fans and most of us have watched all or nearly all the Japanese episodes. I can’t speak for all the reasons of the individual team members, but we all thought the idea of a first person DragonBall Z game was cool enough to want to get involved. It’s free because we are not a company, and don’t have the money to pay off people like Id. This game was created by people, for fun, in their spare time. Everyone on the team contributed to the game design and art style.
+
+- Chris James: project lead, coordinator, original concept, PR.
+- Yrgol: programming, game design.
+- Rodney: Goku and Piccolo models, animation, additional model tweaking, 2d art, skins.
+- Pyrofragger: Gohan, Krillin, and SSJ Goku models, animation, DNamek map, aura and attack models.
+- NilreMK: Phase 2 and “secret character” models, animation, attack models, Q3 editing guru.
+- Mooky: Vegeta and Freeza models.
+- Ansel: lead skin artist, 2d art
+- Dethayngel: skins.
+- Ebola: skins.
+- Anthony: 2d art, char select art.
+- Badhead: attack and aura shaders.
+- Number17: sounds.
+- Yngwei: Snake Way map, skyboxes, performed all music used in BFP.
+- GangstaPoodle: GPCity, GPNamek, GPCityDark, and GPCave maps and textures, 2d art.
+- Kit Carson: Earth and NamekXL maps and textures.
+- $onik: Kami’s Lookout map textures.
+- Perfect Chaos: additional map textures.
+- Nat: webmaster, additional programming.
+- Disco Stu: web design
+
+
+
+
+# zanzoken - 12/17/2000 by Yrgol
+
+Zanzoken is in now. I'm doing it as a short range controlled-direction teleport. Double tap right or left in combination with other movement in order to zanzoken. I don't want to get into specifics but yes there are restrictions and costs to prevent this from being used all the time.
+
+
+
+# stuff - 12/14/2000 by Yrgol
+
+As you can tell I'm still making the mod. The bots own now. If all that mattered were code, bfp would be ready to release. Everything that I said was going to be in phase 1 is in there now and then some, and the bugs are minimal. A few of our models still need some tweaking before the whole is releasable.
+
+Funimation did indeed send us a cease and decist letter within days of selling dbz game rights to Infogrames. However, they have decided to take a closer look at the situation. We did not want to jump the gun on being foxed, because things are still fairly up in the air. We did not want the slashdot post to happen.
+
+Bid For Power will be released. It will most likely have Dragon Ball Z characters in it when it is released.
+
+17-3 (not finished):
+* made bots aim beams.
+* made bots charge attacks randomly above the minimum.
+* made bots melee.
+* made bots act blind when hit with the taiyoken.
+* made bots aware of splash damage on charge-up attacks.
+* lowered shyogeki ha damage from 20 to 5.
+* lowered shyogeki ha range from 8192 to 1200.
+* lowered shyogeki ha spread.
+* made homing attacks easier to dodge.
+* lowered kiaiho range from 8192 to 1000.
+* made kiaiho do a stable 20% damage.
+* removed the damage boost for ki boosting.
+* got rid of the character change messages because they never worked right.
+* shrunk the pre-fire deathball a bit so it doesn't clip freeza's head.
+* made blindness not last through death.
+* got rid of the melee axehandle smash direction.
+* fixed a bug in melee that caused a crash.
+* lowered hit-stun time from 3 seconds to 2.
+* capped self-splash at 20%.
+* added in the separate kienzan attack shader.
+* shrunk freeza's kienzan.
+* fixed a bug that made the kienzans stay around far longer than they should.
+* fixed a bug that made kienzans hit way too fast and too often.
+* gave the kienzans a max number of hits, which fixes a memory leak.
+* fixed a bug with the animation timers.
+* fixed a bug with switching weapons while firing a beam.
+* fixed a bug with switching weapons while charging up an attack.
+* made charging only prevent intentional movement, not glue you in place.
+
+17-2:
+* made bots be 100% agressive all the time.
+* made bots fly a better.
+* taught bots how to use charge-up attacks and beams.
+* fixed bot aim.
+* upped the charge time for the chobakuretsumaha and sokidan.
+* scaled down the pre-fire flashes of most attacks.
+* fixed the spelling and grammer in the bot files and fixed the error messages.
+
+17-1:
+* redid third person view.
+* got around the gimble lock for flight so you can go upside down.
+* changed default cg_thirdpersonheight to -50.
+
+
+
+
+# more sutff than you wanted - 11/29/2000 by Yrgol
+
+16-3/17:
+* fixed the timing of the "ready" message with the kamehameha.
+* made a bfp.cfg file so that bfp will work fine by using the mods menu.
+* fixed a bug that caused untextured weapon flashes to appear.
+* fixed a bug that caused attack voices to sometimes come out at the wrong time.
+* added cg_stfu to turn the voices off.
+* fixed a bug that made melee mess up charge-up attacks.
+* fixed a bug that caused a lot of problems in the char select screen.
+* fixed a bug that made melee screw up the weapon time and hit way too fast.
+* fixed a bug that made the charge-o-meter misread the sokidan/chobakuretsumaha power.
+* fixed another bug with the ready message.
+* added cull disable to all the explosion shaders so they don't disappear when you get too close.
+* added an explosion shell/shockwave. can be turned off with cg_explosionShell 0.
+* added cg_lowpolysphere to toggle between a low and a high polycount sphere model.
+* fixed the infamous death-spawn bug.
+* fixed the sizing of the accept button in the video setup menu.
+* seemingly accidentally fixed the problem of ki trails showing through walls.
+* made cg_thirdpersonrange and cg_thirdpersonheight get saved in bfp.cfg.
+* gave cg_thirdpersonheight a default of 20.
+* increased freezabeam explosion radius.
+16-2:
+* added g_basePL with a slider in the server startup to define starting PL.
+* gave the renzoku an alternating firing offset.
+* cut chobakuretsumaha damage in half.
+* upped the ki cost of the chobakuretsumaha.
+* lowered freezabeam damage.
+* made melee hit stun not happen when both people are holding melee.
+* made homing missiles select a target based on angle instead of distance.
+* refined homing angle selection somewhat.
+* fixed the sbc spiral so it disappears less often.
+* fixed jump (detection and animation).
+* made the kienzan stop disappearing when it hits other missiles.
+* added self splash damage back in.
+* made blocking deflect missiles.
+* added in 17's explosion sounds.
+* added in character sounds for sbc, kamehameha, final flash, bb attack, masenko.
+* hopefully fixed the ride me big boy bug.
+
+16-1:
+* made the spiral not disappear when two people sbc at the same time
+* made ki trails a little more robust
+* forced r_railCoreWidth to 15 on startup.
+* removed a bug with the ssjhead getting an aura when it shouldn't.
+* fixed a bug that caused the char select screen to crash the game.
+* resized images so they were powers of 2.
+* got rid of "unknown gametype" when connecting to a oozaru or lms server.
+* homing attacks don't turn all the way around anymore.
+* homing attacks no longer lose their velocity when homing.
+* tweaked the various homing missile speeds to account for the new code.
+* made taiyoken based on FOV, meaning you have to see it to go blind.
+* set the size of menu buttons to 80x80 and made those values
+settable through one file instead of 30 different files.
+* made the eyebeam visible from all perspectives.
+* upped the eyebeam damage.
+* made the eyebeam not draw when holding melee.
+* fixed a rotation problem on the auras.
+* fixed a problem with showing the wrong gametype name when starting a server.
+
+16:
+* decreased melee dive range from 2000 to 700.
+* hopefully fixed the problem of melee dive teleporting you into objects.
+* new menu.
+* new attack icons.
+* new hud.
+* new ki trail.
+* fixed the servers menu to differentiate between bfp gametypes.
+* removed the "impressive" for the freezabeam.
+* lowered the ki cost of the freezabeam from 1000 to 500.
+* made the freezabeam explode when it hits a player instead of going through them.
+* gave the freezabeam explosion some damage.
+* changed the sound played for the freezabeam.
+* fixed a bug in the eyebeam that made the beam not show up.
+* changed the name of the sbc to makankosappo.
+* removed drowning.
+* made the speed boost depend on PL instead of power tier.
+* made cg_kitrail be a slider var from 0 to 99 for ki trail length.
+
+15-8:
+* removed heals from spawning on maps.
+* increased visual explosion size.
+* made charging a lot more effective.
+* cut damage in half.
+* made melee dive a lot more solid.
+* gave the melee hit sound an origin.
+
+15-7:
+* made the chargeup alert display in third person
+* moved the chargeup alert to the bottom of the screen.
+* plays the pain sounds based on percent of max health.
+* upped masenko damage.
+* made the hud player counter not include dead people.
+* removed gibs for killing someone. you can still gib a corpse.
+* made the prepare anim play for kakusan.
+* fixed the timing on at least some charge up attack anims.
+* added a flash model to firing beams.
+* added KamehamehaFlashShader etc for the beam attacks.
+this shader needs to be opaque enough to hide the beam origin
+* scaled down the pre-fire ball for beam attacks.
+* removed the standard flash entirely.
+
+15-6:
+* added a "ready to fire" message for charge-up attacks.
+* added cg_chargeupAlert to turn off that message.
+* fixed a bug that would let attacks get kienzan properties.
+* upped kakusan damage some.
+* lowered taiyoken damage to 1%.
+* added a visibility check on taiyoken. only checks barriers, not direction.
+* increased taiyoken chargeup time.
+* shortened blindness time some.
+* made the melee_axehandle animation run, but it's not very noticable.
+* toned down the angry gohan.
+* made the prepare anim play for the angry gohan.
+* changed the scoreboard name of the angry gohan to the Angry Gohan Attack.
+* fixed "the incredible eternal kienzan" bug.
+
+15-5:
+* first person vis mode works. it works best with models that have tag_eyes (gohan/krillin).
+* angry gohan no longer draws while meleeing or charging.
+* took out the announcer sounds for player names.
+* blindness no longer stays with you through death.
+* took out the ring model for explosions on players.
+* fixed a scaling problem in the explosion ring that made it go black.
+* removed the eject brass option from the game options menu.
+* individual score now survives death in LMS mode.
+* following in LMS spectator mode no longer fucks the next round.
+* got rid of some red in the menu.
+* gave explosions and missiles dynamic lights.
+* added cg_lightExplosions to turn off dynamic explosion lights.
+* removed the chargeup anim for taiyoken.
+
+15-4: lost all code since 15, had to redo stuff.
+--SHADERS--
+* added shaders for kamehameha and masenko.
+* added separate beam shaders.
+* added separate shaders for explosions.
+* added a shader for the freezabeam explosion
+--WEAPONS--
+* freeza's second attack no longer ejects brass.
+* fixed manual beam detonation.
+* missiles should collide with each other a bit better.
+* fixed homing attack collisions.
+* increased eye beam range.
+* removed self-damage.
+* gave deathball a min charge up time (it was supposed to have one).
+* gave masenko a min charge up time (it was supposed to have one).
+* decreased the chargeup speed of sokidan/chobakuretsumaha.
+* made all weapons explode when they hit a player.
+* put the BIG BANG in the BIG BANG attack
+* pumped up the deathball.
+* can't continue firing a beam while charging, blocking,
+melee fighting, or in hit-stun.
+* beam attacks use the sphere as the pre-fire flash.
+* beams now trump missiles for mid-air collisions.
+* lowered renzoku damage.
+* made taiyoken work.
+* gave the SBC a spiral trail.
+* weapon firing sound plays while firing beams instead of
+while charging them.
+* made the freeza beam red and gave it an explosion.
+* gave explosions a smoke ring.
+--OTHER--
+* custom skins now work fine. if you use a custom
+vegeta skin that I don't have, instead of showing
+up as sarge you will show up as vegeta/default on
+my screen.
+* somewhat improved bots.
+* cg_forceModel removed.
+* added cg_forceSkin to force the default skin for each model.
+* changed obituary messages.
+* beam attacks no longer blind you in first person view.
+* switched from the nuke model to the sphere model for explosions.
+* tweaked explosions a lot.
+* picking up a normal health after a mega health won't set
+you back to 100% health anymore.
+* no longer plays the melee sound for missiles.
+* unfucked spectator mode.
+* switching chars while in LMS mode causes a suicide like in
+any other game mode.
diff --git a/source/cgame/cg_consolecmds.c b/source/cgame/cg_consolecmds.c
index fa6328a..a7f04e4 100644
--- a/source/cgame/cg_consolecmds.c
+++ b/source/cgame/cg_consolecmds.c
@@ -173,23 +173,44 @@ CG_StartOrbit_f
*/
static void CG_StartOrbit_f( void ) {
+ // BFP - No developer cvar handling
+#if 0
char var[MAX_TOKEN_CHARS];
trap_Cvar_VariableStringBuffer( "developer", var, sizeof( var ) );
if ( !atoi(var) ) {
return;
}
+#endif
if (cg_cameraOrbit.value != 0) {
trap_Cvar_Set ("cg_cameraOrbit", "0");
- // trap_Cvar_Set("cg_thirdPerson", "0"); // BFP - don't disable even when restarting the server
+ trap_Cvar_Set ("cg_thirdPerson", "0");
} else {
trap_Cvar_Set("cg_cameraOrbit", "5");
+ // trap_Cvar_Set("cg_cameraOrbitDelay", "50");
trap_Cvar_Set("cg_thirdPerson", "1");
trap_Cvar_Set("cg_thirdPersonAngle", "0");
- trap_Cvar_Set("cg_thirdPersonRange", "110");
+ trap_Cvar_Set("cg_thirdPersonRange", "100");
}
}
+
+/*
+==================
+CG_TransformOrbit_f
+==================
+*/
+static void CG_TransformOrbit_f( void ) { // BFP - Transformation orbit
+ // just force enabling camera orbit with these values
+ if ( cg_cameraOrbit.integer != 5 ) { // c'mon, force it! (>_<)
+ trap_Cvar_Set("cg_cameraOrbit", "5");
+ cg_cameraOrbit.integer = 5;
+ }
+ trap_Cvar_Set("cg_cameraOrbitDelay", "50");
+ trap_Cvar_Set("cg_thirdPerson", "1");
+ trap_Cvar_Set("cg_thirdPersonAngle", "0");
+}
+
/*
static void CG_Camera_f( void ) {
char name[1024];
@@ -232,8 +253,9 @@ static consoleCommand_t commands[] = {
{ "vtell_attacker", CG_VoiceTellAttacker_f },
{ "tcmd", CG_TargetCommand_f },
{ "startOrbit", CG_StartOrbit_f },
+ { "transformorbit", CG_TransformOrbit_f }, // BFP - Transformation orbit
//{ "camera", CG_Camera_f },
- { "loaddeferred", CG_LoadDeferredPlayers }
+ { "loaddefered", CG_LoadDeferredPlayers }
};
@@ -307,5 +329,5 @@ void CG_InitConsoleCommands( void ) {
trap_AddCommand ("teamvote");
trap_AddCommand ("stats");
trap_AddCommand ("teamtask");
- trap_AddCommand ("loaddefered"); // spelled wrong, but not changing for demo
+ trap_AddCommand ("loaddefered");
}
diff --git a/source/cgame/cg_cvar.h b/source/cgame/cg_cvar.h
index 84c902b..6c89c62 100644
--- a/source/cgame/cg_cvar.h
+++ b/source/cgame/cg_cvar.h
@@ -31,14 +31,19 @@ CG_CVAR( cg_drawAttacker, "cg_drawAttacker", "1", CVAR_ARCHIVE )
CG_CVAR( cg_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE )
CG_CVAR( cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE )
CG_CVAR( cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE )
+CG_CVAR( cg_simpleHUD, "cg_simpleHUD", "0", CVAR_ARCHIVE ) // BFP - Simple HUD
+CG_CVAR( cg_chargeupAlert, "cg_chargeupAlert", "1", CVAR_ARCHIVE ) // BFP - Ready message when charging attacks
CG_CVAR( cg_crosshairColor, "cg_crosshairColor", "7", CVAR_ARCHIVE ) // BFP - Crosshair color
CG_CVAR( cg_crosshairSize, "cg_crosshairSize", "24", CVAR_ARCHIVE )
CG_CVAR( cg_crosshairHealth, "cg_crosshairHealth", "1", CVAR_ARCHIVE )
CG_CVAR( cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE )
CG_CVAR( cg_crosshairY, "cg_crosshairY", "0", CVAR_ARCHIVE )
+CG_CVAR( cg_lifedeaths, "cg_lifedeaths", "0", CVAR_ROM | CVAR_ARCHIVE ) // BFP - Number of deaths
+CG_CVAR( cg_lifekills, "cg_lifekills", "0", CVAR_ROM | CVAR_ARCHIVE ) // BFP - Number of kills
CG_CVAR( cg_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE )
CG_CVAR( cg_simpleItems, "cg_simpleItems", "0", CVAR_ARCHIVE )
CG_CVAR( cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE )
+CG_CVAR( cg_particles, "cg_particles", "1", CVAR_ARCHIVE ) // BFP - Particles
CG_CVAR( cg_lagometer, "cg_lagometer", "1", CVAR_ARCHIVE )
CG_CVAR( cg_railTrailTime, "cg_railTrailTime", "400", CVAR_ARCHIVE )
CG_CVAR( cg_gun_frame, "cg_gun_frame", "", CVAR_ROM )
@@ -64,7 +69,17 @@ CG_CVAR( cg_footsteps, "cg_footsteps", "1", CVAR_CHEAT )
CG_CVAR( cg_tracerChance, "cg_tracerchance", "0.4", CVAR_CHEAT )
CG_CVAR( cg_tracerWidth, "cg_tracerwidth", "1", CVAR_CHEAT )
CG_CVAR( cg_tracerLength, "cg_tracerlength", "100", CVAR_CHEAT )
+CG_CVAR( cg_lowpolysphere, "cg_lowpolysphere", "0", CVAR_ARCHIVE ) // BFP - Low poly sphere
+CG_CVAR( cg_bigExplosions, "cg_bigExplosions", "1", CVAR_ARCHIVE ) // BFP - Big explosions
+CG_CVAR( cg_explosionRing, "cg_explosionRing", "1", CVAR_ARCHIVE ) // BFP - Explosion ring
+CG_CVAR( cg_explosionShell, "cg_explosionShell", "1", CVAR_ARCHIVE ) // BFP - Explosion shell
+CG_CVAR( cg_explosionSmoke, "cg_explosionSmoke", "1", CVAR_ARCHIVE ) // BFP - Explosion smoke
+CG_CVAR( cg_lightExplosions, "cg_lightExplosions", "1", CVAR_ARCHIVE ) // BFP - Dynamic explosion lights
CG_CVAR( cg_flytilt, "cg_flytilt", "1", CVAR_ARCHIVE ) // BFP - Fly tilt
+CG_CVAR( cg_kiTrail, "cg_kiTrail", "50", CVAR_ARCHIVE ) // BFP - Ki trail length
+CG_CVAR( cg_beamTrail, "cg_beamTrail", "50", CVAR_ARCHIVE ) // BFP - Beam trail length
+CG_CVAR( cg_playHitSound, "cg_playHitSound", "0", CVAR_ARCHIVE ) // BFP - Play hit sound
+CG_CVAR( cg_stfu, "cg_stfu", "0", CVAR_ARCHIVE ) // BFP - Shut the f*** up!
CG_CVAR( cg_thirdPersonRange, "cg_thirdPersonRange", "110", CVAR_ARCHIVE ) // BFP
CG_CVAR( cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_ARCHIVE ) // BFP
CG_CVAR( cg_thirdPersonHeight, "cg_thirdPersonHeight", "-60", CVAR_ARCHIVE ) // BFP - Camera height
@@ -72,13 +87,25 @@ CG_CVAR( cg_fixedThirdPerson, "cg_fixedThirdPerson", "1", CVAR_ARCHIVE ) // BFP
CG_CVAR( cg_drawOwnModel, "cg_drawOwnModel", "1", CVAR_ARCHIVE ) // BFP - toggle first person between traditional and vis modes
CG_CVAR( cg_drawKiWarning, "cg_drawKiWarning", "1", CVAR_ARCHIVE ) // BFP - Ki warning
CG_CVAR( cg_stableCrosshair, "cg_stableCrosshair", "0", CVAR_ARCHIVE ) // BFP - Accurate crosshair
+CG_CVAR( cg_spriteAura, "cg_spriteAura", "0", CVAR_ARCHIVE ) // BFP - Sprite aura
+CG_CVAR( cg_particleAura, "cg_particleAura", "0", CVAR_ARCHIVE ) // BFP - Particle aura
+CG_CVAR( cg_lightAuras, "cg_lightAuras", "1", CVAR_ARCHIVE ) // BFP - Light auras
+CG_CVAR( cg_smallOwnAura, "cg_smallOwnAura", "0", CVAR_ARCHIVE ) // BFP - Small own aura
+CG_CVAR( cg_lightweightAuras, "cg_lightweightAuras", "0", CVAR_ARCHIVE ) // BFP - Lightweight auras
+CG_CVAR( cg_polygonAura, "cg_polygonAura", "1", CVAR_ARCHIVE ) // BFP - Polygonal aura
+CG_CVAR( cg_highPolyAura, "cg_highPolyAura", "1", CVAR_ARCHIVE ) // BFP - High polycount aura
+CG_CVAR( cg_permaglowUltimate, "cg_permaglowUltimate", "1", CVAR_ARCHIVE ) // BFP - Ultimate perma-glow
+CG_CVAR( cg_transformationAura, "cg_transformationAura", "1", CVAR_ARCHIVE ) // BFP - Transformation aura
CG_CVAR( cg_thirdPerson, "cg_thirdPerson", "1", 0 ) // BFP
+CG_CVAR( cg_superdeformed, "cg_superdeformed", "0", CVAR_ARCHIVE ) // BFP - Super Deformed (Chibi style) easter egg
CG_CVAR( cg_yrgolroxor, "cg_yrgolroxor", "0", 0 ) // BFP - Yrgol Roxor easter egg
+CG_CVAR( cg_3dparticles, "cg_3dparticles", "0", CVAR_ARCHIVE ) // BFP - 3D particles
CG_CVAR( cg_teamChatTime, "cg_teamChatTime", "3000", CVAR_ARCHIVE )
CG_CVAR( cg_teamChatHeight, "cg_teamChatHeight", "0", CVAR_ARCHIVE )
-CG_CVAR( cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE )
+CG_CVAR( cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE ) // BFP - TODO: In the future, remove cg_forceModel, which wasn't removed originally?
+CG_CVAR( cg_forceSkin, "cg_forceSkin", "0", CVAR_ARCHIVE ) // BFP - Forces the default skin for each model, applicable when changing models/skins
CG_CVAR( cg_predictItems, "cg_predictItems", "1", CVAR_ARCHIVE )
-CG_CVAR( cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE )
+CG_CVAR( cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE ) // BFP - TODO: In the future, remove cg_deferPlayers, which wasn't removed originally?
CG_CVAR( cg_drawTeamOverlay, "cg_drawTeamOverlay", "0", CVAR_ARCHIVE )
CG_CVAR( cg_teamOverlayUserinfo, "teamoverlay", "0", CVAR_ROM | CVAR_USERINFO )
CG_CVAR( cg_stats, "cg_stats", "0", 0 )
@@ -108,10 +135,15 @@ CG_CVAR( cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE )
CG_CVAR( cg_noProjectileTrail, "cg_noProjectileTrail", "0", CVAR_ARCHIVE )
CG_CVAR( cg_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE )
CG_CVAR( cg_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE )
+
+CG_CVAR( cg_oldRocket, "cg_oldRocket", "0", CVAR_ARCHIVE )
+/*
+// BFP - Remove these unused cvars from the code in the future
CG_CVAR( cg_oldRail, "cg_oldRail", "1", CVAR_ARCHIVE )
-CG_CVAR( cg_oldRocket, "cg_oldRocket", "1", CVAR_ARCHIVE )
CG_CVAR( cg_oldPlasma, "cg_oldPlasma", "1", CVAR_ARCHIVE )
CG_CVAR( cg_trueLightning, "cg_trueLightning", "0.0", CVAR_ARCHIVE )
+*/
+
//CG_CVAR( cg_pmove_fixed, "cg_pmove_fixed", "0", CVAR_USERINFO | CVAR_ARCHIVE )
#undef CG_CVAR
diff --git a/source/cgame/cg_draw.c b/source/cgame/cg_draw.c
index 7774b23..58ffdcb 100644
--- a/source/cgame/cg_draw.c
+++ b/source/cgame/cg_draw.c
@@ -155,11 +155,58 @@ void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t head
float len;
vec3_t origin;
vec3_t mins, maxs;
+ // BFP - Powerlevel
+ int powerlevel = -1;
+ // BFP - Ultimate tier head model and skin
+ qhandle_t tierHeadModel, tierHeadSkin;
+ // BFP - Monster gamemode, check to show monster head model and icon
+ qboolean isMonster;
ci = &cgs.clientinfo[ clientNum ];
+ // BFP - Powerlevel
+ powerlevel = cg_entities[clientNum].currentState.frame;
+ if ( clientNum == cg.snap->ps.clientNum ) { // fixes a weird bug when trying to see the powerlevel of itself
+ powerlevel = cg.snap->ps.persistant[PERS_POWERLEVEL];
+ }
+
+ // BFP - Monster gamemode, check to show monster head model and icon
+ isMonster = ( cgs.gametype == GT_MONSTER && cgs.monster > 0
+ && ( cg_entities[clientNum].currentState.eFlags & EF_MONSTER ) );
+
+ // BFP - Ultimate tier head model and skin
+ if ( isMonster ) {
+ tierHeadModel = cgs.media.monsterHeadModel;
+ tierHeadSkin = cgs.media.monsterHeadSkin;
+ if ( powerlevel >= 1000 && cgs.media.monsterUltTierHeadModel ) {
+ tierHeadModel = cgs.media.monsterUltTierHeadModel;
+ tierHeadSkin = cgs.media.monsterUltTierHeadSkin;
+ }
+ } else {
+ tierHeadModel = ci->headModel;
+ tierHeadSkin = ci->headSkin;
+ if ( powerlevel >= 1000 ) {
+ if ( ci->ultTierHeadModel ) {
+ tierHeadModel = ci->ultTierHeadModel;
+ }
+ if ( ci->ultTierHeadSkin ) {
+ tierHeadSkin = ci->ultTierHeadSkin;
+ }
+ }
+ }
+
if ( cg_draw3dIcons.integer ) {
cm = ci->headModel;
+ // BFP - Ultimate tier head model
+ if ( isMonster ) {
+ cm = cgs.media.monsterHeadModel;
+ if ( powerlevel >= 1000 && cgs.media.monsterUltTierHeadModel )
+ cm = cgs.media.monsterUltTierHeadModel;
+ } else {
+ cm = ci->headModel;
+ if ( powerlevel >= 1000 && ci->ultTierHeadModel )
+ cm = ci->ultTierHeadModel;
+ }
if ( !cm ) {
return;
}
@@ -178,9 +225,14 @@ void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t head
// allow per-model tweaking
VectorAdd( origin, ci->headOffset, origin );
- CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles );
+ // BFP - Ultimate tier head model and skin draw
+ CG_Draw3DModel( x, y, w, h, tierHeadModel, tierHeadSkin, origin, headAngles );
} else if ( cg_drawIcons.integer ) {
- CG_DrawPic( x, y, w, h, ci->modelIcon );
+ qhandle_t icon = ci->modelIcon;
+ if ( isMonster && cgs.media.monsterModelIcon ) {
+ icon = cgs.media.monsterModelIcon;
+ }
+ CG_DrawPic( x, y, w, h, icon );
}
// if they are deferred, draw a cross out
@@ -226,8 +278,6 @@ void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean fo
handle = cgs.media.redFlagModel;
} else if( team == TEAM_BLUE ) {
handle = cgs.media.blueFlagModel;
- } else if( team == TEAM_FREE ) {
- handle = cgs.media.neutralFlagModel;
} else {
return;
}
@@ -239,8 +289,6 @@ void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean fo
item = BG_FindItemForPowerup( PW_REDFLAG );
} else if( team == TEAM_BLUE ) {
item = BG_FindItemForPowerup( PW_BLUEFLAG );
- } else if( team == TEAM_FREE ) {
- item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
} else {
return;
}
@@ -303,7 +351,8 @@ static void CG_DrawStatusBarHead( float x ) {
angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac;
angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac;
- CG_DrawHead( x, 480 - size, size, size,
+ // BFP - Place the head HUD at the corner leaving a bit of margin
+ CG_DrawHead( x + 7, 472 - size, size, size,
cg.snap->ps.clientNum, angles );
}
@@ -314,7 +363,7 @@ CG_DrawStatusBarFlag
================
*/
static void CG_DrawStatusBarFlag( float x, int team ) {
- CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team, qfalse );
+ CG_DrawFlagModel( x + 147, 480 - ICON_SIZE - 63, ICON_SIZE, ICON_SIZE, team, qfalse ); // BFP - Before Q3: x: x, y: 480 - ICON_SIZE
}
/*
@@ -344,6 +393,112 @@ void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team )
trap_R_SetColor( NULL );
}
+// BFP - Gauge bar colors
+vec4_t backgroundBlue = {0, 0, 1, 0.33};
+vec4_t barRed = {1, 0, 0, 0.66};
+vec4_t barGreen = {0, 1, 0, 0.66};
+vec4_t barBlue = {0, 0, 1, 0.66};
+vec4_t barYellow = {1, 1, 0, 0.66};
+
+/*
+================
+CG_DrawHealthGauge
+================
+*/
+static void CG_DrawHealthGauge( float x, float y, float w, float h, int value, int maxValue ) { // BFP - Health gauge
+ if ( cg_simpleHUD.integer < 1 ) { // BFP - Simple HUD
+ float percentage = (float)value / (float)maxValue;
+ float barWidth = 2 * w * percentage;
+ float *barColor = barGreen;
+
+ if ( (float)value <= (float)maxValue * 0.64 ) { // <= 64% of health
+ barColor = barBlue;
+ }
+ if ( (float)value <= (float)maxValue * 0.31 ) { // <= 31% of health
+ barColor = barRed;
+ }
+ CG_FillRect( x - w, y, w * 2, h, backgroundBlue );
+ trap_R_SetColor( barColor );
+ CG_DrawPic( x - w, y, barWidth, h, cgs.media.whiteShader );
+ }
+}
+
+/*
+================
+CG_DrawKiGauge
+================
+*/
+static void CG_DrawKiGauge( float x, float y, float w, float h, int value, int maxValue ) { // BFP - Ki gauge
+ if ( cg_simpleHUD.integer < 1 ) { // BFP - Simple HUD
+ float percentage = (float)value / (float)maxValue;
+ float barWidth = 2 * w * percentage;
+ float *barColor = barGreen;
+
+ if ( maxValue > 9999 ) { // make the bar color notice at that small difference (don't change the percentage at all)
+ maxValue = 9999;
+ }
+
+ if ( (float)value < (float)maxValue * (2.0f / 3.0f) ) {
+ barColor = barBlue;
+ }
+ if ( (float)value < (float)maxValue * (1.0f / 3.0f) ) {
+ barColor = barRed;
+ }
+ CG_FillRect( x - w, y, w * 2, h, backgroundBlue );
+ trap_R_SetColor( barColor );
+ CG_DrawPic( x - w, y, barWidth, h, cgs.media.whiteShader );
+ }
+}
+
+/*
+================
+CG_DrawPowerlevelGauge
+================
+*/
+static void CG_DrawPowerlevelGauge( float x, float y, float w, float h, int value, int maxValue ) { // BFP - Powerlevel gauge
+ if ( cg_simpleHUD.integer < 1 ) { // BFP - Simple HUD
+ float percentage = (float)value / (float)maxValue;
+ float barWidth = 2 * w * percentage;
+ float *barColor = barYellow;
+
+ if ( (float)value < (float)maxValue * 0.5 ) {
+ barColor = barRed;
+ }
+ if ( (float)value < (float)maxValue * 0.1 ) {
+ barColor = barBlue;
+ }
+ CG_FillRect( x - w, y, w * 2, h, backgroundBlue );
+ trap_R_SetColor( barColor );
+ CG_DrawPic( x - w, y, barWidth, h, cgs.media.whiteShader );
+ }
+}
+
+/*
+==================
+CG_DrawSelectedKiAttack
+==================
+*/
+static void CG_DrawSelectedKiAttack( void ) { // BFP - Show selected ki attack
+ // BFP - TODO: Replace weapons to ki attack configs
+ // cg.weaponSelect frozen at whatever it initialized to, so the icon
+ // never reflected the actual active attack slot, particularly
+ // noticeable when spectating another player using a different slot than expected.
+ // cg.predictedPlayerState.weapon already tracks the correct player automatically
+ CG_DrawPic( 535, 383, 96, 37, cg_weapons[ cg.predictedPlayerState.weapon ].weaponIcon );
+}
+
+/*
+================
+CG_DrawHUDOverlay
+================
+*/
+static void CG_DrawHUDOverlay( void ) { // BFP - HUD overlay
+ if ( cg_simpleHUD.integer < 1 ) { // BFP - Simple HUD
+ CG_DrawPic( 5, 413, 211, 65, cgs.media.hudoverlay );
+ CG_DrawPic( 430, 378, 206, 96.5, cgs.media.hudoverlayr );
+ }
+}
+
/*
================
CG_DrawStatusBar
@@ -351,15 +506,17 @@ CG_DrawStatusBar
================
*/
static void CG_DrawStatusBar( void ) {
- int color;
- centity_t *cent;
playerState_t *ps;
- int value;
+ int value, hvalue;
char *string;
- int spacer = CHAR_WIDTH*2;
vec4_t hcolor;
vec3_t angles;
+ // BFP - Unused variables
+#if 0
+ centity_t *cent;
+ int spacer = CHAR_WIDTH*2;
vec3_t origin;
+ int color;
static float colors[4][4] = {
// { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} };
@@ -367,6 +524,11 @@ static void CG_DrawStatusBar( void ) {
{ 1.0f, 0.2f, 0.2f, 1.0f }, // low health
{ 0.5f, 0.5f, 0.5f, 1.0f }, // weapon firing
{ 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100
+#endif
+
+ // BFP - Gauge sizes
+ const float GAUGE_WIDTH = 57.5;
+ const float GAUGE_HEIGHT = 18;
if ( cg_drawStatus.integer == 0 ) {
return;
@@ -375,7 +537,8 @@ static void CG_DrawStatusBar( void ) {
// draw the team background
CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] );
- cent = &cg_entities[cg.snap->ps.clientNum];
+ // BFP - Unused cent variable
+ // cent = &cg_entities[cg.snap->ps.clientNum];
ps = &cg.snap->ps;
VectorClear( angles );
@@ -392,15 +555,10 @@ static void CG_DrawStatusBar( void ) {
}
#endif
- // BFP - place head in the corner
- CG_DrawStatusBarHead( 0 ); // CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
-
if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) {
CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED );
} else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) {
CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_BLUE );
- } else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) {
- CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_FREE );
}
// BFP - hide armor and disable ammo text
@@ -449,14 +607,38 @@ static void CG_DrawStatusBar( void ) {
}
#endif
- // BFP - TODO: Draw a HUD bar of 6 points to indicate ki attack preparation
- // BFP - TODO: Draw player powerlevel info
-
+ // BFP - Draw player powerlevel info
+ value = ps->persistant[PERS_POWERLEVEL];
+ CG_DrawPowerlevelGauge( 562.5, 452, GAUGE_WIDTH + 10, GAUGE_HEIGHT, value, 1000 );
+ // BFP - NOTE: Just curious, BFP had a 16-bit size issue (only reaches 32767 - the maximum),
+ // they thought that adding ",000" at the last can be entertaining for the player
+ if ( value >= 1000 ) {
+ string = va( "1 Mil!" );
+ CG_DrawBigString( 510, 454, string, 1.0f );
+ } else {
+ int x = 510;
+ string = va( "%d", value );
+ if ( value < 10 ) {
+ x = 526;
+ }
+ CG_DrawBigString( x, 454, string, 1.0f );
+ string = va( ",000" );
+ x = 552;
+ if ( value < 100 ) {
+ x = 538;
+ }
+ CG_DrawBigString( x, 454, string, 1.0f );
+ }
//
// health
//
- value = ps->stats[STAT_HEALTH];
+ // BFP - Visualize only 1 - 100 instead 0 - 1000
+ value = ( ps->stats[STAT_HEALTH] * 100 ) / ps->stats[STAT_MAX_HEALTH];
+ hvalue = ( value < 1 ) ? 1 : ( value > 100 ) ? 100 : value;
+ CG_DrawHealthGauge( 154.5, -18 + SCREEN_HEIGHT - ( SMALLCHAR_HEIGHT * 2 ), GAUGE_WIDTH, GAUGE_HEIGHT, hvalue, 100 );
+ // BFP - No drawing HP Q3 field
+#if 0
if ( value > 100 ) {
trap_R_SetColor( colors[3] ); // white
} else if (value > 25) {
@@ -469,10 +651,11 @@ static void CG_DrawStatusBar( void ) {
}
// stretch the health up when taking damage
- // CG_DrawField ( 185, 432, 3, value);
+ CG_DrawField ( 185, 432, 3, value);
+#endif
- string = va( "HP: %d%%", value ); // %% is a percentage sign
- CG_DrawBigString( spacer, SCREEN_HEIGHT - ( SMALLCHAR_HEIGHT * 2 ), string, 1.0f );
+ string = va( "%d%%", hvalue ); // %% is a percentage sign
+ CG_DrawBigString( 107, -18 + SCREEN_HEIGHT - ( SMALLCHAR_HEIGHT * 2 ), string, 1.0f );
CG_ColorForHealth( hcolor );
trap_R_SetColor( hcolor );
@@ -480,17 +663,18 @@ static void CG_DrawStatusBar( void ) {
if ( ps->stats[STAT_KI] <= 0 ) // BFP - If ki is less than 0, adjust to 0
ps->stats[STAT_KI] = 0;
value = ps->stats[STAT_KI];
- string = va( "KI: %d", value );
- CG_DrawBigString( spacer, SCREEN_HEIGHT - SMALLCHAR_HEIGHT, string, 1.0f );
+ CG_DrawKiGauge( 154.5, -12 + SCREEN_HEIGHT - SMALLCHAR_HEIGHT, GAUGE_WIDTH, GAUGE_HEIGHT, value, ps->stats[STAT_MAX_KI] );
+
+ string = va( "%d", value );
+ CG_DrawBigString( 107, -12 + SCREEN_HEIGHT - SMALLCHAR_HEIGHT, string, 1.0f );
+// BFP - disable armor
+#if 0
if (value >= 100) {
trap_R_SetColor( colors[3] ); // white
} else {
trap_R_SetColor( colors[1] ); // red
}
-
-// BFP - disable armor
-#if 0
//
// armor
//
@@ -788,6 +972,7 @@ static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) {
// draw weapon icon
xx += TINYCHAR_WIDTH * 3;
+ // BFP - TODO: Replace weapons to ki attack configs
if ( cg_weapons[ci->curWeapon].weaponIcon ) {
CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
cg_weapons[ci->curWeapon].weaponIcon );
@@ -880,6 +1065,14 @@ static float CG_DrawScores( float y ) {
vec4_t color;
float y1;
gitem_t *item;
+ const int POS_STR_Y = -19; // BFP - Before Q3: 4
+ const int POS_FLAG_X = 6; // BFP - Before Q3: (nothing)
+ const int POS_FLAG_Y = 34; // BFP - Before Q3: 4
+
+ // BFP - Don't show small two score display when spectating
+ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
+ return 0;
+ }
s1 = cgs.scores1;
s2 = cgs.scores2;
@@ -890,7 +1083,7 @@ static float CG_DrawScores( float y ) {
// draw from the right side to left
if ( cgs.gametype >= GT_TEAM ) {
- x = 640;
+ x = 630; // BFP - Before Q3: 640
color[0] = 0.0f;
color[1] = 0.0f;
color[2] = 1.0f;
@@ -898,11 +1091,11 @@ static float CG_DrawScores( float y ) {
s = va( "%2i", s2 );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
- CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
+ CG_FillRect( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
- CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
+ CG_DrawPic( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
}
- CG_DrawBigString( x + 4, y, s, 1.0F);
+ CG_DrawBigString( x + 4, y - POS_STR_Y, s, 1.0F );
if ( cgs.gametype == GT_CTF ) {
// Display flag status
@@ -911,7 +1104,7 @@ static float CG_DrawScores( float y ) {
if (item) {
y1 = y - BIGCHAR_HEIGHT - 8;
if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) {
- CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] );
+ CG_DrawPic( x - POS_FLAG_X, y1-POS_FLAG_Y, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] );
}
}
}
@@ -922,11 +1115,11 @@ static float CG_DrawScores( float y ) {
s = va( "%2i", s1 );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
- CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
+ CG_FillRect( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
- CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
+ CG_DrawPic( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
}
- CG_DrawBigString( x + 4, y, s, 1.0F);
+ CG_DrawBigString( x + 4, y - POS_STR_Y, s, 1.0F);
if ( cgs.gametype == GT_CTF ) {
// Display flag status
@@ -935,7 +1128,7 @@ static float CG_DrawScores( float y ) {
if (item) {
y1 = y - BIGCHAR_HEIGHT - 8;
if( cgs.redflag >= 0 && cgs.redflag <= 2 ) {
- CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] );
+ CG_DrawPic( x - POS_FLAG_X, y1-POS_FLAG_Y, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] );
}
}
}
@@ -949,13 +1142,13 @@ static float CG_DrawScores( float y ) {
s = va( "%2i", v );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
- CG_DrawBigString( x + 4, y, s, 1.0F);
+ CG_DrawBigString( x + 4, y - POS_STR_Y, s, 1.0F);
}
} else {
qboolean spectator;
- x = 640;
+ x = 630; // BFP - Before Q3: 640
score = cg.snap->ps.persistant[PERS_SCORE];
spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR );
@@ -972,16 +1165,16 @@ static float CG_DrawScores( float y ) {
color[1] = 0.0f;
color[2] = 0.0f;
color[3] = 0.33f;
- CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
- CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
+ CG_FillRect( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, color );
+ CG_DrawPic( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
} else {
color[0] = 0.5f;
color[1] = 0.5f;
color[2] = 0.5f;
color[3] = 0.33f;
- CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
+ CG_FillRect( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, color );
}
- CG_DrawBigString( x + 4, y, s, 1.0F);
+ CG_DrawBigString( x + 4, y - POS_STR_Y, s, 1.0F);
}
// first place
@@ -994,23 +1187,23 @@ static float CG_DrawScores( float y ) {
color[1] = 0.0f;
color[2] = 1.0f;
color[3] = 0.33f;
- CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
- CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
+ CG_FillRect( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, color );
+ CG_DrawPic( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
} else {
color[0] = 0.5f;
color[1] = 0.5f;
color[2] = 0.5f;
color[3] = 0.33f;
- CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
+ CG_FillRect( x, y-POS_STR_Y, w, BIGCHAR_HEIGHT+8, color );
}
- CG_DrawBigString( x + 4, y, s, 1.0F);
+ CG_DrawBigString( x + 4, y - POS_STR_Y, s, 1.0F);
}
if ( cgs.fraglimit ) {
s = va( "%2i", cgs.fraglimit );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
- CG_DrawBigString( x + 4, y, s, 1.0F);
+ CG_DrawBigString( x + 4, y - POS_STR_Y, s, 1.0F);
}
}
@@ -1018,6 +1211,8 @@ static float CG_DrawScores( float y ) {
return y1 - 8;
}
+// BFP - Don't draw powerups
+#if 0
/*
================
CG_DrawPowerups
@@ -1116,6 +1311,7 @@ static float CG_DrawPowerups( float y ) {
return y;
}
+#endif
/*
=====================
@@ -1128,12 +1324,23 @@ static void CG_DrawLowerRight( void ) {
y = 480 - ICON_SIZE;
+ // BFP - CG_DrawTeamOverlay and CG_DrawScores are placed to get better visibility with cg_drawTeamOverlay 2
+
+ if ( cg.snap->ps.stats[STAT_HEALTH] > 0 ) { // BFP - Don't draw if the player is dead
+ // before Q3: y = CG_DrawScores( y );
+ CG_DrawScores( y );
+ }
+
+ if ( cg.snap->ps.stats[STAT_HEALTH] > 0 && cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { // BFP - Don't draw if the player is dead or spectating
+ // BFP - HUD overlay
+ CG_DrawHUDOverlay();
+ }
+
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) {
y = CG_DrawTeamOverlay( y, qtrue, qfalse );
}
-
- y = CG_DrawScores( y );
- y = CG_DrawPowerups( y );
+ // BFP - Don't draw powerups
+ // y = CG_DrawPowerups( y );
}
/*
@@ -1166,6 +1373,27 @@ static int CG_DrawPickupItem( int y ) {
return y;
}
+/*
+=====================
+CG_DrawKiAttackChargeUpPoints
+=====================
+*/
+static void CG_DrawKiAttackChargeUpPoints( void ) { // BFP - Ki attack charge up points
+ int x = 105, i = 1;
+
+ if ( cg.predictedPlayerState.generic1 > 0
+ // don't draw unless the player is charging/exploding a ki wave
+ && ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING || cg.predictedPlayerState.weaponstate == WEAPON_ACTIVE ) ) {
+ while ( i <= 6 ) {
+ if ( cg.predictedPlayerState.generic1 >= i ) {
+ CG_DrawPic( x, 412, BIGCHAR_WIDTH - 2, BIGCHAR_HEIGHT - 2, cgs.media.chargeupbuttgreen );
+ }
+ x += 16;
+ ++i;
+ }
+ }
+}
+
/*
=====================
CG_DrawLowerLeft
@@ -1177,11 +1405,23 @@ static void CG_DrawLowerLeft( void ) {
y = 480 - ICON_SIZE;
+ if ( cg.snap->ps.stats[STAT_HEALTH] > 0 && cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { // BFP - Don't draw if the player is dead or spectating
+ // BFP - Selected ki attack
+ CG_DrawSelectedKiAttack();
+
+ // BFP - Place head in the corner
+ CG_DrawStatusBarHead( 0 ); // CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
+
+ // BFP - Ki attack charge up points
+ CG_DrawKiAttackChargeUpPoints();
+ }
+
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) {
y = CG_DrawTeamOverlay( y, qfalse, qfalse );
}
-
+ // BFP - Put the pickup item icon a bit up
+ y = 480 - ICON_SIZE - 32;
y = CG_DrawPickupItem( y );
}
@@ -1356,7 +1596,7 @@ LAGOMETER
===============================================================================
*/
-#define LAG_SAMPLES 128
+#define LAG_SAMPLES 256
typedef struct {
@@ -1459,7 +1699,7 @@ static void CG_DrawLagometer( void ) {
int a, x, y, i;
float v;
float ax, ay, aw, ah, mid, range;
- int color;
+ int color, d;
float vscale;
if ( !cg_lagometer.integer || cgs.localServer ) {
@@ -1471,7 +1711,7 @@ static void CG_DrawLagometer( void ) {
// draw the graph
//
x = 640 - 48;
- y = 480 - 48;
+ y = 480 - 200; // BFP - Put the lagometer at the center/a bit up. Before Q3: 480 - 48
trap_R_SetColor( NULL );
CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader );
@@ -1488,6 +1728,8 @@ static void CG_DrawLagometer( void ) {
vscale = range / MAX_LAGOMETER_RANGE;
+ d = (int)(ax + aw);
+
// draw the frame interpoalte / extrapolate graph
for ( a = 0 ; a < aw ; a++ ) {
i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1);
@@ -1501,7 +1743,7 @@ static void CG_DrawLagometer( void ) {
if ( v > range ) {
v = range;
}
- trap_R_DrawStretchPic ( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
+ trap_R_DrawStretchPic ( d - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
} else if ( v < 0 ) {
if ( color != 2 ) {
color = 2;
@@ -1511,7 +1753,7 @@ static void CG_DrawLagometer( void ) {
if ( v > range ) {
v = range;
}
- trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
+ trap_R_DrawStretchPic( d - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
}
}
@@ -1538,13 +1780,13 @@ static void CG_DrawLagometer( void ) {
if ( v > range ) {
v = range;
}
- trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
+ trap_R_DrawStretchPic( d - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
} else if ( v < 0 ) {
if ( color != 4 ) {
color = 4; // RED for dropped snapshots
trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] );
}
- trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader );
+ trap_R_DrawStretchPic( d - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader );
}
}
@@ -1557,6 +1799,30 @@ static void CG_DrawLagometer( void ) {
CG_DrawDisconnect();
}
+/*
+==================
+CG_DrawNumAlivePlayers
+==================
+*/
+static void CG_DrawNumAlivePlayers( void ) { // BFP - Show number of alive players for the player itself
+ // if the player is the only one playing, the counter won't be shown
+ int i = 0, alivePlayers = 0;
+ while ( i < MAX_CLIENTS ) {
+ if ( cg_entities[i].currentValid && !( cg_entities[i].currentState.eFlags & EF_DEAD ) ) {
+ if ( cgs.gametype >= GT_TEAM
+ && cg.snap->ps.persistant[PERS_TEAM] == cgs.clientinfo[i].team ) {
+ ++alivePlayers;
+ } else {
+ ++alivePlayers;
+ }
+ }
+ ++i;
+ }
+ // don't show number of alive players when spectating and when there's only one client
+ if ( alivePlayers > 0 && cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) {
+ CG_DrawBigString( 608, 255, va( "%d", alivePlayers ), 1.0F );
+ }
+}
/*
@@ -1711,6 +1977,15 @@ static void CG_DrawCrosshair(void) {
#endif
static float lastPositionY = 480.0f; // BFP - Last Y position for traceable crosshair to move smoothly like BFP vanilla does
+ // BFP - Frametime handling
+ static int previous;
+ int t, frameTime;
+
+ // BFP - Handle frametime to avoid being timescaled
+ t = trap_Milliseconds();
+ frameTime = t - previous;
+ previous = t;
+
// BFP - TODO: BFP doesn't use the crosshair as player view,
// e.g. if the camera angle is 90º, the crosshair should look what's in this view,
// not what the player sees
@@ -1753,9 +2028,6 @@ static void CG_DrawCrosshair(void) {
// BFP - Make crosshair size starting from biggest to current
w = LERP( w*2, w, f ); // before: w *= ( 1 + f );
h = LERP( h*2, h, f ); // before: h *= ( 1 + f );
- if ( cg_crosshairHealth.integer && f <= 0.35f ) { // BFP - BFP crosshair health feature
- trap_R_SetColor( colorRed );
- }
}
x = cg_crosshairX.integer;
@@ -1766,6 +2038,9 @@ static void CG_DrawCrosshair(void) {
ca = 0;
}
hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ];
+ if ( cg_crosshairHealth.integer && f <= 0.425f ) { // BFP - BFP crosshair health feature
+ hShader = cgs.media.redCrosshairShader[ ca % NUM_CROSSHAIRS ];
+ }
if ( cg_thirdPerson.integer >= 1 && cg_stableCrosshair.integer <= 0 ) { // BFP - Third person traceable crosshair
#if ESF_STYLE
@@ -1788,12 +2063,12 @@ static void CG_DrawCrosshair(void) {
CG_AdjustFrom640( &x, &y, &w, &h );
// BFP - Make the traceable crosshair move smoothly like BFP vanilla does
- // LERP( , , (float)(cg.frametime / 1000.00f) * );
+ // LERP( , , (float)(frameTime / 1000.00f) * );
#if ESF_STYLE
- x = LERP( lastPositionX, x, (float)(cg.frametime / 1000.00f) * 18.0f );
- y = LERP( lastPositionY, y, (float)(cg.frametime / 1000.00f) * 18.0f );
+ x = LERP( lastPositionX, x, (float)(frameTime / 1000.00f) * 18.0f );
+ y = LERP( lastPositionY, y, (float)(frameTime / 1000.00f) * 18.0f );
#else
- y = LERP( lastPositionY, y, (float)(cg.frametime / 1000.00f) * 12.0f );
+ y = LERP( lastPositionY, y, (float)(frameTime / 1000.00f) * 12.0f );
#endif
trap_R_DrawStretchPic( x - 0.5f * w, // 492.799987
@@ -1840,10 +2115,13 @@ static void CG_ScanForCrosshairEntity( void ) {
return;
}
+ // BFP - No invis powerup
+#if 0
// if the player is invisible, don't show it
if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) ) {
return;
}
+#endif
// update the fade timer
cg.crosshairClientNum = trace.entityNum;
@@ -1858,8 +2136,10 @@ CG_DrawCrosshairNames
*/
static void CG_DrawCrosshairNames( void ) {
float *color;
- char *name;
+ // BFP - Show powerlevel from the other player
+ char *namePowerlevel;
float w;
+ int powerlevel = -1;
if ( !cg_drawCrosshair.integer ) {
return;
@@ -1884,9 +2164,22 @@ static void CG_DrawCrosshairNames( void ) {
return;
}
- name = cgs.clientinfo[ cg.crosshairClientNum ].name;
- w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
- CG_DrawBigString( 320 - w / 2, 170, name, color[3] * 0.5f );
+ // BFP - Draw name and powerlevel from the other player
+ namePowerlevel = cgs.clientinfo[ cg.crosshairClientNum ].name;
+ powerlevel = cg_entities[ cg.crosshairClientNum ].currentState.frame;
+ w = CG_DrawStrlen( namePowerlevel ) * BIGCHAR_WIDTH;
+ CG_DrawBigString( 320 - w / 2, 170, namePowerlevel, color[3] * 0.5f );
+ trap_R_SetColor( NULL );
+
+ // BFP - NOTE: Just curious, BFP had a 16-bit size issue (only reaches 32767 - the maximum),
+ // they thought that adding "000" at the last can be entertaining for the player powerlevel
+ namePowerlevel = va( "%i000", powerlevel );
+ // BFP - If the powerlevel is higher than 1000, set to this limit
+ if ( powerlevel >= 1000 ) {
+ namePowerlevel = "1 Mil";
+ }
+ w = CG_DrawStrlen( namePowerlevel ) * BIGCHAR_WIDTH;
+ CG_DrawBigString( 320 - w / 2, 190, namePowerlevel, color[3] * 0.5f );
trap_R_SetColor( NULL );
}
@@ -1900,11 +2193,12 @@ CG_DrawSpectator
*/
static void CG_DrawSpectator(void) {
CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F);
- if ( cgs.gametype == GT_TOURNAMENT ) {
+ if ( cgs.gametype == GT_TOURNAMENT
+ || cgs.gametype == GT_SURVIVAL ) { // BFP - Survival
CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F);
}
else if ( cgs.gametype >= GT_TEAM ) {
- CG_DrawBigString(320 - 39 * 8, 460, "press ESC and use the JOIN menu to play", 1.0F);
+ CG_DrawBigString(320 - 25 * 8, 460, "use the TEAM menu to play", 1.0F);
}
}
@@ -1971,7 +2265,7 @@ static void CG_DrawTeamVote(void) {
}
-static qboolean CG_DrawScoreboard() {
+static qboolean CG_DrawScoreboard( void ) {
return CG_DrawOldScoreboard();
}
@@ -2029,7 +2323,7 @@ CG_DrawKiWarning
*/
static void CG_DrawKiWarning( void ) {
const char *s;
- int w;
+ int w, y = 64;
if ( cg_drawKiWarning.integer <= 0 ) { // BFP - ki warning (before cg_drawAmmoWarning)
return;
@@ -2055,8 +2349,48 @@ static void CG_DrawKiWarning( void ) {
} else if ( cg.predictedPlayerState.stats[STAT_KI] < 0 ) {
s = "OUT OF KI";
}
+
+ // BFP - Put the center notification a bit lower when spectating and following a player
+ if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
+ y *= 2;
+ }
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
- CG_DrawBigString(320 - w / 2, 64, s, 1.0F);
+ CG_DrawBigString(320 - w / 2, y, s, 1.0F);
+}
+
+/*
+================
+CG_DrawBlindEffect
+
+Draws a white screen overlay that fades out (Blinding Flash effect)
+================
+*/
+static void CG_DrawBlindEffect( void ) { // BFP - Blind
+ const float BLIND_ALPHA_TIME = 17000.0f,
+ BLIND_DURATION = 6000.0f; // original BFP lasts 6 seconds with that opacity
+ float blindAlpha = 0.0f;
+ float elapsed = (float)( cg.time - cg.blindStartTime );
+
+ if ( !cg.blind ) {
+ return;
+ }
+
+ if ( elapsed >= BLIND_ALPHA_TIME ) {
+ cg.blind = qfalse;
+ return;
+ }
+
+ if ( elapsed < BLIND_DURATION ) {
+ blindAlpha = 1.0f - ( elapsed / BLIND_ALPHA_TIME );
+ }
+
+ if ( blindAlpha > 0.0f ) {
+ vec4_t color = { 1.0f, 1.0f, 1.0f, 1.0f };
+ color[3] = blindAlpha;
+ trap_R_SetColor( color );
+ CG_DrawPic( 0, 0, 640, 480, cgs.media.whiteShader );
+ trap_R_SetColor( NULL );
+ }
}
/*
@@ -2066,24 +2400,46 @@ CG_DrawHitStun
*/
static void CG_DrawHitStun( void ) { // BFP - Hit stun bottom centerprint
const char *s;
- int w;
- playerState_t *ps;
- int value;
+ int w, t;
s = ""; // avoid printing when there are no status changes, for dll and shared objects
- if ( cg.predictedPlayerState.stats[STAT_KI] <= 0 ) {
+ // 900 is added to adjust the timer calculated in milliseconds
+ t = ( 900 + cg.predictedPlayerState.stats[STAT_HITSTUN_TIME] ) / 1000;
+ if ( cg.predictedPlayerState.stats[STAT_HITSTUN_TIME] > 0 ) {
s = "Stun";
}
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
- value = 0; // BFP - TODO: hit stun time
- UI_DrawProportionalString( 320 - w / 2, SCREEN_HEIGHT - ( BIGCHAR_HEIGHT * 6 ),
+ UI_DrawProportionalString( 320 - w / 2, SCREEN_HEIGHT - ( BIGCHAR_HEIGHT * 6 ) + 24,
s, UI_SMALLFONT, colorWhite );
- // BFP - TODO: Draw the time (seconds)
- if ( value > 0 ) {
- CG_DrawField ( 320 - w / 2, SCREEN_HEIGHT - ( BIGCHAR_HEIGHT * 4 ), 3, value );
+ if ( cg.predictedPlayerState.stats[STAT_HITSTUN_TIME] > 0 ) {
+ if ( t <= 0 ) {
+ t = 1;
+ }
+ CG_DrawField ( 320 - w - 16, SCREEN_HEIGHT - ( BIGCHAR_HEIGHT * 4 ) + 12, 3, t );
}
}
+/*
+=================
+CG_DrawReadyKiAttack
+=================
+*/
+static void CG_DrawReadyKiAttack( void ) { // BFP - Ready message in the bottom centerprint when charging attacks
+ const char *s;
+ int w;
+
+ s = ""; // avoid printing when there are no status changes, for dll and shared objects
+ if ( cg_chargeupAlert.integer <= 0 ) { // if disabled, don't show it
+ return;
+ }
+ if ( cg.predictedPlayerState.eFlags & EF_READY_KI_ATTACK ) {
+ s = "Ready";
+ }
+ w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
+ UI_DrawProportionalString( 327 - w / 2, SCREEN_HEIGHT - ( BIGCHAR_HEIGHT * 6 ) + 76,
+ s, UI_SMALLFONT, colorWhite );
+}
+
/*
=================
CG_DrawWarmup
@@ -2093,7 +2449,8 @@ static void CG_DrawWarmup( void ) {
int w;
int sec;
int i;
- float scale;
+ // BFP - Unused variable, wth?
+ // float scale;
clientInfo_t *ci1, *ci2;
int cw;
const char *s;
@@ -2111,7 +2468,8 @@ static void CG_DrawWarmup( void ) {
return;
}
- if (cgs.gametype == GT_TOURNAMENT) {
+ if ( cgs.gametype == GT_TOURNAMENT
+ || cgs.gametype == GT_SURVIVAL ) { // BFP - Survival) {
// find the two active players
ci1 = NULL;
ci2 = NULL;
@@ -2178,23 +2536,25 @@ static void CG_DrawWarmup( void ) {
break;
}
}
- scale = 0.45f;
+
+ // BFP - Unused scale variable on this logic, wth?
+ // scale = 0.45f;
switch ( cg.warmupCount ) {
case 0:
cw = 28;
- scale = 0.54f;
+ // scale = 0.54f;
break;
case 1:
cw = 24;
- scale = 0.51f;
+ // scale = 0.51f;
break;
case 2:
cw = 20;
- scale = 0.48f;
+ // scale = 0.48f;
break;
default:
cw = 16;
- scale = 0.45f;
+ // scale = 0.45f;
break;
}
@@ -2223,6 +2583,9 @@ static void CG_Draw2D( void ) {
return;
}
+ // BFP - Draw blind effect (Blinding Flash)
+ CG_DrawBlindEffect();
+
/*
if (cg.cameraMode) {
return;
@@ -2233,11 +2596,13 @@ static void CG_Draw2D( void ) {
CG_DrawCrosshair();
CG_DrawCrosshairNames();
} else {
- // don't draw any status if dead or the scoreboard is being explicitly shown
- if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) {
+ // BFP - Q3 before: don't draw any status if dead or the scoreboard is being explicitly shown
+ if ( cg.snap->ps.stats[STAT_HEALTH] > 0 ) {
CG_DrawStatusBar();
CG_DrawKiWarning(); // BFP - ki warning
CG_DrawHitStun(); // BFP - Hit stun bottom centerprint
+ CG_DrawReadyKiAttack(); // BFP - Ready message in the bottom centerprint when charging attacks
+ CG_DrawNumAlivePlayers(); // BFP - Number of alive players
CG_DrawCrosshair();
CG_DrawCrosshairNames();
CG_DrawWeaponSelect();
@@ -2270,10 +2635,12 @@ static void CG_Draw2D( void ) {
}
}
-
-static void CG_DrawTourneyScoreboard() {
+// BFP - Unused function
+#if 0
+static void CG_DrawTourneyScoreboard( void ) {
CG_DrawOldTourneyScoreboard();
}
+#endif
/*
=====================
diff --git a/source/cgame/cg_drawtools.c b/source/cgame/cg_drawtools.c
index 2ec45ed..f7ff3bc 100644
--- a/source/cgame/cg_drawtools.c
+++ b/source/cgame/cg_drawtools.c
@@ -30,20 +30,16 @@ CG_AdjustFrom640
Adjusted for resolution and screen aspect ratio
================
*/
-void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) {
-#if 0
- // adjust for wide screens
- if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) {
- *x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) );
- }
-#endif
+void CG_AdjustFrom640( float *x, float *y, float *w, float *h )
+{
// scale for screen sizes
- *x *= cgs.screenXScale;
- *y *= cgs.screenYScale;
+ *x = *x * cgs.screenXScale + cgs.screenXBias;
+ *y = *y * cgs.screenYScale + cgs.screenYBias;
*w *= cgs.screenXScale;
*h *= cgs.screenYScale;
}
+
/*
================
CG_FillRect
@@ -60,6 +56,20 @@ void CG_FillRect( float x, float y, float width, float height, const float *colo
trap_R_SetColor( NULL );
}
+
+/*
+================
+CG_FillScreen
+================
+*/
+void CG_FillScreen( const float *color )
+{
+ trap_R_SetColor( color );
+ trap_R_DrawStretchPic( 0, 0, cgs.glconfig.vidWidth, cgs.glconfig.vidHeight, 0, 0, 0, 0, cgs.media.whiteShader );
+ trap_R_SetColor( NULL );
+}
+
+
/*
================
CG_DrawSides
@@ -74,12 +84,15 @@ void CG_DrawSides(float x, float y, float w, float h, float size) {
trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
}
+
void CG_DrawTopBottom(float x, float y, float w, float h, float size) {
CG_AdjustFrom640( &x, &y, &w, &h );
size *= cgs.screenYScale;
trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
}
+
+
/*
================
UI_DrawRect
@@ -90,14 +103,13 @@ Coordinates are 640*480 virtual values
void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) {
trap_R_SetColor( color );
- CG_DrawTopBottom(x, y, width, height, size);
- CG_DrawSides(x, y, width, height, size);
+ CG_DrawTopBottom(x, y, width, height, size);
+ CG_DrawSides(x, y, width, height, size);
trap_R_SetColor( NULL );
}
-
/*
================
CG_DrawPic
@@ -111,7 +123,6 @@ void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader
}
-
/*
===============
CG_DrawChar
@@ -119,7 +130,7 @@ CG_DrawChar
Coordinates and size in 640*480 virtual screen size
===============
*/
-void CG_DrawChar( int x, int y, int width, int height, int ch ) {
+static void CG_DrawChar( int x, int y, int width, int height, int ch ) {
int row, col;
float frow, fcol;
float size;
@@ -238,6 +249,520 @@ void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) {
CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 );
}
+// new font renderer
+
+#ifdef USE_NEW_FONT_RENDERER
+
+#define MAX_FONT_SHADERS 4
+
+typedef struct {
+ float tc_prop[4];
+ float tc_mono[4];
+ float space1;
+ float space2;
+ float width;
+} font_metric_t;
+
+typedef struct {
+ font_metric_t metrics[256];
+ qhandle_t shader[ MAX_FONT_SHADERS ];
+ int shaderThreshold[ MAX_FONT_SHADERS ];
+ int shaderCount;
+} font_t;
+
+static font_t bigchars;
+static font_t numbers;
+static const font_t *font = &bigchars;
+static const font_metric_t *metrics = &bigchars.metrics[0];
+
+
+void CG_SelectFont( int index )
+{
+ if ( index == 0 )
+ font = &bigchars;
+ else
+ font = &numbers;
+
+ metrics = &font->metrics[0];
+}
+
+
+static qboolean CG_FileExist( const char *file )
+{
+ fileHandle_t f;
+
+ if ( !file || !file[0] )
+ return qfalse;
+
+ trap_FS_FOpenFile( file, &f, FS_READ );
+ if ( f == FS_INVALID_HANDLE )
+ return qfalse;
+ else {
+ trap_FS_FCloseFile( f );
+ return qtrue;
+ }
+}
+
+
+static void CG_LoadFont( font_t *fnt, const char *fontName )
+{
+ char buf[ 8000 ];
+ fileHandle_t f;
+ char *token, *text;
+ float width, height, r_width, r_height;
+ float char_width;
+ float char_height;
+ char shaderName[ MAX_FONT_SHADERS ][ MAX_QPATH ], tmpName[ MAX_QPATH ];
+ int shaderCount;
+ int shaderThreshold[ MAX_FONT_SHADERS ];
+ font_metric_t *fm;
+ int i, tmp, len, chars;
+ float w1, w2;
+ float s1, s2;
+ float x0, y0;
+ qboolean swapped;
+
+ memset( fnt, 0, sizeof( *fnt ) );
+
+ len = trap_FS_FOpenFile( fontName, &f, FS_READ );
+ if ( f == FS_INVALID_HANDLE ) {
+ CG_Printf( S_COLOR_YELLOW "CG_LoadFont: error opening %s\n", fontName );
+ return;
+ }
+
+ if ( len >= sizeof( buf ) ) {
+ CG_Printf( S_COLOR_YELLOW "CG_LoadFont: font file is too long: %i\n", len );
+ len = sizeof( buf )-1;
+ }
+
+ trap_FS_Read( buf, len, f );
+ trap_FS_FCloseFile( f );
+ buf[ len ] = '\0';
+
+ shaderCount = 0;
+
+ text = buf; // initialize parser
+ COM_BeginParseSession( fontName );
+
+ while ( 1 )
+ {
+ token = COM_ParseExt( &text, qtrue );
+ if ( token[0] == '\0' ) {
+ Com_Printf( S_COLOR_RED "CG_LoadFont: parse error.\n" );
+ return;
+ }
+
+ // font image
+ if ( strcmp( token, "img" ) == 0 ) {
+ if ( shaderCount >= MAX_FONT_SHADERS ) {
+ Com_Printf( "CG_LoadFont: too many font images, ignoring.\n" );
+ SkipRestOfLine( &text );
+ continue;
+ }
+ token = COM_ParseExt( &text, qfalse );
+ if ( !CG_FileExist( token ) ) {
+ Com_Printf( "CG_LoadFont: font image '%s' doesn't exist.\n", token );
+ return;
+ }
+ // save shader name
+ Q_strncpyz( shaderName[ shaderCount ], token, sizeof( shaderName[ shaderCount ] ) );
+ // get threshold
+ token = COM_ParseExt( &text, qfalse );
+ shaderThreshold[ shaderCount ] = atoi( token );
+
+ //Com_Printf( S_COLOR_CYAN "img: %s, threshold: %i\n", shaderName[ shaderCount ], shaderThreshold[ shaderCount ] );
+ shaderCount++;
+
+ SkipRestOfLine( &text );
+ continue;
+ }
+
+ // font parameters
+ if ( strcmp( token, "fnt" ) == 0 ) {
+ token = COM_ParseExt( &text, qfalse );
+ if ( token[0] == '\0' || (width = atof( token )) <= 0.0 ) {
+ Com_Printf( "CG_LoadFont: error reading image width.\n" );
+ return;
+ }
+ r_width = 1.0 / width;
+
+ token = COM_ParseExt( &text, qfalse );
+ if ( token[0] == '\0' || (height = atof( token )) <= 0.0 ) {
+ Com_Printf( "CG_LoadFont: error reading image height.\n" );
+ return;
+ }
+ r_height = 1.0 / height;
+
+ token = COM_ParseExt( &text, qfalse );
+ if ( token[0] == '\0' ) {
+ Com_Printf( "CG_LoadFont: error reading char widht.\n" );
+ return;
+ }
+ char_width = atof( token );
+
+ token = COM_ParseExt( &text, qfalse );
+ if ( token[0] == '\0' ) {
+ Com_Printf( "CG_LoadFont: error reading char height.\n" );
+ return;
+ }
+ char_height = atof( token );
+
+ break; // parse char metrics
+ }
+ }
+
+ if ( shaderCount == 0 ) {
+ Com_Printf( "CG_LoadFont: no font images specified in %s.\n", fontName );
+ return;
+ }
+
+ fm = fnt->metrics;
+
+ chars = 0;
+ for ( ;; ) {
+ // char index
+ token = COM_ParseExt( &text, qtrue );
+ if ( !token[0] )
+ break;
+
+ if ( token[0] == '\'' && token[1] && token[2] == '\'' ) // char code in form 'X'
+ i = token[1] & 255;
+ else // integer code
+ i = atoi( token );
+
+ if ( i < 0 || i > 255 ) {
+ CG_Printf( S_COLOR_RED "CG_LoadFont: bad char index %i.\n", i );
+ return;
+ }
+ fm = fnt->metrics + i;
+
+ // x0
+ token = COM_ParseExt( &text, qfalse );
+ if ( !token[0] ) {
+ CG_Printf( S_COLOR_RED "CG_LoadFont: error reading x0.\n" );
+ return;
+ }
+ x0 = atof( token );
+
+ // y0
+ token = COM_ParseExt( &text, qfalse );
+ if ( !token[0] ) {
+ CG_Printf( S_COLOR_RED "CG_LoadFont: error reading y0.\n" );
+ return;
+ }
+ y0 = atof( token );
+
+ // w1-offset
+ token = COM_ParseExt( &text, qfalse );
+ if ( !token[0] ) {
+ CG_Printf( S_COLOR_RED "CG_LoadFont: error reading x-offset.\n" );
+ return;
+ }
+ w1 = atof( token );
+
+ // w2-offset
+ token = COM_ParseExt( &text, qfalse );
+ if ( !token[0] ) {
+ CG_Printf( S_COLOR_RED "CG_LoadFont: error reading x-length.\n" );
+ return;
+ }
+ w2 = atof( token );
+
+ // space1
+ token = COM_ParseExt( &text, qfalse );
+ if ( !token[0] ) {
+ CG_Printf( S_COLOR_RED "CG_LoadFont: error reading space1.\n" );
+ return;
+ }
+ s1 = atof( token );
+
+ // space2
+ token = COM_ParseExt( &text, qfalse );
+ if ( !token[0] ) {
+ CG_Printf( S_COLOR_RED "CG_LoadFont: error reading space2.\n" );
+ return;
+ }
+ s2 = atof( token );
+
+ fm->tc_mono[0] = x0 * r_width;
+ fm->tc_mono[1] = y0 * r_height;
+ fm->tc_mono[2] = ( x0 + char_width ) * r_width;
+ fm->tc_mono[3] = ( y0 + char_height ) * r_height;
+
+ // proportional y-coords is matching with mono
+ fm->tc_prop[1] = fm->tc_mono[1];
+ fm->tc_prop[3] = fm->tc_mono[3];
+
+ fm->width = w2 / char_width;
+ fm->space1 = s1 / char_width;
+ fm->space2 = (s2 + w2) / char_width;
+ fm->tc_prop[0] = fm->tc_mono[0] + (w1 * r_width);
+ fm->tc_prop[2] = fm->tc_prop[0] + (w2 * r_width);
+
+ chars++;
+
+ SkipRestOfLine( &text );
+ }
+
+ // sort images by threshold
+ do {
+ for ( swapped = qfalse, i = 1 ; i < shaderCount; i++ ) {
+ if ( shaderThreshold[i-1] > shaderThreshold[i] ) {
+ tmp = shaderThreshold[i-1];
+ shaderThreshold[i-1] = shaderThreshold[i];
+ shaderThreshold[i] = tmp;
+ strcpy( tmpName, shaderName[i-1] );
+ strcpy( shaderName[i-1], shaderName[i] );
+ strcpy( shaderName[i], tmpName );
+ swapped = qtrue;
+ }
+ }
+ } while ( swapped );
+
+ // always assume zero threshold for lowest-quality shader
+ shaderThreshold[0] = 0;
+
+ fnt->shaderCount = shaderCount;
+ for ( i = 0; i < shaderCount; i++ ) {
+ fnt->shader[i] = trap_R_RegisterShaderNoMip( shaderName[i] );
+ fnt->shaderThreshold[i] = shaderThreshold[i];
+ }
+
+ CG_Printf( "Font '%s' loaded with %i chars and %i images\n", fontName, chars, shaderCount );
+}
+
+
+void CG_LoadFonts( void )
+{
+ CG_LoadFont( &bigchars, "gfx/2d/bigchars.cfg" );
+ CG_LoadFont( &numbers, "gfx/2d/numbers.cfg" );
+}
+
+
+static float DrawStringLength( const char *string, float ax, float aw, float max_ax, int proportional )
+{
+ const font_metric_t *fm;
+ //float aw1;
+ float x_end;
+ const byte *s;
+ float xx;
+
+ if ( !string )
+ return 0.0f;
+
+ s = (const byte*)string;
+
+ xx = ax;
+
+ while ( *s != '\0' ) {
+
+ if ( *s == Q_COLOR_ESCAPE && s[1] != '\0' && s[1] != '^' ) {
+ //if ( !(flags & DS_SHOW_CODE) ) {
+ s += 2;
+ continue;
+ //}
+ }
+
+ //fm = &font->metrics[ *s ];
+ fm = &metrics[ *s ];
+ if ( proportional ) {
+ //aw1 = fm->width * aw;
+ ax += fm->space1 * aw; // add extra space if required by metrics
+ x_end = ax + fm->space2 * aw; // final position
+ } else {
+ //aw1 = aw;
+ x_end = ax + aw;
+ }
+
+ if ( x_end > max_ax )
+ break;
+
+ ax = x_end;
+ s++;
+ }
+
+ return (ax - xx);
+}
+
+
+void CG_DrawString( float x, float y, const char *string, const vec4_t setColor, float charWidth, float charHeight, int maxChars, int flags )
+{
+ const font_metric_t *fm;
+ const float *tc; // texture coordinates for char
+ float ax, ay, aw, aw1, ah; // absolute positions/dimensions
+ float scale;
+ float x_end, xx;
+ vec4_t color;
+ const byte *s;
+ float xx_add, yy_add;
+ float max_ax;
+ int i;
+ qhandle_t sh;
+ int proportional;
+
+ if ( !string )
+ return;
+
+ s = (const byte *)string;
+
+ ax = x * cgs.screenXScale + cgs.screenXBias;
+ ay = y * cgs.screenYScale + cgs.screenYBias;
+
+ aw = charWidth * cgs.screenXScale;
+ ah = charHeight * cgs.screenYScale;
+
+ if ( maxChars <= 0 ) {
+ max_ax = 9999999.0f;
+ } else {
+ max_ax = ax + aw * maxChars;
+ }
+
+ proportional = (flags & DS_PROPORTIONAL);
+
+ if ( flags & ( DS_CENTER | DS_RIGHT ) ) {
+ if ( flags & DS_CENTER ) {
+ ax -= 0.5f * DrawStringLength( string, ax, aw, max_ax, proportional );
+ } else {
+ ax -= DrawStringLength( string, ax, aw, max_ax, proportional );
+ }
+ }
+
+ sh = font->shader[0]; // low-res shader by default
+
+ if ( flags & DS_SHADOW ) {
+ xx = ax;
+
+ // calculate shadow offsets
+ scale = charWidth * 0.075f; // charWidth/15
+ xx_add = scale * cgs.screenXScale;
+ yy_add = scale * cgs.screenYScale;
+
+ color[0] = color[1] = color[2] = 0.0f;
+ color[3] = setColor[3] * 0.5f;
+ trap_R_SetColor( color );
+
+ while ( *s != '\0' ) {
+ if ( *s == Q_COLOR_ESCAPE && s[1] != '\0' && s[1] != '^' ) {
+ //if ( !(options & DS_SHOW_CODE) ) {
+ s += 2;
+ continue;
+ //}
+ }
+ //fm = &font->metrics[ *s ];
+ fm = &metrics[ *s ];
+ if ( proportional ) {
+ tc = fm->tc_prop;
+ aw1 = fm->width * aw;
+ ax += fm->space1 * aw; // add extra space if required by metrics
+ x_end = ax + fm->space2 * aw; // final position
+ } else {
+ tc = fm->tc_mono;
+ aw1 = aw;
+ x_end = ax + aw;
+ }
+
+ if ( x_end > max_ax || ax >= cgs.glconfig.vidWidth )
+ break;
+
+ trap_R_DrawStretchPic( ax + xx_add, ay + yy_add, aw1, ah, tc[0], tc[1], tc[2], tc[3], sh );
+
+ ax = x_end;
+ s++;
+ }
+
+ // recover altered parameters
+ s = (const byte*)string;
+ ax = xx;
+ }
+
+ // select hi-res shader if accepted
+ for ( i = 1; i < font->shaderCount; i++ ) {
+ if ( ah >= font->shaderThreshold[i] ) {
+ sh = font->shader[i];
+ }
+ }
+
+ Vector4Copy( setColor, color );
+ trap_R_SetColor( color );
+
+ while ( *s != '\0' ) {
+
+ if ( *s == Q_COLOR_ESCAPE && s[1] != '\0' && s[1] != '^' ) {
+ if ( !( flags & DS_FORCE_COLOR ) ) {
+ VectorCopy( g_color_table[ ColorIndex( s[1] ) ], color );
+ trap_R_SetColor( color );
+ }
+ //if ( !(options & DS_SHOW_CODE) ) {
+ s += 2;
+ continue;
+ //}
+ }
+
+ //fm = &font->metrics[ *s ];
+ fm = &metrics[ *s ];
+ if ( proportional ) {
+ tc = fm->tc_prop;
+ aw1 = fm->width * aw;
+ ax += fm->space1 * aw; // add extra space if required by metrics
+ x_end = ax + fm->space2 * aw; // final position
+ } else {
+ tc = fm->tc_mono;
+ aw1 = aw;
+ x_end = ax + aw;
+ }
+
+ if ( x_end > max_ax || ax >= cgs.glconfig.vidWidth )
+ break;
+
+ trap_R_DrawStretchPic( ax, ay, aw1, ah, tc[0], tc[1], tc[2], tc[3], sh );
+
+ ax = x_end;
+ s++;
+ }
+
+ //trap_R_SetColor( NULL );
+}
+#else
+
+
+static float DrawStringLen( const char *s, float charWidth )
+{
+ int count;
+ count = 0;
+ while ( *s ) {
+ if ( Q_IsColorString( s ) ) {
+ s += 2;
+ } else {
+ count++;
+ s++;
+ }
+ }
+ return count * charWidth;
+}
+
+
+void CG_DrawString( float x, float y, const char *s, const vec4_t color, float charWidth, float charHeight, int maxChars, int flags )
+{
+ if ( !color )
+ {
+ color = g_color_table[ ColorIndex( COLOR_WHITE ) ];
+ }
+
+ if ( flags & ( DS_CENTER | DS_RIGHT ) )
+ {
+ float w;
+ w = DrawStringLen( s, charWidth );
+ if ( flags & DS_CENTER )
+ x -= w * 0.5f;
+ else
+ x -= w;
+ }
+
+ CG_DrawStringExt( x, y, s, color, flags & DS_FORCE_COLOR, flags & DS_SHADOW, charWidth, charHeight, maxChars );
+}
+#endif
+
+
/*
=================
CG_DrawStrlen
@@ -261,6 +786,7 @@ int CG_DrawStrlen( const char *str ) {
return count;
}
+
/*
=============
CG_TileClearBox
@@ -280,7 +806,6 @@ static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) {
}
-
/*
==============
CG_TileClear
@@ -319,7 +844,6 @@ void CG_TileClear( void ) {
}
-
/*
================
CG_FadeColor
@@ -351,12 +875,43 @@ float *CG_FadeColor( int startMsec, int totalMsec ) {
}
+/*
+================
+CG_FadeColorTime
+================
+*/
+float *CG_FadeColorTime( int startMsec, int totalMsec, int fadeMsec ) {
+ static vec4_t color;
+ int t;
+
+ if ( startMsec == 0 ) {
+ return NULL;
+ }
+
+ t = cg.time - startMsec;
+
+ if ( t >= totalMsec ) {
+ return NULL;
+ }
+
+ // fade out
+ if ( totalMsec - t < fadeMsec ) {
+ color[3] = ( totalMsec - t ) * 1.0f/(float)fadeMsec;
+ } else {
+ color[3] = 1.0f;
+ }
+ color[0] = color[1] = color[2] = 1.0f;
+
+ return color;
+}
+
+
/*
================
CG_TeamColor
================
*/
-float *CG_TeamColor( int team ) {
+const float *CG_TeamColor( team_t team ) {
static vec4_t red = {1, 0.2f, 0.2f, 1};
static vec4_t blue = {0.2f, 0.2f, 1, 1};
static vec4_t other = {1, 1, 1, 1};
@@ -419,6 +974,7 @@ void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ) {
}
}
+
/*
=================
CG_ColorForHealth
@@ -432,7 +988,6 @@ void CG_ColorForHealth( vec4_t hcolor ) {
-
// bk001205 - code below duplicated in q3_ui/ui-atoms.c
// bk001205 - FIXME: does this belong in ui_shared.c?
// bk001205 - FIXME: HARD_LINKED flags not visible here
@@ -610,7 +1165,7 @@ static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color )
trap_R_SetColor( color );
ax = x * cgs.screenXScale + cgs.screenXBias;
- ay = y * cgs.screenXScale;
+ ay = y * cgs.screenYScale + cgs.screenYBias;
s = str;
while ( *s )
@@ -720,7 +1275,7 @@ static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t co
trap_R_SetColor( color );
ax = x * cgs.screenXScale + cgs.screenXBias;
- ay = y * cgs.screenXScale;
+ ay = y * cgs.screenYScale + cgs.screenYBias;
s = str;
while ( *s )
@@ -814,7 +1369,7 @@ void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t
drawcolor[0] = color[0];
drawcolor[1] = color[1];
drawcolor[2] = color[2];
- drawcolor[3] = 0.5 + 0.5 * sin( cg.time / PULSE_DIVISOR );
+ drawcolor[3] = 0.5 + 0.5 * sin( ( cg.time % TMOD_075 ) / PULSE_DIVISOR );
UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetPropGlow );
return;
}
diff --git a/source/cgame/cg_effects.c b/source/cgame/cg_effects.c
index 73c6656..02983d9 100644
--- a/source/cgame/cg_effects.c
+++ b/source/cgame/cg_effects.c
@@ -162,7 +162,7 @@ CG_SpawnEffect
Player teleporting in or out
==================
*/
-void CG_SpawnEffect( vec3_t org ) {
+refEntity_t *CG_SpawnEffect( const vec3_t origin, qboolean firstPerson ) {
localEntity_t *le;
refEntity_t *re;
@@ -184,8 +184,19 @@ void CG_SpawnEffect( vec3_t org ) {
re->hModel = cgs.media.teleportEffectModel;
AxisClear( re->axis );
- VectorCopy( org, re->origin );
- re->origin[2] -= 24;
+ VectorCopy( origin, re->origin );
+ if ( firstPerson ) {
+ // adjust teleport effect model to cover whole FOV for better immersion
+ re->origin[2] -= 5.0f;
+ re->renderfx = RF_FIRST_PERSON;
+ // add second model visible through portals only
+ re = CG_SpawnEffect( origin, qfalse );
+ re->renderfx = RF_THIRD_PERSON;
+ } else {
+ re->origin[2] -= 24.0f;
+ }
+
+ return re;
}
/*
@@ -294,6 +305,51 @@ localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir,
return ex;
}
+/*
+====================
+CG_SpawnExplosionModel
+====================
+*/
+localEntity_t *CG_SpawnExplosionModel( vec3_t origin, vec3_t dir, leType_t type, qhandle_t hModel, qhandle_t shader, float duration ) { // BFP - Explosion sphere, shell and ring effect
+ localEntity_t *le;
+ vec3_t newOrigin;
+
+ le = CG_AllocLocalEntity();
+
+ le->leType = type;
+
+ le->refEntity.hModel = hModel;
+ le->refEntity.customShader = shader;
+
+ VectorCopy( origin, newOrigin );
+
+ if ( !dir ) {
+ AxisClear( le->refEntity.axis );
+ } else {
+ VectorCopy( dir, le->refEntity.axis[0] );
+ RotateAroundDirection( le->refEntity.axis, 0 );
+ }
+
+ VectorCopy( origin, le->refEntity.origin );
+ VectorCopy( newOrigin, le->refEntity.oldorigin );
+
+ le->refEntity.reType = RT_MODEL;
+ // BFP - Allows to be seen while looking at the other side
+ // while the origin isn't present in camera
+ le->refEntity.nonNormalizedAxes = qtrue;
+
+ le->color[0] = le->color[1] = le->color[2] = 1.0;
+
+ le->startTime = cg.time - (rand() & 63);
+ le->lifeRate = duration;
+ le->endTime = le->startTime + le->lifeRate;
+
+ // bias the time so all shader effects start correctly
+ le->refEntity.shaderTime = cg.time / 1000.0f;
+
+ return le;
+}
+
/*
=================
@@ -525,3 +581,422 @@ void CG_BigExplode( vec3_t playerOrigin ) {
CG_LaunchExplode( origin, velocity, cgs.media.smoke2 );
}
+
+/*
+=================
+CG_DebrisExplosion
+=================
+*/
+void CG_DebrisExplosion( vec3_t origin, vec3_t dir ) { // BFP - Debris particles explosion
+ int i;
+ vec3_t sprOrg, sprVel;
+ int numRocks = 26;
+ vec3_t right, up, forward;
+
+ VectorCopy( dir, forward );
+ PerpendicularVector( right, forward );
+ CrossProduct( forward, right, up );
+
+ // BFP - NOTE: Debris particles shouldn't be used for bullet and disk weapon types
+
+ // BFP - TODO: Apply number of rocks as indicated on default.cfg file of some character: explosionRocks
+
+ for ( i = 0; i < numRocks; ++i ) {
+ // spawn randomly the shaders with the particles
+ int shaderIndex = rand() % 3;
+ float speed = 1000.0f + (rand() % 800);
+ float rSpread = crandom() * 4000.0f;
+ float uSpread = crandom() * 4000.0f;
+
+ // that would be the range for debris particles
+ VectorMA( origin, 24, dir, sprOrg );
+ sprOrg[0] += (rand() % 24);
+ sprOrg[1] += (rand() % 24);
+ sprOrg[2] += (rand() % 24);
+
+ VectorScale( dir, speed, sprVel );
+
+ VectorMA( sprVel, rSpread, right, sprVel );
+ VectorMA( sprVel, uSpread, up, sprVel );
+
+ sprVel[0] += crandom() * 100;
+ sprVel[1] += crandom() * 100;
+ sprVel[2] += crandom() * 100;
+
+ // BFP - Rock models
+ if ( cg_3dparticles.integer > 0
+ && cgs.media.pebbleMdl1
+ && cgs.media.pebbleMdl2
+ && cgs.media.pebbleMdl3 ) {
+ int rockModelIndex = rand() % 3;
+ switch ( rockModelIndex ) {
+ case 0: {
+ CG_ParticleRockDebris( cgs.media.pebbleShader1, cgs.media.pebbleMdl1, sprOrg, sprVel, 3, 150, 250 );
+ break;
+ }
+ case 1: {
+ CG_ParticleRockDebris( cgs.media.pebbleShader2, cgs.media.pebbleMdl2, sprOrg, sprVel, 3, 150, 250 );
+ break;
+ }
+ default: {
+ CG_ParticleRockDebris( cgs.media.pebbleShader3, cgs.media.pebbleMdl3, sprOrg, sprVel, 3, 150, 250 );
+ }
+ }
+ continue;
+ }
+
+ switch ( shaderIndex ) {
+ case 0: {
+ CG_ParticleRockDebris( cgs.media.pebbleShader1, 0, sprOrg, sprVel, 3, 150, 250 );
+ break;
+ }
+ case 1: {
+ CG_ParticleRockDebris( cgs.media.pebbleShader2, 0, sprOrg, sprVel, 3, 150, 250 );
+ break;
+ }
+ default: {
+ CG_ParticleRockDebris( cgs.media.pebbleShader3, 0, sprOrg, sprVel, 3, 150, 250 );
+ }
+ }
+ }
+}
+
+/*
+=================
+CG_SparksExplosion
+=================
+*/
+void CG_SparksExplosion( vec3_t origin, vec3_t dir ) { // BFP - Spark particles explosion
+ int i;
+ vec3_t sparkOrg, sparkVel;
+ int numSparks = 26;
+ vec3_t right, up, forward;
+
+ VectorCopy( dir, forward );
+ PerpendicularVector( right, forward );
+ CrossProduct( forward, right, up );
+
+ // BFP - NOTE: Spark particles shouldn't be used on bullet and disk weapon types
+
+ // BFP - TODO: Apply calling this function for finger blast and these rail gun weapon types when hitting a player
+
+ // BFP - TODO: Apply number of sparks as indicated on default.cfg file of some character: explosionSparks
+
+ for ( i = 0; i < numSparks; ++i ) {
+ // spawn randomly the shaders with the particles
+ int shaderIndex = (rand() % 100) < 50 ? 0 : 1; // if the random range was rand() % 2, it would repeat the pattern without randomize correctly
+ float speed = 2000.0f + (rand() % 800);
+ float rSpread = crandom() * 2000.0f;
+ float uSpread = crandom() * 2000.0f;
+
+ VectorMA( origin, 1.0f, dir, sparkOrg );
+
+ VectorScale( dir, speed, sparkVel );
+
+ VectorMA( sparkVel, rSpread, right, sparkVel );
+ VectorMA( sparkVel, uSpread, up, sparkVel );
+
+ sparkVel[0] += crandom() * 100;
+ sparkVel[1] += crandom() * 100;
+ sparkVel[2] += crandom() * 100;
+
+ switch ( shaderIndex ) {
+ case 0: {
+ CG_ParticleSparks( cgs.media.sparkShader1, sparkOrg, sparkVel );
+ break;
+ }
+ default: {
+ CG_ParticleSparks( cgs.media.sparkShader2, sparkOrg, sparkVel );
+ }
+ }
+ }
+}
+
+/*
+=================
+CG_BeamStruggleEffect
+=================
+*/
+void CG_BeamStruggleEffect( vec3_t origin, vec3_t dir ) { // BFP - Beam struggle effect
+ int i;
+ vec3_t sparkOrg, sparkVel;
+ int numSparks = 26;
+
+ for ( i = 0; i < numSparks; ++i ) {
+ // spawn randomly the shaders with the particles
+ int shaderIndex = (rand() % 100) < 50 ? 0 : 1;
+ float speed = 600.0f + (rand() % 800);
+
+ VectorMA( origin, 0, dir, sparkOrg );
+
+ sparkVel[0] = crandom();
+ sparkVel[1] = crandom();
+ sparkVel[2] = crandom();
+
+ VectorNormalize( sparkVel );
+ VectorScale( sparkVel, speed, sparkVel );
+
+ switch ( shaderIndex ) {
+ case 0: {
+ CG_ParticleBeamStruggleSpark( cgs.media.sparkShader1, sparkOrg, sparkVel );
+ break;
+ }
+ default: {
+ CG_ParticleBeamStruggleSpark( cgs.media.sparkShader2, sparkOrg, sparkVel );
+ }
+ }
+ }
+}
+
+/*
+=================
+CG_SmokeExplosion
+=================
+*/
+void CG_SmokeExplosion( vec3_t origin, vec3_t dir ) { // BFP - Explosion smoke
+ if ( cg_explosionSmoke.integer > 0
+ && !( trap_CM_PointContents( origin, 0 ) & MASK_WATER ) ) { // don't spawn smoke under water, lava or any liquid
+ // BFP - TODO: Apply explosionSmoke as indicated on default.cfg file from some character: explosionSmoke
+ int i, numSmokes = 3;
+ // BFP - TODO: Apply explosionSmokeRadius as indicated on default.cfg file from some character: explosionSmokeRadius
+ int explosionSmokeRadius = 200;
+ // BFP - TODO: Apply explosionSmokeLife as indicated on default.cfg file from some character: explosionSmokeLife
+ int explosionSmokeLife = 1500;
+ // BFP - TODO: Apply explosionSmokeSpeed as indicated on default.cfg file from some character: explosionSmokeSpeed
+ int explosionSmokeSpeed = 10;
+
+ // for spreading smoke
+ vec3_t up = {0, 0, 1};
+ vec3_t right, forward;
+
+ // BFP - To randomize the vertical speed
+ float minVert = 10 * explosionSmokeSpeed;
+ float maxVert = 50 * explosionSmokeSpeed;
+
+ VectorCopy( dir, forward );
+ CrossProduct( forward, up, right );
+ if ( VectorLength( right ) < 0.1f ) {
+ vec3_t side = {1, 0, 0};
+ CrossProduct( forward, side, right );
+ }
+ VectorNormalize( right );
+ CrossProduct( right, forward, up );
+ VectorNormalize( up );
+
+ for ( i = 0; i < numSmokes; ++i ) {
+ localEntity_t *leSmoke;
+ vec3_t vel, smokeOrg, spreadDir;
+ float rightSpread = crandom() * 500;
+ float upSpread = crandom() * 500;
+
+ VectorCopy( dir, spreadDir );
+ VectorMA( spreadDir, rightSpread, right, spreadDir );
+ VectorMA( spreadDir, upSpread, up, spreadDir );
+ VectorNormalize( spreadDir );
+
+ // position smoke offset in the spread direction
+ VectorMA( origin, 20 + ( rand() % 80 ), spreadDir, smokeOrg );
+
+ // velocity moves outward in spread direction
+ VectorScale( spreadDir, 150 + ( rand() % 500 ), vel );
+ vel[2] = maxVert + ( rand() % (int)( minVert ) );
+
+ leSmoke = CG_SmokePuff( smokeOrg, vel,
+ explosionSmokeRadius,
+ 1, 1, 1, 0.33f,
+ explosionSmokeLife,
+ cg.time, 0, 0,
+ cgs.media.particleSmokeShader );
+
+ // change to this type, don't use the common smoke puff
+ leSmoke->leType = LE_MOVE_DONT_SCALE_FADE;
+ }
+ }
+}
+
+
+/*
+=================
+CG_ExplosionSound
+=================
+*/
+void CG_ExplosionSound( vec3_t origin ) { // BFP - Explosion sounds
+ // BFP - TODO: noExplosionSound from skin config
+ int i = rand() % 6;
+
+ switch ( i ) {
+ case 0: trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.explosion1Sound ); break;
+ case 1: trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.explosion2Sound ); break;
+ case 2: trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.explosion3Sound ); break;
+ case 3: trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.explosion4Sound ); break;
+ case 4: trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.explosion5Sound ); break;
+ default: trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.explosion6Sound ); break;
+ }
+}
+
+
+/*
+=================
+CG_ExplosionEffect
+=================
+*/
+void CG_ExplosionEffect( vec3_t origin, vec3_t dir ) { // BFP - Explosion effects
+ // BFP - Low poly sphere
+ // BFP - TODO: Apply explosionModel from bfp attack config, highPolySphereModel is just a test
+ qhandle_t sphereModel = ( cg_lowpolysphere.integer > 0 ) ? cgs.media.lowPolySphereModel : cgs.media.highPolySphereModel;
+ localEntity_t *leSphere, *leRing = NULL, *leShell = NULL;
+ float scale = 1;
+
+ // BFP - TODO: Apply explosionShader from bfp attack config, ImpactBeamExplosionShader is just a test
+ leSphere = CG_SpawnExplosionModel( origin, dir, LE_EXPLOSION_SPHERE, sphereModel, cgs.media.ImpactBeamExplosionShader, 1200 );
+ if ( cg_explosionShell.integer > 0 ) { // BFP - Explosion shell
+ leShell = CG_SpawnExplosionModel( origin, dir, LE_EXPLOSION_SHELL, sphereModel, cgs.media.explosionShellShader, 250 );
+ }
+ if ( cg_explosionRing.integer > 0 ) { // BFP - Explosion ring
+ leRing = CG_SpawnExplosionModel( origin, dir, LE_EXPLOSION_RING, cgs.media.ringFlashModel, cgs.media.railExplosionShader, 500 );
+ }
+ CG_SmokeExplosion( origin, dir ); // BFP - Explosion smoke
+
+ if ( cg_bigExplosions.integer > 0 ) { // BFP - Big explosions
+ const float MAX_SCALE = 25.0f, MAX_SCALEFACTOR = 6.0f; // limits to prevent too large scaling
+ int numPointsChargedOverMin = 1; // that means when reaching to 'READY!', it starts as 1 and if it's charging another charge point, adds 1 more
+ // BFP - TODO: Apply explosionScaleFactor as indicated on default.cfg file from some character: explosionScaleFactor
+ // BFP - TODO: Apply explosionScaleFactorChargeMult as indicated on default.cfg file from some character: explosionScaleFactorChargeMult
+ float explosionScaleFactor = 0.95, explosionScaleFactorChargeMult = 0;
+ // BFP - TODO: Apply explosionRingScaleFactor from default.cfg file from some character: explosionRingScaleFactor
+ // BFP - TODO: Apply explosionRingScaleFactorChargeMult from default.cfg file from some character: explosionRingScaleFactorChargeMult
+ float explosionRingScaleFactor = 0.95, explosionRingScaleFactorChargeMult = 0;
+ // BFP - TODO: Apply explosionShellScaleFactor from default.cfg file from some character: explosionShellScaleFactor
+ // BFP - TODO: Apply explosionShellScaleFactorChargeMult from default.cfg file from some character: explosionShellScaleFactorChargeMult
+ float explosionShellScaleFactor = 0.95, explosionShellScaleFactorChargeMult = 0;
+
+ if ( explosionScaleFactor > MAX_SCALEFACTOR ) explosionScaleFactor = MAX_SCALEFACTOR;
+ if ( explosionScaleFactorChargeMult > MAX_SCALEFACTOR ) explosionScaleFactorChargeMult = MAX_SCALEFACTOR;
+ scale = explosionScaleFactor + explosionScaleFactorChargeMult * numPointsChargedOverMin;
+ if ( scale > MAX_SCALE ) scale = MAX_SCALE;
+ VectorScale( leSphere->refEntity.axis[0], scale, leSphere->refEntity.axis[0] );
+ VectorScale( leSphere->refEntity.axis[1], scale, leSphere->refEntity.axis[1] );
+ VectorScale( leSphere->refEntity.axis[2], scale, leSphere->refEntity.axis[2] );
+
+ if ( cg_explosionRing.integer > 0 ) {
+ if ( explosionRingScaleFactor > MAX_SCALEFACTOR ) explosionRingScaleFactor = MAX_SCALEFACTOR;
+ if ( explosionRingScaleFactorChargeMult > MAX_SCALEFACTOR ) explosionRingScaleFactorChargeMult = MAX_SCALEFACTOR;
+ scale = explosionRingScaleFactor + explosionRingScaleFactorChargeMult * numPointsChargedOverMin;
+ if ( scale > MAX_SCALE ) scale = MAX_SCALE;
+ if ( leRing ) {
+ VectorScale( leRing->refEntity.axis[0], scale, leRing->refEntity.axis[0] );
+ VectorScale( leRing->refEntity.axis[1], scale, leRing->refEntity.axis[1] );
+ VectorScale( leRing->refEntity.axis[2], scale, leRing->refEntity.axis[2] );
+ }
+ }
+
+ if ( cg_explosionShell.integer > 0 ) {
+ if ( explosionShellScaleFactor > MAX_SCALEFACTOR ) explosionShellScaleFactor = MAX_SCALEFACTOR;
+ if ( explosionShellScaleFactorChargeMult > MAX_SCALEFACTOR ) explosionShellScaleFactorChargeMult = MAX_SCALEFACTOR;
+ scale = explosionShellScaleFactor + explosionShellScaleFactorChargeMult * numPointsChargedOverMin;
+ if ( scale > MAX_SCALE ) scale = MAX_SCALE;
+ if ( leShell ) {
+ VectorScale( leShell->refEntity.axis[0], scale, leShell->refEntity.axis[0] );
+ VectorScale( leShell->refEntity.axis[1], scale, leShell->refEntity.axis[1] );
+ VectorScale( leShell->refEntity.axis[2], scale, leShell->refEntity.axis[2] );
+ }
+ }
+ }
+
+ // BFP - Apply dynamic explosion light values
+ leSphere->light = 400 * scale;
+ leSphere->lightColor[0] = 1;
+ leSphere->lightColor[1] = 0.75;
+ leSphere->lightColor[2] = 0.0;
+}
+
+
+/*
+=================
+CG_ForceFieldEffect
+=================
+*/
+void CG_ForceFieldEffect( centity_t *cent, vec3_t origin, char *explosionModel, char *explosionShader ) { // BFP - Forcefield effect
+ if ( cent->currentState.weapon == WP_SHOTGUN
+ && ( cent->currentState.eFlags & EF_FIRING ) ) {
+ if ( !cent->pe.forceFieldStartTime ) {
+ cent->pe.forceFieldStartTime = cg.time;
+ }
+ } else {
+ cent->pe.forceFieldStartTime = 0;
+ }
+
+ if ( cent->pe.forceFieldStartTime ) {
+ const float MAX_SCALE = 20;
+ int elapsed = cg.time - cent->pe.forceFieldStartTime;
+ float scale;
+ // BFP - TODO: Apply explosionModel from bfp attack config
+ qhandle_t explosionModelRef = trap_R_RegisterModel( explosionModel );
+ // BFP - TODO: Apply explosionShader from bfp attack config
+ qhandle_t explosionShaderRef = trap_R_RegisterShader( explosionShader );
+ // BFP - TODO: Apply explosionScaleFactor as indicated on default.cfg file from some character: explosionScaleFactor
+ // BFP - TODO: Apply explosionScaleFactorChargeMult as indicated on default.cfg file from some character: explosionScaleFactorChargeMult
+ float explosionScaleFactor = 1, explosionScaleFactorChargeMult = 2;
+ refEntity_t ff;
+
+ memset( &ff, 0, sizeof(ff) );
+
+ ff.reType = RT_MODEL;
+ ff.hModel = explosionModelRef;
+ ff.customShader = explosionShaderRef;
+ ff.renderfx = RF_LIGHTING_ORIGIN;
+
+ VectorCopy( origin, ff.origin );
+ VectorCopy( origin, ff.lightingOrigin );
+ AnglesToAxis( vec3_origin, ff.axis );
+
+ if ( explosionScaleFactor <= 0 ) {
+ explosionScaleFactor = 1;
+ }
+
+// BFP - That version scales smoothly than the original BFP one
+#define FORCEFIELD_SMOOTH_SCALE 1
+#if FORCEFIELD_SMOOTH_SCALE
+ {
+ int endTime = cent->pe.forceFieldStartTime + 5400;
+ int duration = endTime - cent->pe.forceFieldStartTime;
+ const float TIMESCALE_FF = 13;
+ float t = (float)(elapsed * TIMESCALE_FF) / (float)duration;
+
+ if ( t > 1 ) {
+ t = 1;
+ }
+ scale = ( explosionScaleFactor + explosionScaleFactorChargeMult )
+ * ( 1.0f + t * ( MAX_SCALE - 1.0f ) );
+ }
+#else
+ {
+ const int MAX_STEPS = 27;
+ const int STEP_TIME_MSC = 200; // msc per step
+ int step = elapsed / STEP_TIME_MSC;
+
+ if ( step > MAX_STEPS ) {
+ step = MAX_STEPS;
+ }
+ //scale = 1.0f + (float)step / (float)MAX_STEPS * ( MAX_SCALE - 1.0f );
+
+ //scale = ( explosionScaleFactor + explosionScaleFactorChargeMult )
+ // * ( 1.0f + (float)step / MAX_STEPS * ( MAX_SCALE - 1.0f ) );
+
+ scale = 1.0f + ( (float)step / (float)MAX_STEPS ) * MAX_SCALE
+ * ( explosionScaleFactor + explosionScaleFactorChargeMult );
+ }
+#endif
+ // BFP - Allows to be seen while looking at the other side
+ // while the origin isn't present in camera
+ ff.nonNormalizedAxes = qtrue;
+
+ CG_ModelSize( &ff, scale );
+
+ // add the entity
+ trap_R_AddRefEntityToScene( &ff );
+
+ // add the dlight
+ trap_R_AddLightToScene( ff.origin, 600 * scale, 0.25, 0.15, 0.75 );
+ }
+}
diff --git a/source/cgame/cg_ents.c b/source/cgame/cg_ents.c
index 98f3190..23354d4 100644
--- a/source/cgame/cg_ents.c
+++ b/source/cgame/cg_ents.c
@@ -426,11 +426,33 @@ static void CG_Missile( centity_t *cent ) {
VectorCopy( cent->lerpOrigin, ent.origin);
VectorCopy( cent->lerpOrigin, ent.oldorigin);
- if ( cent->currentState.weapon == WP_PLASMAGUN ) {
+ // BFP - missileShader
+ if ( weapon->missileShader ) {
+ ent.customShader = weapon->missileShader;
+ }
+
+ // BFP - missileRotation
+ if ( weapon->missileRotation > 0 ) {
+ ent.rotation = weapon->missileRotation;
+ }
+
+ // BFP - missileModel and missileShader
+ if ( !weapon->missileModel && weapon->missileShader ) {
ent.reType = RT_SPRITE;
- ent.radius = 16;
- ent.rotation = 0;
- ent.customShader = cgs.media.plasmaBallShader;
+
+ // BFP - missileRadius and missileRadiusChargeMult
+ if ( ent.reType == RT_SPRITE ) {
+ float missileRadius = 50, missileRadiusChargeMult = 75;
+ float minCharge = 2;
+ float totalCharge = cent->currentState.generic1 - minCharge;
+ if ( totalCharge < 0 ) {
+ totalCharge = 0;
+ }
+ if ( missileRadius > 0 ) {
+ ent.radius = missileRadius + missileRadiusChargeMult * totalCharge;
+ }
+ }
+
trap_R_AddRefEntityToScene( &ent );
return;
}
@@ -438,6 +460,10 @@ static void CG_Missile( centity_t *cent ) {
// flicker between two skins
ent.skinNum = cg.clientFrame & 1;
ent.hModel = weapon->missileModel;
+ // BFP - Skip rendering if no missile model
+ if ( !weapon->missileModel ) {
+ return;
+ }
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
// convert direction of travel into axis
@@ -446,18 +472,46 @@ static void CG_Missile( centity_t *cent ) {
}
// spin as it moves
- if ( s1->pos.trType != TR_STATIONARY ) {
- RotateAroundDirection( ent.axis, cg.time / 4 );
+ if ( s1->pos.trType != TR_STATIONARY
+ && s1->weapon != WP_BFG ) { // BFP - For disk or missileSpinHoriz (qboolean) weapons, don't rotate like the rocket
+ // BFP - missileModelRotation
+ float missileModelRotation = 0.2;
+ RotateAroundDirection( ent.axis, cg.time * missileModelRotation );
} else {
{
+ // BFP - Rotate Z-axis like a wheel
+ vec3_t temp;
RotateAroundDirection( ent.axis, s1->time );
+
+ VectorCopy( ent.axis[0], temp );
+ RotatePointAroundVector( ent.axis[0], ent.axis[2], temp, cg.autoAnglesFast[1] );
+ VectorCopy( ent.axis[1], temp );
+ RotatePointAroundVector( ent.axis[1], ent.axis[2], temp, cg.autoAnglesFast[1] );
+ }
+ }
+
+ // BFP - missileScaleFactor and missileScaleFactorChargeMult
+ // BFP - TODO: Handle saved ki charge points and calculate the total of number of points charged over the min
+ if ( ent.reType == RT_MODEL ) {
+ float scale = 1;
+ float missileScaleFactor = 3, missileScaleFactorChargeMult = 1;
+ float minCharge = 2;
+ float totalCharge = cent->currentState.generic1 - minCharge;
+ if ( totalCharge < 0 ) {
+ totalCharge = 0;
}
+ if ( missileScaleFactor > 0 ) {
+ scale = missileScaleFactor + missileScaleFactorChargeMult * totalCharge;
+ }
+ CG_ModelSize( &ent, scale );
}
// add to refresh list, possibly with quad glow
- CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
+ CG_AddRefEntityWithPowerups( ent, s1, TEAM_FREE );
}
+// BFP - no hook
+#if 0
/*
===============
CG_Grapple
@@ -506,6 +560,7 @@ static void CG_Grapple( centity_t *cent ) {
trap_R_AddRefEntityToScene( &ent );
}
+#endif
/*
===============
@@ -618,7 +673,7 @@ Also called by client movement prediction code
void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
centity_t *cent;
vec3_t oldOrigin, origin, deltaOrigin;
- vec3_t oldAngles, angles, deltaAngles;
+ vec3_t oldAngles, angles; //, deltaAngles;
if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
VectorCopy( in, out );
@@ -638,7 +693,8 @@ void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int
BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles );
VectorSubtract( origin, oldOrigin, deltaOrigin );
- VectorSubtract( angles, oldAngles, deltaAngles );
+ // BFP - Unused in Q3? What?
+ // VectorSubtract( angles, oldAngles, deltaAngles );
VectorAdd( in, deltaOrigin, out );
@@ -743,9 +799,6 @@ static void CG_TeamBase( centity_t *cent ) {
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
model.hModel = cgs.media.blueFlagBaseModel;
}
- else {
- model.hModel = cgs.media.neutralFlagBaseModel;
- }
trap_R_AddRefEntityToScene( &model );
}
}
diff --git a/source/cgame/cg_event.c b/source/cgame/cg_event.c
index dc3c179..79b2b3b 100644
--- a/source/cgame/cg_event.c
+++ b/source/cgame/cg_event.c
@@ -83,8 +83,8 @@ static void CG_Obituary( entityState_t *ent ) {
const char *attackerInfo;
char targetName[32];
char attackerName[32];
- gender_t gender;
- clientInfo_t *ci;
+ // BFP - No gender variable for MOD messages
+ // gender_t gender;
target = ent->otherEntityNum;
attacker = ent->otherEntityNum2;
@@ -93,7 +93,6 @@ static void CG_Obituary( entityState_t *ent ) {
if ( target < 0 || target >= MAX_CLIENTS ) {
CG_Error( "CG_Obituary: target out of range" );
}
- ci = &cgs.clientinfo[target];
if ( attacker < 0 || attacker >= MAX_CLIENTS ) {
attacker = ENTITYNUM_WORLD;
@@ -138,12 +137,18 @@ static void CG_Obituary( entityState_t *ent ) {
case MOD_TRIGGER_HURT:
message = "was in the wrong place";
break;
+ // BFP - Player being kicked by using an illegal model
+ case MOD_ILLEGAL_PLAYER_MODEL:
+ message = "got kicked into spectator mode for using an illegal model";
+ break;
default:
message = NULL;
break;
}
if (attacker == target) {
+ // BFP - No gender MOD messages
+#if 0
gender = ci->gender;
switch (mod) {
case MOD_GRENADE_SPLASH:
@@ -182,6 +187,10 @@ static void CG_Obituary( entityState_t *ent ) {
message = "killed himself";
break;
}
+#endif
+ if ( attacker == cg.snap->ps.clientNum ) { // BFP - Add 1 lifedeath in the history
+ trap_Cvar_Set( "cg_lifedeaths", va( "%i", (int)( cg_lifedeaths.integer + 1 ) ) );
+ }
}
if (message) {
@@ -190,18 +199,20 @@ static void CG_Obituary( entityState_t *ent ) {
}
// check for kill messages from the current clientNum
- if ( attacker == cg.snap->ps.clientNum ) {
+ if ( attacker == cg.snap->ps.clientNum && attacker != target ) {
char *s;
- if ( cgs.gametype < GT_TEAM ) {
- s = va("You fragged %s\n%s place with %i", targetName,
+ if ( cgs.gametype < GT_TEAM ) { // BFP - Before Q3: "You fragged %s\n%s place with %i"
+ s = va("You sent %s to the next dimension!\n%s place with %i", targetName,
CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ),
cg.snap->ps.persistant[PERS_SCORE] );
- } else {
- s = va("You fragged %s", targetName );
+ } else { // BFP - Before Q3: "You fragged %s"
+ s = va("You sent %s to the next dimension!", targetName );
}
CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
// print the text message as well
+ // BFP - Add 1 lifekill in the history
+ trap_Cvar_Set( "cg_lifekills", va( "%i", (int)( cg_lifekills.integer + 1 ) ) );
}
// check for double client messages
@@ -217,8 +228,11 @@ static void CG_Obituary( entityState_t *ent ) {
}
}
- if ( attacker != ENTITYNUM_WORLD ) {
+ // BFP - Don't admit that the attacker is the same as the target
+ if ( attacker != ENTITYNUM_WORLD && attacker != target ) {
switch (mod) {
+ // BFP - No other MOD messages
+#if 0
case MOD_GRAPPLE:
message = "was caught by";
break;
@@ -270,11 +284,18 @@ static void CG_Obituary( entityState_t *ent ) {
message = "tried to invade";
message2 = "'s personal space";
break;
- default:
- message = "was killed by";
+#endif
+ case MOD_MELEE: // BFP - Melee
+ message = "was beaten up by";
+ break;
+ default: // BFP - Kill with ki attack and telefrag message
+ message = "was sent to the next dimension by";
break;
}
+ if ( target == cg.snap->ps.clientNum ) { // BFP - Add 1 lifedeath in the history
+ trap_Cvar_Set( "cg_lifedeaths", va( "%i", (int)( cg_lifedeaths.integer + 1 ) ) );
+ }
if (message) {
CG_Printf( "%s %s %s%s\n",
targetName, message, attackerName, message2);
@@ -284,6 +305,11 @@ static void CG_Obituary( entityState_t *ent ) {
// we don't know what it was
CG_Printf( "%s died.\n", targetName );
+ if ( target == cg.snap->ps.clientNum ) { // BFP - Add 1 lifedeath in the history
+ trap_Cvar_Set( "cg_lifedeaths", va( "%i", (int)( cg_lifedeaths.integer + 1 ) ) );
+ }
+ // BFP - Reset ki trails to avoid viewing other trails
+ CG_ResetTrail( KI_TRAIL, target, vec3_origin );
}
//==========================================================================
@@ -402,109 +428,158 @@ An entity has an event value
also called by CG_CheckPlayerstateEvents
==============
*/
-#define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");}
-void CG_EntityEvent( centity_t *cent, vec3_t position ) {
+void CG_EntityEvent( centity_t *cent, vec3_t position, int entityNum ) {
entityState_t *es;
- int event;
+ entity_event_t event;
vec3_t dir;
const char *s;
int clientNum;
clientInfo_t *ci;
+ centity_t *ce;
es = ¢->currentState;
event = es->event & ~EV_EVENT_BITS;
+ if ( (unsigned) event >= EV_MAX ) {
+ CG_Error( "Unknown event: %i", event );
+ return;
+ }
+
if ( cg_debugEvents.integer ) {
- CG_Printf( "ent:%3i event:%3i ", es->number, event );
+ CG_Printf( "ent:%3i event:%3i %s\n", es->number, event, eventnames[ event ] );
}
if ( !event ) {
- DEBUGNAME("ZEROEVENT");
+ // DEBUGNAME("ZEROEVENT");
return;
}
clientNum = es->clientNum;
- if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
+ if ( (unsigned) clientNum >= MAX_CLIENTS ) {
clientNum = 0;
}
ci = &cgs.clientinfo[ clientNum ];
+ // BFP - HIGHLY MODIFIED, every event is sorted for original BFP networking
+
switch ( event ) {
+ case EV_NONE: // 0
+ case EV_UNUSED_INDEX1: // 1
+ case EV_UNUSED_INDEX2: // 2
+ case EV_UNUSED_INDEX3: // 3
+ case EV_UNUSED_INDEX4: // 4
+ case EV_UNUSED_INDEX5: // 5
+ case EV_UNUSED_INDEX6: // 6
+ case EV_UNUSED_INDEX7: // 7
+ case EV_UNUSED_INDEX8: // 8
+ case EV_UNUSED_INDEX9: // 9
+ break;
+
+ // BFP - Melee
+ case EV_MELEE_READY: // 10
+ break;
+ case EV_MELEE: // 11
+ {
+ int rndMeleeSnd = rand() % 5;
+ switch ( rndMeleeSnd ) {
+ case 0: {
+ trap_S_StartSound (NULL, es->number, CHAN_BODY, CG_CustomSound( es->number, "sound/bfp/melee_hit1.wav" ) );
+ break;
+ }
+ case 1: {
+ trap_S_StartSound (NULL, es->number, CHAN_BODY, CG_CustomSound( es->number, "sound/bfp/melee_hit2.wav" ) );
+ break;
+ }
+ case 2: {
+ trap_S_StartSound (NULL, es->number, CHAN_BODY, CG_CustomSound( es->number, "sound/bfp/melee_hit3.wav" ) );
+ break;
+ }
+ case 3: {
+ trap_S_StartSound (NULL, es->number, CHAN_BODY, CG_CustomSound( es->number, "sound/bfp/melee_hit4.wav" ) );
+ break;
+ }
+ default: {
+ trap_S_StartSound (NULL, es->number, CHAN_BODY, CG_CustomSound( es->number, "sound/bfp/melee_hit5.wav" ) );
+ }
+ }
+ break;
+ }
+
+ case EV_UNUSED_INDEX12: // 12
+ break;
+
+ // BFP - Tier up events
+ case EV_TIER_RESET: // 13
+ break;
+ case EV_TIER_0: // 14
+ case EV_TIER_1: // 15
+ case EV_TIER_2: // 16
+ case EV_TIER_3: // 17
+ case EV_TIER_4: // 18
+ trap_S_StartSound ( NULL, es->otherEntityNum, CHAN_BODY, cgs.media.tierUpSound );
+ if ( event == EV_TIER_4 && es->otherEntityNum == cg.snap->ps.clientNum ) {
+ trap_SendConsoleCommand( "transformorbit\n" );
+ }
+ break;
+
+ // BFP - Short-Range Teleport (Zanzoken)
+ case EV_ZANZOKEN_IN: // 19
+ trap_S_StartSound (NULL, es->number, CHAN_BODY, CG_CustomSound( es->number, "sound/bfp/srteleport.wav" ) );
+ case EV_ZANZOKEN_OUT: // 20
+ break;
+
+ // BFP - Ki boost
+ case EV_KI_BOOST: // 21
+ break;
+
+ // BFP - A normal jump sound is played when enables the flight
+ case EV_ENABLE_FLIGHT: // 22
+ trap_S_StartSound (NULL, es->number, CHAN_BODY, CG_CustomSound( es->number, "sound/bfp/jump1.wav" ) );
+ break;
+
//
// movement generated events
//
- case EV_FOOTSTEP:
- DEBUGNAME("EV_FOOTSTEP");
+ case EV_FOOTSTEP: // 23
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ ci->footsteps ][rand()&3] );
}
break;
- case EV_FOOTSTEP_METAL:
- DEBUGNAME("EV_FOOTSTEP_METAL");
+
+ case EV_FOOTSTEP_METAL: // 24
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_METAL ][rand()&3] );
}
break;
- case EV_FOOTSPLASH:
- DEBUGNAME("EV_FOOTSPLASH");
+
+ case EV_FOOTSPLASH: // 25
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] );
}
break;
- case EV_FOOTWADE:
- DEBUGNAME("EV_FOOTWADE");
+
+ case EV_FOOTWADE: // 26
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] );
}
break;
- case EV_SWIM:
- DEBUGNAME("EV_SWIM");
+
+ case EV_SWIM: // 27
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] );
}
break;
-
- case EV_FALL_SHORT:
- DEBUGNAME("EV_FALL_SHORT");
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound );
- if ( clientNum == cg.predictedPlayerState.clientNum ) {
- // smooth landing z changes
- cg.landChange = -8;
- cg.landTime = cg.time;
- }
- break;
- case EV_FALL_MEDIUM:
- DEBUGNAME("EV_FALL_MEDIUM");
- // use normal pain sound
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) );
- if ( clientNum == cg.predictedPlayerState.clientNum ) {
- // smooth landing z changes
- cg.landChange = -16;
- cg.landTime = cg.time;
- }
- break;
- case EV_FALL_FAR:
- DEBUGNAME("EV_FALL_FAR");
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
- cent->pe.painTime = cg.time; // don't play a pain sound right after this
- if ( clientNum == cg.predictedPlayerState.clientNum ) {
- // smooth landing z changes
- cg.landChange = -24;
- cg.landTime = cg.time;
- }
- break;
-
- case EV_STEP_4:
- case EV_STEP_8:
- case EV_STEP_12:
+ case EV_STEP_4: // 28
+ case EV_STEP_8: // 29
+ case EV_STEP_12: // 30
+ // 31
case EV_STEP_16: // smooth out step up transitions
- DEBUGNAME("EV_STEP");
{
float oldStep;
int delta;
@@ -536,9 +611,41 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
break;
}
- case EV_JUMP_PAD:
- DEBUGNAME("EV_JUMP_PAD");
+ case EV_FALL_SHORT: // 32
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound );
+ if ( clientNum == cg.predictedPlayerState.clientNum ) {
+ // smooth landing z changes
+ cg.landChange = -8;
+ cg.landTime = cg.time;
+ }
+ break;
+
+ case EV_FALL_MEDIUM: // 33
+ // BFP - Use normal land sound instead
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound );
+ // use normal pain sound
+ // trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) );
+ if ( clientNum == cg.predictedPlayerState.clientNum ) {
+ // smooth landing z changes
+ cg.landChange = -16;
+ cg.landTime = cg.time;
+ }
+ break;
+
+ case EV_FALL_FAR: // 34
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
+ cent->pe.painTime = cg.time; // don't play a pain sound right after this
+ if ( clientNum == cg.predictedPlayerState.clientNum ) {
+ // smooth landing z changes
+ cg.landChange = -24;
+ cg.landTime = cg.time;
+ }
+ break;
+
+ case EV_JUMP_PAD: // 35
// CG_Printf( "EV_JUMP_PAD w/effect #%i\n", es->eventParm );
+// BFP - No smoke puff effect when using a jump pad
+#if 0
{
localEntity_t *smoke;
vec3_t up = {0, 0, 1};
@@ -552,39 +659,91 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
LEF_PUFF_DONT_SCALE,
cgs.media.smokePuffShader );
}
+#endif
// boing sound at origin, jump sound on player
trap_S_StartSound ( cent->lerpOrigin, -1, CHAN_VOICE, cgs.media.jumpPadSound );
- trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "sound/bfp/jump1.wav" ) ); // BFP - Normal jump sound
+ // BFP - Q3 jump sound removed and no break after this case, so continue to BFP jump sound
break;
- case EV_JUMP:
- DEBUGNAME("EV_JUMP");
- trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "sound/bfp/jump1.wav" ) ); // BFP - Normal jump sound
- break;
- case EV_TAUNT:
- DEBUGNAME("EV_TAUNT");
- trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
+ case EV_JUMP: // 36
+ case EV_JUMP_2: // 37
+ // BFP - Use the second jump sound when using ki boost only when it isn't flying
+ if ( ( es->eFlags & EF_AURA ) && !( cent->currentState.eFlags & EF_FLIGHT ) ) {
+ trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "sound/bfp/jump2.wav" ) ); // BFP - Ki boost jump sound
+ } else {
+ trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "sound/bfp/jump1.wav" ) ); // BFP - Normal jump sound
+ }
break;
- case EV_WATER_TOUCH:
- DEBUGNAME("EV_WATER_TOUCH");
+
+
+ case EV_WATER_TOUCH: // 38
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrInSound );
break;
- case EV_WATER_LEAVE:
- DEBUGNAME("EV_WATER_LEAVE");
+
+ case EV_WATER_LEAVE: // 39
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound );
break;
- case EV_WATER_UNDER:
- DEBUGNAME("EV_WATER_UNDER");
+
+ case EV_WATER_UNDER: // 40
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound );
+ // BFP - Bubble and splash particles when entering under water
+ {
+ vec3_t end = {0, 0, 1};
+ vec3_t splashOrigin;
+ float bubbleSize = 2;
+ float bubbleRange = 20;
+ float debrisSize = 3;
+ float velocity = 150;
+ float accel = 250;
+
+ VectorCopy( cent->lerpOrigin, splashOrigin );
+ splashOrigin[2] += 20; // place a bit above
+
+ // that would be the range
+ splashOrigin[0] += (crandom() * 5);
+ splashOrigin[1] += (crandom() * 5);
+
+ // BFP - Monster gamemode, player monster particle size and positions
+ if ( cgs.gametype == GT_MONSTER
+ && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ bubbleSize = 8;
+ bubbleRange = 100;
+ debrisSize = 8;
+ velocity = 400;
+ accel = 700;
+ }
+
+ // Splash!
+ // BFP - NOTE: These are not debris :P
+ CG_ParticleWaterSplash( cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, debrisSize, velocity, accel );
+ CG_ParticleWaterSplash( cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, debrisSize, velocity, accel );
+ CG_ParticleWaterSplash( cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, debrisSize, velocity, accel );
+ CG_ParticleWaterSplash( cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, debrisSize, velocity, accel );
+ CG_ParticleWaterSplash( cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, debrisSize, velocity, accel );
+
+ splashOrigin[2] -= 25; // place a bit below
+ // BFP - Monster gamemode, player monster particle size and positions
+ if ( cgs.gametype == GT_MONSTER
+ && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ splashOrigin[2] -= 100; // place a bit below
+ }
+
+ // Blub, blub, blub...
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, splashOrigin, end, 700, bubbleRange, bubbleSize );
+ }
break;
- case EV_WATER_CLEAR:
- DEBUGNAME("EV_WATER_CLEAR");
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) );
+
+ case EV_WATER_CLEAR: // 41
+ // BFP - No water clear sound
+ // trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) );
break;
- case EV_ITEM_PICKUP:
- DEBUGNAME("EV_ITEM_PICKUP");
+ case EV_ITEM_PICKUP: // 42
{
gitem_t *item;
int index;
@@ -594,6 +753,17 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( index < 1 || index >= bg_numItems ) {
break;
}
+
+ if ( entityNum >= 0 ) {
+ // our predicted entity
+ ce = cg_entities + entityNum;
+ if ( ce->delaySpawn > cg.time && ce->delaySpawnPlayed ) {
+ break; // delay item pickup
+ }
+ } else {
+ ce = NULL;
+ }
+
item = &bg_itemlist[ index ];
// powerups and team items will have a separate global sound, this one
@@ -609,11 +779,14 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( es->number == cg.snap->ps.clientNum ) {
CG_ItemPickup( index );
}
+
+ if ( ce ) {
+ ce->delaySpawnPlayed = qtrue;
+ }
}
break;
- case EV_GLOBAL_ITEM_PICKUP:
- DEBUGNAME("EV_GLOBAL_ITEM_PICKUP");
+ case EV_GLOBAL_ITEM_PICKUP: // 43
{
gitem_t *item;
int index;
@@ -623,6 +796,17 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( index < 1 || index >= bg_numItems ) {
break;
}
+
+ if ( entityNum >= 0 ) {
+ // our predicted entity
+ ce = cg_entities + entityNum;
+ if ( ce->delaySpawn > cg.time && ce->delaySpawnPlayed ) {
+ break;
+ }
+ } else {
+ ce = NULL;
+ }
+
item = &bg_itemlist[ index ];
// powerup pickups are global
if( item->pickup_sound ) {
@@ -633,86 +817,50 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( es->number == cg.snap->ps.clientNum ) {
CG_ItemPickup( index );
}
+
+ if ( ce ) {
+ ce->delaySpawnPlayed = qtrue;
+ }
}
break;
//
// weapon events
//
- case EV_NOAMMO:
- DEBUGNAME("EV_NOAMMO");
+ case EV_NOAMMO: // 44
// trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound );
if ( es->number == cg.snap->ps.clientNum ) {
CG_OutOfAmmoChange();
}
break;
- case EV_CHANGE_WEAPON:
- DEBUGNAME("EV_CHANGE_WEAPON");
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.selectSound );
+
+ case EV_CHANGE_WEAPON: // 45
+ // BFP - Don't play select sound to other players and spectators, only the player itself
+ if ( es->number == cg.snap->ps.clientNum && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) {
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.selectSound );
+ }
break;
- case EV_FIRE_WEAPON:
- DEBUGNAME("EV_FIRE_WEAPON");
+
+ case EV_FIRE_WEAPON: // 46
CG_FireWeapon( cent );
break;
- case EV_USE_ITEM0:
- DEBUGNAME("EV_USE_ITEM0");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM1:
- DEBUGNAME("EV_USE_ITEM1");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM2:
- DEBUGNAME("EV_USE_ITEM2");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM3:
- DEBUGNAME("EV_USE_ITEM3");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM4:
- DEBUGNAME("EV_USE_ITEM4");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM5:
- DEBUGNAME("EV_USE_ITEM5");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM6:
- DEBUGNAME("EV_USE_ITEM6");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM7:
- DEBUGNAME("EV_USE_ITEM7");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM8:
- DEBUGNAME("EV_USE_ITEM8");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM9:
- DEBUGNAME("EV_USE_ITEM9");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM10:
- DEBUGNAME("EV_USE_ITEM10");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM11:
- DEBUGNAME("EV_USE_ITEM11");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM12:
- DEBUGNAME("EV_USE_ITEM12");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM13:
- DEBUGNAME("EV_USE_ITEM13");
- CG_UseItem( cent );
- break;
- case EV_USE_ITEM14:
- DEBUGNAME("EV_USE_ITEM14");
+ case EV_USE_ITEM0: // 47
+ case EV_USE_ITEM1: // 48
+ case EV_USE_ITEM2: // 49
+ case EV_USE_ITEM3: // 50
+ case EV_USE_ITEM4: // 51
+ case EV_USE_ITEM5: // 52
+ case EV_USE_ITEM6: // 53
+ case EV_USE_ITEM7: // 54
+ case EV_USE_ITEM8: // 55
+ case EV_USE_ITEM9: // 56
+ case EV_USE_ITEM10: // 57
+ case EV_USE_ITEM11: // 58
+ case EV_USE_ITEM12: // 59
+ case EV_USE_ITEM13: // 60
+ case EV_USE_ITEM14: // 61
+ case EV_USE_ITEM15: // 62
CG_UseItem( cent );
break;
@@ -721,91 +869,34 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
//
// other events
//
- case EV_PLAYER_TELEPORT_IN:
- DEBUGNAME("EV_PLAYER_TELEPORT_IN");
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound );
- CG_SpawnEffect( position);
+ case EV_ITEM_RESPAWN: // 63
+ cent->miscTime = cg.time; // scale up from this
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound );
break;
- case EV_PLAYER_TELEPORT_OUT:
- DEBUGNAME("EV_PLAYER_TELEPORT_OUT");
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound );
- CG_SpawnEffect( position);
+ case EV_ITEM_POP: // 64
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound );
break;
- case EV_ITEM_POP:
- DEBUGNAME("EV_ITEM_POP");
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound );
+ case EV_PLAYER_TELEPORT_IN: // 65
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound );
+ CG_SpawnEffect( position, cg.snap->ps.clientNum == clientNum && cg_thirdPerson.integer == 0 );
break;
- case EV_ITEM_RESPAWN:
- DEBUGNAME("EV_ITEM_RESPAWN");
- cent->miscTime = cg.time; // scale up from this
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound );
+
+ case EV_PLAYER_TELEPORT_OUT: // 66
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound );
+ CG_SpawnEffect( position, qfalse );
break;
- case EV_GRENADE_BOUNCE:
- DEBUGNAME("EV_GRENADE_BOUNCE");
+ case EV_GRENADE_BOUNCE: // 67
if ( rand() & 1 ) {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb1aSound );
} else {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb2aSound );
}
break;
- case EV_SCOREPLUM:
- DEBUGNAME("EV_SCOREPLUM");
- CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time );
- break;
-
- //
- // missile impacts
- //
- case EV_MISSILE_HIT:
- DEBUGNAME("EV_MISSILE_HIT");
- ByteToDir( es->eventParm, dir );
- CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum );
- break;
-
- case EV_MISSILE_MISS:
- DEBUGNAME("EV_MISSILE_MISS");
- ByteToDir( es->eventParm, dir );
- CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT );
- break;
-
- case EV_MISSILE_MISS_METAL:
- DEBUGNAME("EV_MISSILE_MISS_METAL");
- ByteToDir( es->eventParm, dir );
- CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_METAL );
- break;
-
- case EV_RAILTRAIL:
- DEBUGNAME("EV_RAILTRAIL");
- cent->currentState.weapon = WP_RAILGUN;
- // if the end was on a nomark surface, don't make an explosion
- CG_RailTrail( ci, es->origin2, es->pos.trBase );
- if ( es->eventParm != 255 ) {
- ByteToDir( es->eventParm, dir );
- CG_MissileHitWall( es->weapon, es->clientNum, position, dir, IMPACTSOUND_DEFAULT );
- }
- break;
-
- case EV_BULLET_HIT_WALL:
- DEBUGNAME("EV_BULLET_HIT_WALL");
- ByteToDir( es->eventParm, dir );
- CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD );
- break;
-
- case EV_BULLET_HIT_FLESH:
- DEBUGNAME("EV_BULLET_HIT_FLESH");
- CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm );
- break;
-
- case EV_SHOTGUN:
- DEBUGNAME("EV_SHOTGUN");
- CG_ShotgunFire( es );
- break;
- case EV_GENERAL_SOUND:
- DEBUGNAME("EV_GENERAL_SOUND");
+ case EV_GENERAL_SOUND: // 68
if ( cgs.gameSounds[ es->eventParm ] ) {
trap_S_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] );
} else {
@@ -813,9 +904,8 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) );
}
break;
-
+ // 69
case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes
- DEBUGNAME("EV_GLOBAL_SOUND");
if ( cgs.gameSounds[ es->eventParm ] ) {
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] );
} else {
@@ -823,25 +913,24 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) );
}
break;
-
+ // 70
case EV_GLOBAL_TEAM_SOUND: // play from the player's head so it never diminishes
{
- DEBUGNAME("EV_GLOBAL_TEAM_SOUND");
switch( es->eventParm ) {
case GTS_RED_CAPTURE: // CTF: red team captured the blue flag, 1FCTF: red team captured the neutral flag
- if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
+ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED )
CG_AddBufferedSound( cgs.media.captureYourTeamSound );
else
CG_AddBufferedSound( cgs.media.captureOpponentSound );
break;
case GTS_BLUE_CAPTURE: // CTF: blue team captured the red flag, 1FCTF: blue team captured the neutral flag
- if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
+ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
CG_AddBufferedSound( cgs.media.captureYourTeamSound );
else
CG_AddBufferedSound( cgs.media.captureOpponentSound );
break;
case GTS_RED_RETURN: // CTF: blue flag returned, 1FCTF: never used
- if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
+ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED )
CG_AddBufferedSound( cgs.media.returnYourTeamSound );
else
CG_AddBufferedSound( cgs.media.returnOpponentSound );
@@ -849,7 +938,7 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
CG_AddBufferedSound( cgs.media.blueFlagReturnedSound );
break;
case GTS_BLUE_RETURN: // CTF red flag returned, 1FCTF: neutral flag returned
- if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
+ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
CG_AddBufferedSound( cgs.media.returnYourTeamSound );
else
CG_AddBufferedSound( cgs.media.returnOpponentSound );
@@ -859,40 +948,29 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
case GTS_RED_TAKEN: // CTF: red team took blue flag, 1FCTF: blue team took the neutral flag
// if this player picked up the flag then a sound is played in CG_CheckLocalSounds
- if (cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) {
+ if ( cg.snap->ps.powerups[PW_BLUEFLAG] ) {
}
else {
- if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) {
+ if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE) {
CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound );
}
- else if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) {
+ else if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED) {
CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound );
}
}
break;
case GTS_BLUE_TAKEN: // CTF: blue team took the red flag, 1FCTF red team took the neutral flag
// if this player picked up the flag then a sound is played in CG_CheckLocalSounds
- if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) {
- }
- else {
- if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) {
+ if ( cg.snap->ps.powerups[PW_REDFLAG] ) {
+ } else {
+ if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED) {
CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound );
}
- else if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) {
+ else if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE) {
CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound );
}
}
break;
- case GTS_REDOBELISK_ATTACKED: // Overload: red obelisk is being attacked
- if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) {
- CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound );
- }
- break;
- case GTS_BLUEOBELISK_ATTACKED: // Overload: blue obelisk is being attacked
- if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) {
- CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound );
- }
- break;
case GTS_REDTEAM_SCORED:
CG_AddBufferedSound(cgs.media.redScoredSound);
@@ -915,85 +993,168 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
break;
}
- case EV_PAIN:
+ case EV_BULLET_HIT_FLESH: // 71
+ CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm );
+ break;
+
+ case EV_BULLET_HIT_WALL: // 72
+ ByteToDir( es->eventParm, dir );
+ CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD );
+ break;
+
+ //
+ // missile impacts
+ //
+ case EV_MISSILE_HIT: // 73
+ ByteToDir( es->eventParm, dir );
+ CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum );
+ CG_ResetTrail( BEAM_TRAIL, es->number, es->origin ); // BFP - Reset beam trail
+ break;
+
+ case EV_MISSILE_MISS: // 74
+ ByteToDir( es->eventParm, dir );
+ CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT );
+ if ( es->weapon != WP_MACHINEGUN && es->weapon != WP_SHOTGUN ) { // BFP - Avoid exploding using finger blast type thingies
+ // BFP - Debris particles explosion
+ CG_DebrisExplosion( position, dir );
+ // BFP - Spark particles explosion
+ CG_SparksExplosion( position, dir );
+ }
+ CG_ResetTrail( BEAM_TRAIL, es->number, es->origin ); // BFP - Reset beam trail
+ break;
+
+ case EV_MISSILE_MISS_METAL: // 75
+ ByteToDir( es->eventParm, dir );
+ CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_METAL );
+ // BFP - Debris particles explosion
+ CG_DebrisExplosion( position, dir );
+ // BFP - Spark particles explosion
+ CG_SparksExplosion( position, dir );
+ CG_ResetTrail( BEAM_TRAIL, es->number, es->origin ); // BFP - Reset beam trail
+ break;
+
+ // BFP - EV_MISSILE_DETONATE - used on ki grenade bounces and beams,
+ // that happens when projectiles/beams reaches their lifetime limit or are stopped by the player actions
+ // no debris and sparks particles here
+ case EV_MISSILE_DETONATE: // 76
+ {
+ vec3_t dirDetonate = {0, 0, 1}; // place the explosion position and size correctly
+ CG_MissileHitWall( es->weapon, 0, position, dirDetonate, IMPACTSOUND_DEFAULT );
+ CG_ResetTrail( BEAM_TRAIL, es->number, es->origin ); // BFP - Reset beam trail
+ }
+ break;
+
+ case EV_RAILTRAIL: // 77
+ cent->currentState.weapon = WP_RAILGUN;
+ // if the end was on a nomark surface, don't make an explosion
+ CG_RailTrail( ci, es->origin2, es->pos.trBase );
+ ByteToDir( es->eventParm, dir );
+ break;
+
+ case EV_SHOTGUN: // 78
+ // BFP - No shotgun fire, just force field test
+ // CG_ShotgunFire( es );
+ break;
+
+ case EV_UNUSED_INDEX79: // 79
+ break;
+
+ case EV_PAIN: // 80
// local player sounds are triggered in CG_CheckLocalSounds,
// so ignore events on the player
- DEBUGNAME("EV_PAIN");
if ( cent->currentState.number != cg.snap->ps.clientNum ) {
CG_PainEvent( cent, es->eventParm );
}
break;
- case EV_DEATH1:
- case EV_DEATH2:
- case EV_DEATH3:
- DEBUGNAME("EV_DEATHx");
+ case EV_DEATH1: // 81
+ case EV_DEATH2: // 82
+ case EV_DEATH3: // 83
trap_S_StartSound( NULL, es->number, CHAN_VOICE,
CG_CustomSound( es->number, va("*death%i.wav", event - EV_DEATH1 + 1) ) );
break;
-
- case EV_OBITUARY:
- DEBUGNAME("EV_OBITUARY");
+ case EV_OBITUARY: // 84
CG_Obituary( es );
break;
//
// powerup events
//
- case EV_POWERUP_QUAD:
- DEBUGNAME("EV_POWERUP_QUAD");
+ case EV_POWERUP_QUAD: // 85
if ( es->number == cg.snap->ps.clientNum ) {
cg.powerupActive = PW_QUAD;
cg.powerupTime = cg.time;
}
trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.quadSound );
break;
- case EV_POWERUP_BATTLESUIT:
- DEBUGNAME("EV_POWERUP_BATTLESUIT");
+
+ case EV_POWERUP_BATTLESUIT: // 86
+ // BFP - No battlesuit powerup
+#if 0
if ( es->number == cg.snap->ps.clientNum ) {
cg.powerupActive = PW_BATTLESUIT;
cg.powerupTime = cg.time;
}
+#endif
trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.protectSound );
break;
- case EV_POWERUP_REGEN:
- DEBUGNAME("EV_POWERUP_REGEN");
- if ( es->number == cg.snap->ps.clientNum ) {
- cg.powerupActive = PW_REGEN;
- cg.powerupTime = cg.time;
- }
- trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.regenSound );
+
+ case EV_UNUSED_INDEX87: // 87
break;
- case EV_GIB_PLAYER:
- DEBUGNAME("EV_GIB_PLAYER");
- // don't play gib sound when using the kamikaze because it interferes
- // with the kamikaze sound, downside is that the gib sound will also
- // not be played when someone is gibbed while just carrying the kamikaze
- if ( !(es->eFlags & EF_KAMIKAZE) ) {
- trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound );
- }
+ case EV_GIB_PLAYER: // 88
+ trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound );
CG_GibPlayer( cent->lerpOrigin );
break;
- case EV_STOPLOOPINGSOUND:
- DEBUGNAME("EV_STOPLOOPINGSOUND");
+ case EV_UNUSED_INDEX89: // 89
+ case EV_UNUSED_INDEX90: // 90
+ break;
+
+ case EV_SCOREPLUM: // 91
+ CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time );
+ break;
+
+ case EV_TAUNT: // 92
+ trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
+ break;
+
+ case EV_UNUSED_INDEX93: // 93
+ case EV_UNUSED_INDEX94: // 94
+ case EV_UNUSED_INDEX95: // 95
+ break;
+
+ // BFP - Blind
+ case EV_BLINDING: // 96
+ if ( es->number == cg.snap->ps.clientNum
+ && ( !cg.blindLastAttackTime || cg.time - cg.blindLastAttackTime > 4000 ) ) {
+ cg.blind = qtrue;
+ cg.blindStartTime = cg.time;
+ cg.blindLastAttackTime = cg.time;
+ }
+ break;
+
+ case EV_DEBUG_LINE: // 97
+ CG_Beam( cent );
+ break;
+
+ case EV_STOPLOOPINGSOUND: // 98
trap_S_StopLoopingSound( es->number );
es->loopSound = 0;
break;
- case EV_DEBUG_LINE:
- DEBUGNAME("EV_DEBUG_LINE");
- CG_Beam( cent );
+ // BFP - Spark and beam struggle effect
+ case EV_SPARK: // 99
+ ByteToDir( es->eventParm, dir );
+ // BFP - Beam struggle effect
+ CG_BeamStruggleEffect( position, dir );
break;
default:
- DEBUGNAME("UNKNOWN");
CG_Error( "Unknown event: %i", event );
break;
}
-
}
@@ -1032,6 +1193,6 @@ void CG_CheckEvents( centity_t *cent ) {
BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin );
CG_SetEntitySoundPosition( cent );
- CG_EntityEvent( cent, cent->lerpOrigin );
+ CG_EntityEvent( cent, cent->lerpOrigin, -1 );
}
diff --git a/source/cgame/cg_info.c b/source/cgame/cg_info.c
index 1e1dd9c..7271326 100644
--- a/source/cgame/cg_info.c
+++ b/source/cgame/cg_info.c
@@ -114,10 +114,6 @@ void CG_LoadingClient( int clientNum ) {
Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", model, skin );
loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName );
- if ( !loadingPlayerIcons[loadingPlayerIconCount] ) {
- Com_sprintf( iconName, MAX_QPATH, "models/players/characters/%s/icon_%s.tga", model, skin );
- loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName );
- }
if ( !loadingPlayerIcons[loadingPlayerIconCount] ) {
Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", DEFAULT_MODEL, "default" );
loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName );
@@ -127,8 +123,8 @@ void CG_LoadingClient( int clientNum ) {
}
}
- Q_strncpyz( personality, Info_ValueForKey( info, "n" ), sizeof(personality) );
- Q_CleanStr( personality );
+ BG_CleanName( Info_ValueForKey( info, "n" ), personality, sizeof( personality ), "unknown client" );
+ BG_StripColor( personality );
if( cgs.gametype == GT_SINGLE_PLAYER ) {
trap_S_RegisterSound( va( "sound/player/announce/%s.wav", personality ), qtrue );
@@ -233,6 +229,22 @@ void CG_DrawInformation( void ) {
y += PROP_HEIGHT;
}
+ // BFP - Melee only
+ s = Info_ValueForKey( info, "g_meleeOnly" );
+ if ( atoi( s ) > 0 ) {
+ UI_DrawProportionalString( 320, y, "MELEE ONLY",
+ UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
+ y += PROP_HEIGHT;
+ }
+
+ // BFP - No flight
+ s = Info_ValueForKey( info, "g_noFlight" );
+ if ( atoi( s ) > 0 ) {
+ UI_DrawProportionalString( 320, y, "NO FLIGHT",
+ UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
+ y += PROP_HEIGHT;
+ }
+
// game type
switch ( cgs.gametype ) {
case GT_FFA:
@@ -244,9 +256,18 @@ void CG_DrawInformation( void ) {
case GT_TOURNAMENT:
s = "Tournament";
break;
+ case GT_SURVIVAL: // BFP - Survival
+ s = "Survival";
+ break;
+ case GT_MONSTER: // BFP - Monster
+ s = "Monster";
+ break;
case GT_TEAM:
s = "Team Deathmatch";
break;
+ case GT_TLMS: // BFP - Team Last Man Standing
+ s = "Team Last Man Standing";
+ break;
case GT_CTF:
s = "Capture The Flag";
break;
diff --git a/source/cgame/cg_local.h b/source/cgame/cg_local.h
index d46ecab..6a488d2 100644
--- a/source/cgame/cg_local.h
+++ b/source/cgame/cg_local.h
@@ -79,12 +79,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define TEAM_OVERLAY_MAXNAME_WIDTH 12
#define TEAM_OVERLAY_MAXLOCATION_WIDTH 16
-#define DEFAULT_MODEL "sarge"
-#define DEFAULT_TEAM_MODEL "sarge"
-#define DEFAULT_TEAM_HEAD "sarge"
+#define DEFAULT_MODEL "bfp1-kyah" // BFP - Before Q3: "sarge"
+#define DEFAULT_TEAM_MODEL "bfp1-kyah" // BFP - Before Q3: "sarge"
+#define DEFAULT_TEAM_HEAD "bfp1-kyah" // BFP - Before Q3: "sarge"
-#define DEFAULT_REDTEAM_NAME "Stroggs"
-#define DEFAULT_BLUETEAM_NAME "Pagans"
+#define DEFAULT_REDTEAM_NAME "Warriors" // BFP - Before Q3: "Stroggs"
+#define DEFAULT_BLUETEAM_NAME "Fighters" // BFP - Before Q3: "Pagans"
typedef enum {
FOOTSTEP_NORMAL,
@@ -139,16 +139,31 @@ typedef struct {
lerpFrame_t legs, torso, flag;
int painTime;
int painDirection; // flip from 0 to 1
- int lightningFiring;
+ //int lightningFiring;
+ int kiTrailTime; // BFP - Ki trail time
+ int chargeSmokeTime; // BFP - Charge smoke time
+ int ultTierTransformTime; // BFP - Ultimate tier transformation time
+
+ vec3_t muzzleOrigin; // BFP - Muzzle origin
// railgun trail spawning
vec3_t railgunImpact;
qboolean railgunFlash;
+ // BFP - No machinegun spinning
// machinegun spinning
- float barrelAngle;
- int barrelTime;
- qboolean barrelSpinning;
+ //float barrelAngle;
+ //int barrelTime;
+ //qboolean barrelSpinning;
+
+ int forceFieldStartTime; // BFP - Forcefield start time
+
+ // BFP - TODO: Handle chargeAttack and chargeAutoFire for forcefield attack
+ qboolean chargeAutoFire;
+
+ qboolean constantFireAtkPlayed; // BFP - To play constantFireAttack fire sound once
+
+ qboolean lastChargeVoiceLevel; // BFP - To play charge voice once
} playerEntity_t;
//=================================================
@@ -171,6 +186,9 @@ typedef struct centity_s {
int dustTrailTime;
int miscTime;
+ int delaySpawn;
+ qboolean delaySpawnPlayed;
+
int snapShotTime; // last time this entity was found in a snapshot
playerEntity_t pe;
@@ -210,9 +228,13 @@ typedef struct markPoly_s {
typedef enum {
LE_MARK,
LE_EXPLOSION,
+ LE_EXPLOSION_SPHERE, // BFP - Explosion sphere
+ LE_EXPLOSION_RING, // BFP - Explosion ring
+ LE_EXPLOSION_SHELL, // BFP - Explosion shell
LE_SPRITE_EXPLOSION,
LE_FRAGMENT,
LE_MOVE_SCALE_FADE,
+ LE_MOVE_DONT_SCALE_FADE, // BFP - Effect for explosion smoke
LE_FALL_SCALE_FADE,
LE_FADE_RGB,
LE_SCALE_FADE,
@@ -221,9 +243,7 @@ typedef enum {
typedef enum {
LEF_PUFF_DONT_SCALE = 0x0001, // do not scale size over time
- LEF_TUMBLE = 0x0002, // tumble over time, used for ejecting shells
- LEF_SOUND1 = 0x0004, // sound 1 for kamikaze
- LEF_SOUND2 = 0x0008 // sound 2 for kamikaze
+ LEF_TUMBLE = 0x0002 // tumble over time, used for ejecting shells
} leFlag_t;
typedef enum {
@@ -303,8 +323,8 @@ typedef struct {
int botSkill; // 0 = not bot, 1-5 = bot
- vec3_t color1;
- vec3_t color2;
+ // BFP - No color1
+ // vec3_t color1;
int score; // updated by score servercmds
int location; // location index for team mode
@@ -315,9 +335,6 @@ typedef struct {
int handicap;
int wins, losses; // in tourney mode
- int teamTask; // task in teamplay (offence/defence)
- qboolean teamLeader; // true when this is a team leader
-
int powerups; // so can display quad/flag status
int medkitUsageTime;
@@ -333,8 +350,7 @@ typedef struct {
char skinName[MAX_QPATH];
char headModelName[MAX_QPATH];
char headSkinName[MAX_QPATH];
- char redTeam[MAX_TEAMNAME];
- char blueTeam[MAX_TEAMNAME];
+
qboolean deferred;
qboolean newAnims; // true if using the new mission pack animations
@@ -354,6 +370,14 @@ typedef struct {
qhandle_t headModel;
qhandle_t headSkin;
+ // BFP - Ultimate tier models and skins
+ qhandle_t ultTierLegsModel;
+ qhandle_t ultTierLegsSkin;
+ qhandle_t ultTierTorsoModel;
+ qhandle_t ultTierTorsoSkin;
+ qhandle_t ultTierHeadModel;
+ qhandle_t ultTierHeadSkin;
+
qhandle_t modelIcon;
animation_t animations[MAX_TOTALANIMATIONS];
@@ -369,39 +393,59 @@ typedef struct weaponInfo_s {
qboolean registered;
gitem_t *item;
- qhandle_t handsModel; // the hands don't actually draw, they just position the weapon
- qhandle_t weaponModel;
- qhandle_t barrelModel;
+ // BFP - No handsModel, weaponModel and barrelModel
+ //qhandle_t handsModel; // the hands don't actually draw, they just position the weapon
+ //qhandle_t weaponModel;
+ //qhandle_t barrelModel;
qhandle_t flashModel;
vec3_t weaponMidpoint; // so it will rotate centered instead of by tag
float flashDlight;
vec3_t flashDlightColor;
+ // BFP - Indication here: flashSound
sfxHandle_t flashSound[4]; // fast firing weapons randomly choose
- qhandle_t weaponIcon;
- qhandle_t ammoIcon;
+ sfxHandle_t attackChargeVoice; // BFP - attackChargeVoice
+ sfxHandle_t attackFireVoice; // BFP - attackFireVoice
+
+ qhandle_t weaponIcon; // BFP - Indication here: attackIcon
+ // BFP - No ammoIcon
+ //qhandle_t ammoIcon;
qhandle_t ammoModel;
- qhandle_t missileModel;
- sfxHandle_t missileSound;
+ qhandle_t missileModel; // BFP - Indication here: missileModel
+ qhandle_t missileShader; // BFP - For missileShader
+ int missileRotation; // BFP - For missileRotation
+ sfxHandle_t missileSound; // BFP - Indication here: missileSound
void (*missileTrailFunc)( centity_t *, const struct weaponInfo_s *wi );
- float missileDlight;
- vec3_t missileDlightColor;
+ int missileTrailFuncType; // BFP - Indication here: missileTrailFunc, for MISSILE_TRAIL_FUNC_* macro types
+ float missileDlight; // BFP - Indication here: missileDlight
+ vec3_t missileDlightColor; // BFP - Indication here: missileDlightColor
int missileRenderfx;
+ qboolean constantFireAttack; // BFP - constantFireAttack
+
void (*ejectBrassFunc)( centity_t * );
- float trailRadius;
- float wiTrailTime;
+ float trailRadius; // BFP - Indication here: missileTrailRadius
+ float wiTrailTime; // BFP - Indication here: missileTrailTime
+
+ qboolean noExplosion; // BFP - noExplosion
sfxHandle_t readySound;
- sfxHandle_t firingSound;
+ sfxHandle_t firingSound; // BFP - Indication here: firingSound
qboolean loopFireSound;
+
+ sfxHandle_t chargeSound; // BFP - Charge sound
} weaponInfo_t;
+// BFP - missileTrailFunc types
+#define MISSILE_TRAIL_FUNC_BEAM 1 // "beam"
+#define MISSILE_TRAIL_FUNC_ROCKET 2 // "rocket"
+#define MISSILE_TRAIL_FUNC_SPIRALBEAM 3 // "spiralbeam"
+// "none" can be any value, anyway it's nothing
// each IT_* item has an associated itemInfo_t
// that constains media references necessary to present the
@@ -418,14 +462,6 @@ typedef struct {
} powerupInfo_t;
-#define MAX_SKULLTRAIL 10
-
-typedef struct {
- vec3_t positions[MAX_SKULLTRAIL];
- int numpositions;
-} skulltrail_t;
-
-
#define MAX_REWARDSTACK 10
#define MAX_SOUNDBUFFER 20
@@ -535,9 +571,6 @@ typedef struct {
int spectatorOffset; // current offset from start
int spectatorPaintLen; // current offset from start
- // skull trails
- skulltrail_t skulltrails[MAX_CLIENTS];
-
// centerprinting
int centerPrintTime;
int centerPrintCharWidth;
@@ -627,6 +660,10 @@ typedef struct {
//qboolean cameraMode; // if rendering from a loaded camera
+ // BFP - Blind
+ qboolean blind;
+ int blindStartTime; // when the current blind effect started
+ int blindLastAttackTime; // last time the player was 'blind attacked'
// development tool
refEntity_t testModelEntity;
@@ -647,13 +684,8 @@ typedef struct {
qhandle_t charsetPropB;
qhandle_t whiteShader;
- qhandle_t redCubeModel;
- qhandle_t blueCubeModel;
- qhandle_t redCubeIcon;
- qhandle_t blueCubeIcon;
qhandle_t redFlagModel;
qhandle_t blueFlagModel;
- qhandle_t neutralFlagModel;
qhandle_t redFlagShader[3];
qhandle_t blueFlagShader[3];
qhandle_t flagShader[4];
@@ -663,11 +695,9 @@ typedef struct {
qhandle_t redFlagFlapSkin;
qhandle_t blueFlagFlapSkin;
- qhandle_t neutralFlagFlapSkin;
qhandle_t redFlagBaseModel;
qhandle_t blueFlagBaseModel;
- qhandle_t neutralFlagBaseModel;
qhandle_t armorModel;
qhandle_t armorIcon;
@@ -690,9 +720,6 @@ typedef struct {
qhandle_t smoke2;
- qhandle_t machinegunBrassModel;
- qhandle_t shotgunBrassModel;
-
qhandle_t railRingsShader;
qhandle_t railCoreShader;
@@ -707,10 +734,14 @@ typedef struct {
qhandle_t viewBloodShader;
qhandle_t tracerShader;
qhandle_t crosshairShader[NUM_CROSSHAIRS];
+ qhandle_t redCrosshairShader[NUM_CROSSHAIRS]; // BFP - BFP crosshair health feature
qhandle_t lagometerShader;
qhandle_t backTileShader;
qhandle_t noammoShader;
+ // BFP - Ki attack charge up points
+ qhandle_t chargeupbuttgreen;
+
qhandle_t smokePuffShader;
qhandle_t smokePuffRageProShader;
qhandle_t shotgunSmokePuffShader;
@@ -724,6 +755,12 @@ typedef struct {
qhandle_t botSkillShaders[5];
+ // BFP - HUD overlay
+ qhandle_t hudoverlay;
+ qhandle_t hudoverlayr;
+
+ // BFP - NOTE: These mark shaders are unused, they didn't remove. Remove them in the future?
+
// wall mark shaders
qhandle_t wakeMarkShader;
qhandle_t bloodMarkShader;
@@ -732,6 +769,9 @@ typedef struct {
qhandle_t holeMarkShader;
qhandle_t energyMarkShader;
+ // BFP - Crack mark shader
+ qhandle_t crackMarkShader;
+
// powerup shaders
qhandle_t quadShader;
qhandle_t redQuadShader;
@@ -750,6 +790,18 @@ typedef struct {
qhandle_t dishFlashModel;
qhandle_t lightningExplosionModel;
+ // BFP - Explosion, projectile and beam models
+ qhandle_t nukeModel;
+ qhandle_t lowPolySphereModel;
+ qhandle_t highPolySphereModel;
+ qhandle_t beamModel;
+
+ // BFP - Explosion shell shader
+ qhandle_t explosionShellShader;
+
+ // BFP - Explosion shader tests
+ qhandle_t ImpactBeamExplosionShader;
+
// weapon effect shaders
qhandle_t railExplosionShader;
qhandle_t plasmaExplosionShader;
@@ -763,13 +815,48 @@ typedef struct {
qhandle_t teleportEffectModel;
qhandle_t teleportEffectShader;
- qhandle_t invulnerabilityPowerupModel;
+ // BFP - No invulnerability powerup
+// qhandle_t invulnerabilityPowerupModel;
- qhandle_t auraModel; // BFP - Aura model
+ // BFP - Particle shaders
+ qhandle_t pebbleShader1;
+ qhandle_t pebbleShader2;
+ qhandle_t pebbleShader3;
+ qhandle_t particleSmokeShader;
+ qhandle_t sparkShader1;
+ qhandle_t sparkShader2;
+
+ // BFP - Optional 3D pebble/rock models
+ qhandle_t pebbleMdl1;
+ qhandle_t pebbleMdl2;
+ qhandle_t pebbleMdl3;
+
+ // BFP - Aura models
+ qhandle_t auraModel; // BFP - Normal aura model
qhandle_t backauraModel; // BFP - Back aura model
qhandle_t flyauraModel; // BFP - Fly aura model
qhandle_t runauraModel; // BFP - Run aura model
- qhandle_t auraEffectShader; // BFP - Aura shader
+ // BFP - Aura shaders
+ qhandle_t auraRedTinyShader;
+ qhandle_t auraRedChargeShader;
+ qhandle_t auraRedUseShader;
+ qhandle_t auraBlueTinyShader;
+ qhandle_t auraBlueChargeShader;
+ qhandle_t auraBlueUseShader;
+ qhandle_t auraYellowTinyShader;
+ qhandle_t auraYellowChargeShader;
+ qhandle_t auraYellowUseShader;
+ qhandle_t ultimateAuraShader;
+
+ // BFP - Ki trail shaders
+ qhandle_t kiTrailRedShader;
+ qhandle_t kiTrailBlueShader;
+ qhandle_t kiTrailYellowShader;
+
+ // BFP - Ki attack beam and projectile shaders (for testing purposes)
+ qhandle_t SSBSpiralShader;
+ qhandle_t SSBBeamShader;
+ qhandle_t PowerWaveBeamShader;
// scoreboard headers
qhandle_t scoreboardName;
@@ -778,12 +865,31 @@ typedef struct {
qhandle_t scoreboardTime;
// medals shown during gameplay
- qhandle_t medalImpressive;
qhandle_t medalExcellent;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+/*
+ qhandle_t medalImpressive;
qhandle_t medalGauntlet;
qhandle_t medalDefend;
qhandle_t medalAssist;
qhandle_t medalCapture;
+*/
+
+ // BFP - Monster models, animations, skins and icon
+ qhandle_t monsterLegsModel;
+ qhandle_t monsterLegsSkin;
+ qhandle_t monsterTorsoModel;
+ qhandle_t monsterTorsoSkin;
+ qhandle_t monsterHeadModel;
+ qhandle_t monsterHeadSkin;
+ qhandle_t monsterUltTierLegsModel;
+ qhandle_t monsterUltTierLegsSkin;
+ qhandle_t monsterUltTierTorsoModel;
+ qhandle_t monsterUltTierTorsoSkin;
+ qhandle_t monsterUltTierHeadModel;
+ qhandle_t monsterUltTierHeadSkin;
+ qhandle_t monsterModelIcon;
+ animation_t monsterAnimations[MAX_TOTALANIMATIONS];
// sounds
sfxHandle_t quadSound;
@@ -827,15 +933,19 @@ typedef struct {
sfxHandle_t hitSoundHighArmor;
sfxHandle_t hitSoundLowArmor;
sfxHandle_t hitTeamSound;
- sfxHandle_t impressiveSound;
sfxHandle_t excellentSound;
+ sfxHandle_t firstExcellentSound;
+
+ // BFP - No impressive, gauntlet, defend and assist sounds
+/*
+ sfxHandle_t impressiveSound;
sfxHandle_t deniedSound;
sfxHandle_t humiliationSound;
sfxHandle_t assistSound;
sfxHandle_t defendSound;
sfxHandle_t firstImpressiveSound;
- sfxHandle_t firstExcellentSound;
sfxHandle_t firstHumiliationSound;
+*/
sfxHandle_t takenLeadSound;
sfxHandle_t tiedLeadSound;
@@ -849,7 +959,8 @@ typedef struct {
sfxHandle_t watrOutSound;
sfxHandle_t watrUnSound;
- sfxHandle_t flightSound;
+ // BFP - No flight powerup sound
+// sfxHandle_t flightSound;
sfxHandle_t medkitSound;
sfxHandle_t weaponHoverSound;
@@ -871,13 +982,11 @@ typedef struct {
sfxHandle_t redFlagReturnedSound;
sfxHandle_t blueFlagReturnedSound;
- sfxHandle_t neutralFlagReturnedSound;
sfxHandle_t enemyTookYourFlagSound;
sfxHandle_t enemyTookTheFlagSound;
sfxHandle_t yourTeamTookEnemyFlagSound;
sfxHandle_t yourTeamTookTheFlagSound;
sfxHandle_t youHaveFlagSound;
- sfxHandle_t yourBaseIsUnderAttackSound;
sfxHandle_t holyShitSound;
// tournament sounds
@@ -891,7 +1000,8 @@ typedef struct {
qhandle_t selectCursor;
qhandle_t sizeCursor;
- sfxHandle_t regenSound;
+ // BFP - No regen sound
+// sfxHandle_t regenSound;
sfxHandle_t protectSound;
sfxHandle_t n_healthSound;
sfxHandle_t hgrenb1aSound;
@@ -901,9 +1011,24 @@ typedef struct {
sfxHandle_t wstbimpdSound;
sfxHandle_t wstbactvSound;
+ // BFP - Tier up sound
+ sfxHandle_t tierUpSound;
+
+ // BFP - Explosion sounds
+ sfxHandle_t explosion1Sound;
+ sfxHandle_t explosion2Sound;
+ sfxHandle_t explosion3Sound;
+ sfxHandle_t explosion4Sound;
+ sfxHandle_t explosion5Sound;
+ sfxHandle_t explosion6Sound;
+
sfxHandle_t kiChargeSound; // BFP - Ki charge sound
sfxHandle_t kiUseSound; // BFP - Ki use sound
+ sfxHandle_t defaultKiBeamExplosionWaveSound; // BFP - Default ki beam and ki explosion wave sound
+ sfxHandle_t defaultKiFiringAttackSound; // BFP - Default ki firing sound
+ sfxHandle_t defaultKiChargingSound; // BFP - Default ki attack charging sound
+ sfxHandle_t diskKiChargingSound; // BFP - Disk ki attack charging sound
} cgMedia_t;
@@ -917,6 +1042,7 @@ typedef struct {
float screenXScale; // derived from glconfig
float screenYScale;
float screenXBias;
+ float screenYBias;
int serverCommandSequence; // reliable command stream counter
int processedSnapshotNum;// the number of snapshots cgame has requested
@@ -931,6 +1057,8 @@ typedef struct {
int capturelimit;
int timelimit;
int maxclients;
+ // BFP - Monster gamemode, to get g_monster g_cvar
+ int monster;
char mapname[MAX_QPATH];
char redTeam[MAX_QPATH];
char blueTeam[MAX_QPATH];
@@ -1010,6 +1138,8 @@ extern markPoly_t cg_markPolys[MAX_MARK_POLYS];
#include "cg_cvar.h"
#undef EXTERN_CG_CVAR
+extern const char *eventnames[EV_MAX];
+
//
// cg_main.c
//
@@ -1031,8 +1161,8 @@ void CG_MouseEvent(int x, int y);
void CG_EventHandling(int type);
void CG_RankRunFrame( void );
void CG_SetScoreSelection(void *menu);
-score_t *CG_GetSelectedScore();
-void CG_BuildSpectatorString();
+score_t *CG_GetSelectedScore( void );
+void CG_BuildSpectatorString( void );
//
@@ -1060,8 +1190,7 @@ void CG_AdjustFrom640( float *x, float *y, float *w, float *h );
void CG_FillRect( float x, float y, float width, float height, const float *color );
void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader );
void CG_DrawString( float x, float y, const char *string,
- float charWidth, float charHeight, const float *modulate );
-
+ const vec4_t setColor, float charWidth, float charHeight, int maxChars, int flags );
void CG_DrawStringExt( int x, int y, const char *string, const float *setColor,
qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars );
@@ -1073,7 +1202,7 @@ void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color );
int CG_DrawStrlen( const char *str );
float *CG_FadeColor( int startMsec, int totalMsec );
-float *CG_TeamColor( int team );
+const float *CG_TeamColor( team_t team );
void CG_TileClear( void );
void CG_ColorForHealth( vec4_t hcolor );
void CG_GetColorForHealth( int health, int armor, vec4_t hcolor );
@@ -1083,6 +1212,21 @@ void CG_DrawRect( float x, float y, float width, float height, float size, const
void CG_DrawSides(float x, float y, float w, float h, float size);
void CG_DrawTopBottom(float x, float y, float w, float h, float size);
+#define USE_NEW_FONT_RENDERER
+
+// flags for CG_DrawString
+enum {
+ DS_SHADOW = 0x1,
+ DS_FORCE_COLOR = 0x2,
+ DS_PROPORTIONAL = 0x4,
+ DS_CENTER = 0x8, // alignment
+ DS_RIGHT = 0x10 // alignment
+};
+#ifdef USE_NEW_FONT_RENDERER
+void CG_LoadFonts( void );
+void CG_SelectFont( int index );
+#endif
+
//
// cg_draw.c, cg_newDraw.c
@@ -1105,23 +1249,23 @@ void CG_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y
void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style);
int CG_Text_Width(const char *text, float scale, int limit);
int CG_Text_Height(const char *text, float scale, int limit);
-void CG_SelectPrevPlayer();
-void CG_SelectNextPlayer();
+void CG_SelectPrevPlayer( void );
+void CG_SelectNextPlayer( void );
float CG_GetValue(int ownerDraw);
qboolean CG_OwnerDrawVisible(int flags);
void CG_RunMenuScript(char **args);
-void CG_ShowResponseHead();
+void CG_ShowResponseHead( void );
void CG_SetPrintString(int type, const char *p);
-void CG_InitTeamChat();
+void CG_InitTeamChat( void );
void CG_GetTeamColor(vec4_t *color);
-const char *CG_GetGameStatusText();
-const char *CG_GetKillerText();
+const char *CG_GetGameStatusText( void );
+const char *CG_GetKillerText( void );
void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles );
void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader);
-void CG_CheckOrderPending();
-const char *CG_GameTypeString();
-qboolean CG_YourTeamHasFlag();
-qboolean CG_OtherTeamHasFlag();
+void CG_CheckOrderPending( void );
+const char *CG_GameTypeString( void );
+qboolean CG_YourTeamHasFlag( void );
+qboolean CG_OtherTeamHasFlag( void );
qhandle_t CG_StatusHandle(int task);
@@ -1129,13 +1273,16 @@ qhandle_t CG_StatusHandle(int task);
//
// cg_player.c
//
+// BFP - Monster gamemode, those 2 functions are used for g_monster to load monster model
+qboolean CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext );
+qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci );
+
+void CG_ModelSize( refEntity_t *model, float size ); // BFP - Model size
void CG_Player( centity_t *cent );
void CG_ResetPlayerEntity( centity_t *cent );
-void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team );
+void CG_AddRefEntityWithPowerups( refEntity_t re, entityState_t *state, int team );
void CG_NewClientInfo( int clientNum );
sfxHandle_t CG_CustomSound( int clientNum, const char *soundName );
-qboolean CG_GetTagOrientationFromPlayerEntityParentModel( centity_t *cent, refEntity_t *parent,
- qhandle_t parentModel, char *tagName, orientation_t *tagOrient ); // BFP - Parent model tag orientation, used for first person vis mode
//
// cg_predict.c
@@ -1153,7 +1300,7 @@ void CG_LoadDeferredPlayers( void );
//
void CG_CheckEvents( centity_t *cent );
const char *CG_PlaceString( int rank );
-void CG_EntityEvent( centity_t *cent, vec3_t position );
+void CG_EntityEvent( centity_t *cent, vec3_t position, int entityNum );
void CG_PainEvent( centity_t *cent, int health );
@@ -1190,8 +1337,9 @@ void CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean fles
void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end );
void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi );
+void CG_BFPBeamTrail( centity_t *ent, const weaponInfo_t *wi ); // BFP - BFP Beam trail handling
void CG_AddViewWeapon (playerState_t *ps);
-void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team );
+void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team, char *tagName );
void CG_DrawWeaponSelect( void );
void CG_OutOfAmmoChange( void ); // should this be in pmove?
@@ -1228,7 +1376,7 @@ localEntity_t *CG_SmokePuff( const vec3_t p,
int leFlags,
qhandle_t hShader );
void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing );
-void CG_SpawnEffect( vec3_t org );
+refEntity_t *CG_SpawnEffect( const vec3_t origin, qboolean firstPerson );
void CG_ScorePlum( int client, vec3_t org, int score );
void CG_GibPlayer( vec3_t playerOrigin );
@@ -1239,6 +1387,38 @@ void CG_Bleed( vec3_t origin, int entityNum );
localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir,
qhandle_t hModel, qhandle_t shader, int msec,
qboolean isSprite );
+// BFP - Explosion models
+localEntity_t *CG_SpawnExplosionModel( vec3_t origin, vec3_t dir, leType_t type, qhandle_t hModel, qhandle_t shader, float duration );
+// BFP - Debris particles explosion
+void CG_DebrisExplosion( vec3_t origin, vec3_t dir );
+// BFP - Spark particles explosion
+void CG_SparksExplosion( vec3_t origin, vec3_t dir );
+// BFP - Beam struggle sparks
+void CG_BeamStruggleEffect( vec3_t origin, vec3_t dir );
+// BFP - Explosion smoke
+void CG_SmokeExplosion( vec3_t origin, vec3_t dir );
+// BFP - Explosion sounds
+void CG_ExplosionSound( vec3_t origin );
+// BFP - Explosion effects
+void CG_ExplosionEffect( vec3_t origin, vec3_t dir );
+// BFP - Forcefield effect
+void CG_ForceFieldEffect( centity_t *cent, vec3_t origin, char *explosionModel, char *explosionShader );
+
+// BFP - Trails
+//
+// cg_trails.c
+//
+/* Trail types */
+#define KI_TRAIL 0
+#define BEAM_TRAIL 1
+#define MISSILE_TRAIL 2
+void CG_InitTrails( void );
+void CG_ResetTrail( const int TRAIL_TYPE, int entityNum, vec3_t origin );
+void CG_KiTrail( int entityNum, vec3_t origin, qboolean remove, qhandle_t hShader );
+void CG_BeamTrail( int entityNum, vec3_t origin, vec3_t muzzleOrigin, qhandle_t hShader );
+void CG_CorkscrewTrail( int entityNum, vec3_t origin, vec3_t muzzleOrigin, qhandle_t beamShader, qhandle_t corkscrewShader );
+void CG_MissileTrail( int entityNum, vec3_t origin, qhandle_t hShader, vec3_t color, qboolean rainbow );
+void CG_DrawMissileTrails( void );
//
// cg_snapshot.c
@@ -1318,7 +1498,7 @@ int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode );
void trap_FS_Read( void *buffer, int len, fileHandle_t f );
void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
void trap_FS_FCloseFile( fileHandle_t f );
-int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t
+int trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin );
// add commands to the local console as if they were typed in
// for map changing, etc. The command is not executed immediately,
@@ -1476,16 +1656,23 @@ qboolean trap_GetEntityToken( char *buffer, int bufferSize );
void CG_ClearParticles (void);
void CG_AddParticles (void);
-void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum);
-void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent);
-void CG_AddParticleShrapnel (localEntity_t *le);
-void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent);
-void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration);
-void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed);
-void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir);
-void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha);
-void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd);
-extern qboolean initparticles;
-int CG_NewParticleArea ( int num );
-
-
+// BFP - Bubble particle
+void CG_ParticleBubble (centity_t *cent, qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t origin2, int turbtime, float range, float size);
+// BFP - Dash smoke particle for ki boost when moving in the ground
+void CG_ParticleDashSmoke (centity_t *cent, qhandle_t pshader, vec3_t origin, float size, float velocityDisp, float upVelocity, float accel);
+// BFP - Antigrav rock particles for charging
+void CG_ParticleAntigravRock (qhandle_t pshader, qhandle_t pmodel, centity_t *cent, int entityNum, vec3_t origin, float size, float spawnRange, float endTime);
+void CG_AntigravRockHandling (centity_t *cent);
+// BFP - Particle aura
+void CG_ParticleAura (centity_t *cent, int entityNum, qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t origin2, float range);
+void CG_ParticleAuraHandling (centity_t *cent);
+// BFP - Bouncing debris rock fragment (explosion)
+void CG_ParticleRockDebris (qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t vel, float size, float velocity, float accel);
+// BFP - Water entry splash
+void CG_ParticleWaterSplash (qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t vel, float size, float velocity, float accel);
+// BFP - Spark particle
+void CG_ParticleSparks (qhandle_t pshader, vec3_t origin, vec3_t vel);
+// BFP - Charge smoke particle for ki charge
+void CG_ParticleChargeSmoke (centity_t *cent, qhandle_t pshader, vec3_t origin, float size, float radialVel, float baseRadius);
+// BFP - Beam struggle spark particle
+void CG_ParticleBeamStruggleSpark (qhandle_t pshader, vec3_t origin, vec3_t vel);
diff --git a/source/cgame/cg_localents.c b/source/cgame/cg_localents.c
index 53ec373..3c18b72 100644
--- a/source/cgame/cg_localents.c
+++ b/source/cgame/cg_localents.c
@@ -389,6 +389,47 @@ static void CG_AddMoveScaleFade( localEntity_t *le ) {
}
+/*
+==================
+CG_AddMoveDontScaleFade
+==================
+*/
+static void CG_AddMoveDontScaleFade( localEntity_t *le ) { // BFP - For LE_MOVE_DONT_SCALE_FADE type
+ refEntity_t *re;
+ float c;
+
+ re = &le->refEntity;
+
+ // fade / grow time
+ c = (float)( le->endTime - cg.time ) * (float)le->lifeRate;
+
+ // instead 0xff (255), use 510 to make the shader more visible
+ re->shaderRGBA[3] = 510 * c * le->color[3];
+ if ( re->shaderRGBA[3] <= 0 ) { // no alpha, disappear then
+ CG_FreeLocalEntity( le );
+ return;
+ }
+
+ // slow down the movement during 0.33 sec
+ if ( cg.time - le->startTime < 330 ) {
+ le->pos.trDelta[0] *= 0.998;
+ le->pos.trDelta[1] *= 0.998;
+ } else { // stop moving horizontally
+ vec3_t currentPos;
+ BG_EvaluateTrajectory( &le->pos, cg.time, currentPos );
+ VectorCopy( currentPos, le->pos.trBase );
+ le->pos.trTime = cg.time;
+ le->pos.trDelta[0] = le->pos.trDelta[1] = 0;
+ }
+
+ re->radius = le->radius;
+
+ BG_EvaluateTrajectory( &le->pos, cg.time, re->origin );
+
+ trap_R_AddRefEntityToScene( re );
+}
+
+
/*
===================
CG_AddScaleFade
@@ -476,11 +517,37 @@ static void CG_AddExplosion( localEntity_t *ex ) {
ent = &ex->refEntity;
+ if ( cg_bigExplosions.integer > 0 ) { // BFP - Big explosions
+ float scale = 1.04f;
+ float endTime = (float)( ex->endTime ) - (float)( ex->lifeRate * 0.5 );
+ float endScale = (float)( ex->endTime - cg.time ) / (float)( ex->endTime - ex->startTime );
+
+ switch( ex->leType ) {
+ case LE_EXPLOSION:
+ scale = 1.05f; // scale a bit faster to adjust
+ break;
+ case LE_EXPLOSION_SPHERE:
+ if ( cg.time > endTime ) {
+ scale = 0.9f * ( endScale + 0.5f ); // decrease size
+ }
+ break;
+ case LE_EXPLOSION_SHELL:
+ scale = 1.12f; // scale faster than the explosion sphere
+ break;
+ default:
+ break;
+ }
+
+ VectorScale( ent->axis[0], scale, ent->axis[0] );
+ VectorScale( ent->axis[1], scale, ent->axis[1] );
+ VectorScale( ent->axis[2], scale, ent->axis[2] );
+ }
+
// add the entity
trap_R_AddRefEntityToScene(ent);
// add the dlight
- if ( ex->light ) {
+ if ( cg_lightExplosions.integer > 0 ) { // BFP - Dynamic explosion lights
float light;
light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime );
@@ -661,6 +728,9 @@ void CG_AddLocalEntities( void ) {
break;
case LE_EXPLOSION:
+ case LE_EXPLOSION_SPHERE: // BFP - Explosion sphere
+ case LE_EXPLOSION_RING: // BFP - Explosion ring
+ case LE_EXPLOSION_SHELL: // BFP - Explosion shell
CG_AddExplosion( le );
break;
@@ -672,6 +742,10 @@ void CG_AddLocalEntities( void ) {
CG_AddMoveScaleFade( le );
break;
+ case LE_MOVE_DONT_SCALE_FADE: // BFP - Effect for explosion smoke
+ CG_AddMoveDontScaleFade( le );
+ break;
+
case LE_FADE_RGB: // teleporters, railtrails
CG_AddFadeRGB( le );
break;
diff --git a/source/cgame/cg_main.c b/source/cgame/cg_main.c
index 9b13581..bd066e3 100644
--- a/source/cgame/cg_main.c
+++ b/source/cgame/cg_main.c
@@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// cg_main.c -- initialization and primary entry point for cgame
#include "cg_local.h"
-int forceModelModificationCount = -1;
+// int forceModelModificationCount = -1;
void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum );
void CG_Shutdown( void );
@@ -37,7 +37,7 @@ This is the only way control passes into the module.
This must be the very first function compiled into the .q3vm file
================
*/
-intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) {
+DLLEXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) {
switch ( command ) {
case CG_INIT:
@@ -119,15 +119,17 @@ void CG_RegisterCvars( void ) {
trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) );
cgs.localServer = atoi( var );
- forceModelModificationCount = cg_forceModel.modificationCount;
+ // forceModelModificationCount = cg_forceModel.modificationCount;
trap_Cvar_Register(NULL, "model", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE );
trap_Cvar_Register(NULL, "headmodel", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE );
- //trap_Cvar_Register(NULL, "team_model", DEFAULT_TEAM_MODEL, CVAR_USERINFO | CVAR_ARCHIVE );
- //trap_Cvar_Register(NULL, "team_headmodel", DEFAULT_TEAM_HEAD, CVAR_USERINFO | CVAR_ARCHIVE );
+ trap_Cvar_Register(NULL, "team_model", DEFAULT_TEAM_MODEL, CVAR_USERINFO | CVAR_ARCHIVE );
+ trap_Cvar_Register(NULL, "team_headmodel", DEFAULT_TEAM_HEAD, CVAR_USERINFO | CVAR_ARCHIVE );
}
-/*
+// BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?)
+#if 0
+/*
===================
CG_ForceModelChange
===================
@@ -145,6 +147,7 @@ static void CG_ForceModelChange( void ) {
CG_NewClientInfo( i );
}
}
+#endif
/*
=================
@@ -175,11 +178,14 @@ void CG_UpdateCvars( void ) {
trap_Cvar_Set( "teamoverlay", "1" );
}
+ // BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?)
+#if 0
// if force model changed
if ( forceModelModificationCount != cg_forceModel.modificationCount ) {
forceModelModificationCount = cg_forceModel.modificationCount;
CG_ForceModelChange();
}
+#endif
}
int CG_CrosshairPlayer( void ) {
@@ -201,7 +207,7 @@ void QDECL CG_Printf( const char *msg, ... ) {
char text[1024];
va_start (argptr, msg);
- vsprintf (text, msg, argptr);
+ Q_vsprintf (text, msg, argptr);
va_end (argptr);
trap_Print( text );
@@ -212,7 +218,7 @@ void QDECL CG_Error( const char *msg, ... ) {
char text[1024];
va_start (argptr, msg);
- vsprintf (text, msg, argptr);
+ Q_vsprintf (text, msg, argptr);
va_end (argptr);
trap_Error( text );
@@ -226,7 +232,7 @@ void QDECL Com_Error( int level, const char *error, ... ) {
char text[1024];
va_start (argptr, error);
- vsprintf (text, error, argptr);
+ Q_vsprintf (text, error, argptr);
va_end (argptr);
CG_Error( "%s", text);
@@ -237,7 +243,7 @@ void QDECL Com_Printf( const char *msg, ... ) {
char text[1024];
va_start (argptr, msg);
- vsprintf (text, msg, argptr);
+ Q_vsprintf (text, msg, argptr);
va_end (argptr);
CG_Printf ("%s", text);
@@ -364,7 +370,6 @@ static void CG_RegisterSounds( void ) {
cgs.media.youHaveFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_you_flag.wav", qtrue );
cgs.media.holyShitSound = trap_S_RegisterSound("sound/feedback/voc_holyshit.wav", qtrue);
- cgs.media.neutralFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue );
cgs.media.yourTeamTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_1flag.wav", qtrue );
cgs.media.enemyTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_1flag.wav", qtrue );
}
@@ -389,12 +394,15 @@ static void CG_RegisterSounds( void ) {
cgs.media.hitSound = trap_S_RegisterSound( "sound/feedback/hit.wav", qfalse );
- cgs.media.impressiveSound = trap_S_RegisterSound( "sound/feedback/impressive.wav", qtrue );
cgs.media.excellentSound = trap_S_RegisterSound( "sound/feedback/excellent.wav", qtrue );
+ // BFP - No impressive, gauntlet, defend and assist sounds
+#if 0
+ cgs.media.impressiveSound = trap_S_RegisterSound( "sound/feedback/impressive.wav", qtrue );
cgs.media.deniedSound = trap_S_RegisterSound( "sound/feedback/denied.wav", qtrue );
cgs.media.humiliationSound = trap_S_RegisterSound( "sound/feedback/humiliation.wav", qtrue );
cgs.media.assistSound = trap_S_RegisterSound( "sound/feedback/assist.wav", qtrue );
cgs.media.defendSound = trap_S_RegisterSound( "sound/feedback/defense.wav", qtrue );
+#endif
cgs.media.takenLeadSound = trap_S_RegisterSound( "sound/feedback/takenlead.wav", qtrue);
cgs.media.tiedLeadSound = trap_S_RegisterSound( "sound/feedback/tiedlead.wav", qtrue);
@@ -450,7 +458,8 @@ static void CG_RegisterSounds( void ) {
}
// FIXME: only needed with item
- cgs.media.flightSound = trap_S_RegisterSound( "sound/items/flight.wav", qfalse );
+ // BFP - No flight powerup sound
+ // cgs.media.flightSound = trap_S_RegisterSound( "sound/items/flight.wav", qfalse );
cgs.media.medkitSound = trap_S_RegisterSound ("sound/items/use_medkit.wav", qfalse);
cgs.media.quadSound = trap_S_RegisterSound("sound/items/damage3.wav", qfalse);
cgs.media.sfx_ric1 = trap_S_RegisterSound ("sound/weapons/machinegun/ric1.wav", qfalse);
@@ -460,19 +469,131 @@ static void CG_RegisterSounds( void ) {
cgs.media.sfx_rockexp = trap_S_RegisterSound ("sound/weapons/rocket/rocklx1a.wav", qfalse);
cgs.media.sfx_plasmaexp = trap_S_RegisterSound ("sound/weapons/plasma/plasmx1a.wav", qfalse);
- cgs.media.regenSound = trap_S_RegisterSound("sound/items/regen.wav", qfalse);
+ // BFP - No regen sound
+ // cgs.media.regenSound = trap_S_RegisterSound("sound/items/regen.wav", qfalse);
cgs.media.protectSound = trap_S_RegisterSound("sound/items/protect3.wav", qfalse);
cgs.media.n_healthSound = trap_S_RegisterSound("sound/items/n_health.wav", qfalse );
cgs.media.hgrenb1aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb1a.wav", qfalse);
cgs.media.hgrenb2aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb2a.wav", qfalse);
+ // BFP - Explosion sounds
+ cgs.media.explosion1Sound = trap_S_RegisterSound("sound/bfp/explosion1.wav", qfalse);
+ cgs.media.explosion2Sound = trap_S_RegisterSound("sound/bfp/explosion2.wav", qfalse);
+ cgs.media.explosion3Sound = trap_S_RegisterSound("sound/bfp/explosion3.wav", qfalse);
+ cgs.media.explosion4Sound = trap_S_RegisterSound("sound/bfp/explosion4.wav", qfalse);
+ cgs.media.explosion5Sound = trap_S_RegisterSound("sound/bfp/explosion5.wav", qfalse);
+ cgs.media.explosion6Sound = trap_S_RegisterSound("sound/bfp/explosion6.wav", qfalse);
+
cgs.media.kiChargeSound = trap_S_RegisterSound("sound/bfp/kicharge1.wav", qfalse); // BFP - Ki charge sound
cgs.media.kiUseSound = trap_S_RegisterSound("sound/bfp/kiuse1.wav", qfalse); // BFP - Ki use sound
+ cgs.media.defaultKiChargingSound = trap_S_RegisterSound("sound/bfp/attackcharge1.wav", qfalse); // BFP - Default ki charging attack sound
+ cgs.media.diskKiChargingSound = trap_S_RegisterSound("sound/bfp/diskcharge1.wav", qfalse); // BFP - Disk ki charging attack sound
+ cgs.media.tierUpSound = trap_S_RegisterSound("sound/bfp/tierup.wav", qfalse); // BFP - Tier up sound
+
+ // BFP - NOTE: Sounds like that are reusing Q3 ones
+ cgs.media.defaultKiBeamExplosionWaveSound = trap_S_RegisterSound("sound/weapons/rocket/rockfly.wav", qfalse); // BFP - Default ki beam and ki explosion wave sound
+ cgs.media.defaultKiFiringAttackSound = trap_S_RegisterSound("sound/weapons/bfg/bfg_fire.wav", qfalse); // BFP - Default ki firing sound
}
//===================================================================================
+/*
+==========================
+CG_RegisterMonsterModel
+==========================
+*/
+static qboolean CG_RegisterMonsterModel( void ) { // BFP - Monster gamemode, register monster model
+ char filename[MAX_QPATH*2];
+
+ // legs
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", MONSTER_NAME );
+ cgs.media.monsterLegsModel = trap_R_RegisterModel( filename );
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjlegs.md3", MONSTER_NAME );
+ cgs.media.monsterUltTierLegsModel = trap_R_RegisterModel( filename );
+
+ // torso
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", MONSTER_NAME );
+ cgs.media.monsterTorsoModel = trap_R_RegisterModel( filename );
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjtorso.md3", MONSTER_NAME );
+ cgs.media.monsterUltTierTorsoModel = trap_R_RegisterModel( filename );
+
+ // head
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", MONSTER_NAME );
+ cgs.media.monsterHeadModel = trap_R_RegisterModel( filename );
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjhead.md3", MONSTER_NAME );
+ cgs.media.monsterUltTierHeadModel = trap_R_RegisterModel( filename );
+
+ if ( !cgs.media.monsterLegsModel || !cgs.media.monsterTorsoModel || !cgs.media.monsterHeadModel ) {
+ return qfalse;
+ }
+ return qtrue;
+}
+
+/*
+==========================
+CG_RegisterMonsterSkin
+==========================
+*/
+static void CG_RegisterMonsterSkin( void ) { // BFP - Monster gamemode, register monster skin
+ char filename[MAX_QPATH];
+
+ // legs
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", MONSTER_NAME, "default" );
+ cgs.media.monsterLegsSkin = trap_R_RegisterSkin( filename );
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjlower.skin", MONSTER_NAME, "default" );
+ cgs.media.monsterUltTierLegsSkin = trap_R_RegisterSkin( filename );
+
+ // torso
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", MONSTER_NAME, "default" );
+ cgs.media.monsterTorsoSkin = trap_R_RegisterSkin( filename );
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjtorso.skin", MONSTER_NAME, "default" );
+ cgs.media.monsterUltTierTorsoSkin = trap_R_RegisterSkin( filename );
+
+ // head
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", MONSTER_NAME, "default" );
+ cgs.media.monsterHeadSkin = trap_R_RegisterSkin( filename );
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjhead.skin", MONSTER_NAME, "default" );
+ cgs.media.monsterUltTierHeadSkin = trap_R_RegisterSkin( filename );
+}
+
+/*
+==========================
+CG_RegisterMonsterAnimations
+==========================
+*/
+static void CG_RegisterMonsterAnimations( void ) { // BFP - Monster gamemode, register monster skin
+ char filename[MAX_QPATH*2];
+ clientInfo_t temp;
+
+ memset( &temp, 0, sizeof(temp) );
+
+ Com_sprintf( filename, sizeof(filename), "models/players/%s/animation.cfg", MONSTER_NAME );
+ if ( CG_ParseAnimationFile( filename, &temp ) ) {
+ memcpy( cgs.media.monsterAnimations, temp.animations, sizeof(cgs.media.monsterAnimations) );
+ }
+}
+
+/*
+==========================
+CG_RegisterMonsterModelIcon
+==========================
+*/
+static void CG_RegisterMonsterModelIcon( void ) { // BFP - Monster gamemode, register monster model icon
+ char filename[MAX_QPATH*2];
+ clientInfo_t temp;
+
+ memset( &temp, 0, sizeof(temp) );
+
+ if ( CG_FindClientHeadFile( filename, sizeof(filename), &temp, "default", MONSTER_NAME, "default", "icon", "skin" ) ) {
+ cgs.media.monsterModelIcon = trap_R_RegisterShaderNoMip( filename );
+ } else if ( CG_FindClientHeadFile( filename, sizeof(filename), &temp, "default", MONSTER_NAME, "default", "icon", "tga" ) ) {
+ cgs.media.monsterModelIcon = trap_R_RegisterShaderNoMip( filename );
+ }
+ if ( !cgs.media.monsterModelIcon ) {
+ cgs.media.monsterModelIcon = cgs.media.deferShader;
+ }
+}
/*
=================
@@ -543,11 +664,22 @@ static void CG_RegisterGraphics( void ) {
for ( i = 0 ; i < NUM_CROSSHAIRS ; i++ ) {
cgs.media.crosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/crosshair%c", 'a'+i) );
+ cgs.media.redCrosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/redcrosshair%c", 'a'+i) ); // BFP - BFP crosshair health feature
}
cgs.media.backTileShader = trap_R_RegisterShader( "gfx/2d/backtile" );
cgs.media.noammoShader = trap_R_RegisterShader( "icons/noammo" );
+ // BFP - NOTE: Shaders like that on the HUD must be trap_R_RegisterShaderNoMip,
+ // otherwise appears weird lines around the image/texture
+
+ // BFP - Ki attack charge up points
+ cgs.media.chargeupbuttgreen = trap_R_RegisterShaderNoMip( "menu/art/chargeupbuttgreen" );
+
+ // BFP - HUD overlay
+ cgs.media.hudoverlay = trap_R_RegisterShaderNoMip( "menu/art/hudoverlay" );
+ cgs.media.hudoverlayr = trap_R_RegisterShaderNoMip( "menu/art/hudoverlayr" );
+
// powerup shaders
cgs.media.quadShader = trap_R_RegisterShader("powerups/quad" );
cgs.media.quadWeaponShader = trap_R_RegisterShader("powerups/quadWeapon" );
@@ -557,13 +689,6 @@ static void CG_RegisterGraphics( void ) {
cgs.media.regenShader = trap_R_RegisterShader("powerups/regen" );
cgs.media.hastePuffShader = trap_R_RegisterShader("hasteSmokePuff" );
- if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) {
- cgs.media.redCubeModel = trap_R_RegisterModel( "models/powerups/orb/r_orb.md3" );
- cgs.media.blueCubeModel = trap_R_RegisterModel( "models/powerups/orb/b_orb.md3" );
- cgs.media.redCubeIcon = trap_R_RegisterShader( "icons/skull_red" );
- cgs.media.blueCubeIcon = trap_R_RegisterShader( "icons/skull_blue" );
- }
-
if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) {
cgs.media.redFlagModel = trap_R_RegisterModel( "models/flags/r_flag.md3" );
cgs.media.blueFlagModel = trap_R_RegisterModel( "models/flags/b_flag.md3" );
@@ -575,7 +700,8 @@ static void CG_RegisterGraphics( void ) {
cgs.media.blueFlagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_blu3" );
}
- if ( cgs.gametype >= GT_TEAM || cg_buildScript.integer ) {
+ if ( cgs.gametype >= GT_TEAM || cg_buildScript.integer
+ || cgs.gametype == GT_SURVIVAL ) { // BFP - Survival gametype, needs to draw the line status too
cgs.media.friendShader = trap_R_RegisterShader( "sprites/foe" );
cgs.media.redQuadShader = trap_R_RegisterShader("powerups/blueflag" );
cgs.media.teamStatusBar = trap_R_RegisterShader( "gfx/2d/colorbar.tga" );
@@ -584,9 +710,6 @@ static void CG_RegisterGraphics( void ) {
cgs.media.armorModel = trap_R_RegisterModel( "models/powerups/armor/armor_yel.md3" );
cgs.media.armorIcon = trap_R_RegisterShaderNoMip( "icons/iconr_yellow" );
- cgs.media.machinegunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/m_shell.md3" );
- cgs.media.shotgunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" );
-
cgs.media.gibAbdomen = trap_R_RegisterModel( "models/gibs/abdomen.md3" );
cgs.media.gibArm = trap_R_RegisterModel( "models/gibs/arm.md3" );
cgs.media.gibChest = trap_R_RegisterModel( "models/gibs/chest.md3" );
@@ -607,22 +730,62 @@ static void CG_RegisterGraphics( void ) {
cgs.media.bulletFlashModel = trap_R_RegisterModel("models/weaphits/bullet.md3");
cgs.media.ringFlashModel = trap_R_RegisterModel("models/weaphits/ring02.md3");
cgs.media.dishFlashModel = trap_R_RegisterModel("models/weaphits/boom01.md3");
+ // BFP - Explosion, projectile and beam models
+ cgs.media.nukeModel = trap_R_RegisterModel( "models/weaphits/nuke.md3" );
+ cgs.media.lowPolySphereModel = trap_R_RegisterModel( "models/weaphits/sphere.md3" );
+ cgs.media.highPolySphereModel = trap_R_RegisterModel( "models/weaphits/sphere_hi.md3" );
+ cgs.media.beamModel = trap_R_RegisterModel( "models/weaphits/beam.md3" );
+
+ // BFP - Explosion shell shader
+ cgs.media.explosionShellShader = trap_R_RegisterShader( "explosionShellShader" );
+ // BFP - Explosion ring shader (reused rail gun explosion)
+ cgs.media.railExplosionShader = trap_R_RegisterShader( "railExplosion" );
+
+ // BFP - Explosion shader test
+ cgs.media.ImpactBeamExplosionShader = trap_R_RegisterShader( "ImpactBeamExplosionShader" );
+
cgs.media.teleportEffectModel = trap_R_RegisterModel( "models/misc/telep.md3" );
cgs.media.teleportEffectShader = trap_R_RegisterShader( "teleportEffect" );
- cgs.media.invulnerabilityPowerupModel = trap_R_RegisterModel( "models/powerups/shield/shield.md3" );
- cgs.media.medalImpressive = trap_R_RegisterShaderNoMip( "medal_impressive" );
+ // BFP - No invulnerability powerup
+ // cgs.media.invulnerabilityPowerupModel = trap_R_RegisterModel( "models/powerups/shield/shield.md3" );
cgs.media.medalExcellent = trap_R_RegisterShaderNoMip( "medal_excellent" );
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+#if 0
+ cgs.media.medalImpressive = trap_R_RegisterShaderNoMip( "medal_impressive" );
cgs.media.medalGauntlet = trap_R_RegisterShaderNoMip( "medal_gauntlet" );
cgs.media.medalDefend = trap_R_RegisterShaderNoMip( "medal_defend" );
cgs.media.medalAssist = trap_R_RegisterShaderNoMip( "medal_assist" );
cgs.media.medalCapture = trap_R_RegisterShaderNoMip( "medal_capture" );
+#endif
- cgs.media.auraModel = trap_R_RegisterModel( "models/effects/aura.md3" ); // BFP - Aura model
+ // BFP - Aura models
+ cgs.media.auraModel = trap_R_RegisterModel( "models/effects/aura.md3" ); // BFP - Normal aura model
cgs.media.backauraModel = trap_R_RegisterModel( "models/effects/backaura.md3" ); // BFP - Back aura model
cgs.media.flyauraModel = trap_R_RegisterModel( "models/effects/flyaura.md3" ); // BFP - Fly aura model
cgs.media.runauraModel = trap_R_RegisterModel( "models/effects/runaura.md3" ); // BFP - Run aura model
- cgs.media.auraEffectShader = trap_R_RegisterShader( "powerups/redtiny" ); // BFP - Aura shader
+
+ // BFP - Aura shaders
+ cgs.media.auraBlueTinyShader = trap_R_RegisterShader( "powerups/bluetiny" );
+ cgs.media.auraBlueChargeShader = trap_R_RegisterShader( "powerups/bluecharge" );
+ cgs.media.auraBlueUseShader = trap_R_RegisterShader( "powerups/blueuse" );
+ cgs.media.auraRedTinyShader = trap_R_RegisterShader( "powerups/redtiny" );
+ cgs.media.auraRedChargeShader = trap_R_RegisterShader( "powerups/redcharge" );
+ cgs.media.auraRedUseShader = trap_R_RegisterShader( "powerups/reduse" );
+ cgs.media.auraYellowTinyShader = trap_R_RegisterShader( "powerups/yellowtiny" );
+ cgs.media.auraYellowChargeShader = trap_R_RegisterShader( "powerups/yellowcharge" );
+ cgs.media.auraYellowUseShader = trap_R_RegisterShader( "powerups/yellowuse" );
+ cgs.media.ultimateAuraShader = trap_R_RegisterShader( "powerups/ultimateaura" );
+
+ // BFP - Ki trail shaders
+ cgs.media.kiTrailBlueShader = trap_R_RegisterShader( "powerups/bluekitrail" );
+ cgs.media.kiTrailRedShader = trap_R_RegisterShader( "powerups/redkitrail" );
+ cgs.media.kiTrailYellowShader = trap_R_RegisterShader( "powerups/yellowkitrail" );
+
+ // BFP - Ki attack beam and projectile shaders (for testing purposes)
+ cgs.media.PowerWaveBeamShader = trap_R_RegisterShader( "PowerWaveBeamShader" );
+ cgs.media.SSBSpiralShader = trap_R_RegisterShader( "SSBSpiralShader" );
+ cgs.media.SSBBeamShader = trap_R_RegisterShader( "SSBBeamShader" );
memset( cg_items, 0, sizeof( cg_items ) );
memset( cg_weapons, 0, sizeof( cg_weapons ) );
@@ -646,6 +809,28 @@ static void CG_RegisterGraphics( void ) {
cgs.media.wakeMarkShader = trap_R_RegisterShader( "wake" );
cgs.media.bloodMarkShader = trap_R_RegisterShader( "bloodMark" );
+ // BFP - Crack mark shader
+ cgs.media.crackMarkShader = trap_R_RegisterShader( "crackMarkShader" );
+
+ // BFP - Particle shaders
+ cgs.media.pebbleShader1 = trap_R_RegisterShader( "pebbleShader1" ); // BFP - Rock particles for charging and explosion
+ cgs.media.pebbleShader2 = trap_R_RegisterShader( "pebbleShader2" ); // BFP - Rock particles for charging and explosion
+ cgs.media.pebbleShader3 = trap_R_RegisterShader( "pebbleShader3" ); // BFP - Rock particles for charging and explosion
+ cgs.media.particleSmokeShader = trap_R_RegisterShader( "particleSmokeShader" ); // BFP - Smoke particle when using ki boost in the ground and for the explosion
+ cgs.media.sparkShader1 = trap_R_RegisterShader( "sparkShader1" ); // BFP - Spark particle for explosion
+ cgs.media.sparkShader2 = trap_R_RegisterShader( "sparkShader2" ); // BFP - Spark particle for explosion
+
+ // BFP - Optional 3D pebble/rock models
+ cgs.media.pebbleMdl1 = trap_R_RegisterModel( "models/effects/pebble1.md3" );
+ cgs.media.pebbleMdl2 = trap_R_RegisterModel( "models/effects/pebble2.md3" );
+ cgs.media.pebbleMdl3 = trap_R_RegisterModel( "models/effects/pebble3.md3" );
+
+ if ( !cgs.media.pebbleMdl1
+ || !cgs.media.pebbleMdl2
+ || !cgs.media.pebbleMdl3 ) {
+ CG_Printf( "^3Unable to load pebble models, some of them are missing. Remember: must be in models/effects/pebble[1-3].md3 one per each, all 3 of them must be available" );
+ }
+
// register the inline models
cgs.numInlineModels = trap_CM_NumInlineModels();
for ( i = 1 ; i < cgs.numInlineModels ; i++ ) {
@@ -672,8 +857,25 @@ static void CG_RegisterGraphics( void ) {
cgs.gameModels[i] = trap_R_RegisterModel( modelName );
}
+ // BFP - Monster gamemode, set model and skin for the player monster
+ // BFP - NOTE: Historically, on RC versions, the monster model pack went without player sounds
+ // and the game loaded the selected player model sounds that the user played
+ // on the other gamemodes
+ if ( cgs.gametype == GT_MONSTER && cgs.monster > 0 ) {
+ if ( CG_RegisterMonsterModel() ) {
+ CG_Printf( "loaded monster model\n" );
+ } else {
+ CG_Printf( "unable to load monster model\n" );
+ }
+ CG_RegisterMonsterSkin();
+ CG_RegisterMonsterAnimations();
+ CG_RegisterMonsterModelIcon();
+ }
+
CG_ClearParticles ();
-/*
+
+// BFP - Unused function for particles, looks like here is to determine in the areas
+#if 0
for (i=1; i= sizeof(buf) ) {
+ trap_FS_FCloseFile( f );
+ Com_Printf( "BFP config file too long\n" );
+ return;
+ }
+
+ // read the config file
+ trap_FS_Read( buf, len, f );
+ buf[len] = 0;
+ trap_FS_FCloseFile( f );
+
+ // parse the config file line by line
+ ptr = buf;
+
+ while ( *ptr ) {
+ // skip whitespace
+ while ( *ptr && ( *ptr == ' ' || *ptr == '\t' || *ptr == '\r' ) ) {
+ ptr++;
+ }
+
+ if ( !*ptr ) {
+ break;
+ }
+
+ // check command type
+ if ( *ptr == 'b' && *(ptr + 1) == ' ' ) { // "b "
+ char key[256], binding[512];
+ int i;
+
+ ptr += 2; // skip "b "
+
+ // parse key
+ i = 0;
+ while ( *ptr && *ptr != ' ' && *ptr != '\n' ) {
+ key[i++] = *ptr++;
+ }
+ key[i] = 0;
+
+ // skip space
+ while ( *ptr && *ptr == ' ' ) {
+ ptr++;
+ }
+
+ // parse binding (may be quoted)
+ i = 0;
+ if ( *ptr == '"' ) {
+ ptr++; // skip opening quote
+ while ( *ptr && *ptr != '"' && *ptr != '\n' ) {
+ binding[i++] = *ptr++;
+ }
+ if ( *ptr == '"' ) {
+ ptr++; // skip closing quote
+ }
+ } else {
+ while ( *ptr && *ptr != '\n' ) {
+ binding[i++] = *ptr++;
+ }
+ }
+ binding[i] = 0;
+
+ // execute!
+ Com_sprintf( cmd, sizeof( cmd ), "bind %s %s\n", key, binding );
+ trap_SendConsoleCommand( cmd );
+ } else if ( *ptr == 'c' && *(ptr + 1) == ' ' ) { // "c "
+ char cvar[256], value[512];
+ int i;
+
+ ptr += 2; // skip "c "
+
+ // parse cvar name
+ i = 0;
+ while ( *ptr && *ptr != ' ' && *ptr != '\n' ) {
+ cvar[i++] = *ptr++;
+ }
+ cvar[i] = 0;
+
+ // skip space
+ while ( *ptr && *ptr == ' ' ) {
+ ptr++;
+ }
+
+ // parse value
+ i = 0;
+ while ( *ptr && *ptr != '\n' ) {
+ value[i++] = *ptr++;
+ }
+ value[i] = 0;
+
+ // execute!
+ Com_sprintf( cmd, sizeof( cmd ), "seta %s %s\n", cvar, value );
+ trap_SendConsoleCommand( cmd );
+ } else if ( !Q_strncmp( ptr, "bfp", 3 ) ) { // "bfp" marks the end of file
+ break;
+ }
+
+ // skip to next line
+ while ( *ptr && *ptr != '\n' ) {
+ ptr++;
+ }
+ if ( *ptr == '\n' ) {
+ ptr++;
+ }
+ }
+
+ // Com_Printf( S_COLOR_GREEN "BFP config loaded successfully\n" );
+}
+
/*
=================
CG_Init
@@ -803,6 +1134,9 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) {
cgs.media.charsetPropGlow = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" );
cgs.media.charsetPropB = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" );
+ // BFP - Initialize that shader for missile trails
+ cgs.media.railCoreShader = trap_R_RegisterShader( "railCore" );
+
CG_RegisterCvars();
CG_InitConsoleCommands();
@@ -818,6 +1152,10 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) {
cgs.screenXScale = cgs.glconfig.vidWidth / 640.0;
cgs.screenYScale = cgs.glconfig.vidHeight / 480.0;
+#ifdef USE_NEW_FONT_RENDERER
+ CG_LoadFonts();
+#endif
+
// get the gamestate from the client system
trap_GetGameState( &cgs.gameState );
@@ -857,6 +1195,9 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) {
CG_InitMarkPolys();
+ // BFP - Initialize trails
+ CG_InitTrails();
+
// remove the last loading update
cg.infoScreenText[0] = 0;
@@ -867,6 +1208,9 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) {
CG_LoadingString( "" );
+ // BFP - Load bfp.cfg
+ CG_LoadBFPConfig();
+
CG_ShaderStateChanged();
trap_S_ClearLoopingSounds( qtrue );
diff --git a/source/cgame/cg_marks.c b/source/cgame/cg_marks.c
index 7717308..1f8b8f5 100644
--- a/source/cgame/cg_marks.c
+++ b/source/cgame/cg_marks.c
@@ -65,7 +65,7 @@ CG_FreeMarkPoly
==================
*/
void CG_FreeMarkPoly( markPoly_t *le ) {
- if ( !le->prevMark ) {
+ if ( !le->prevMark || !le->nextMark ) {
CG_Error( "CG_FreeLocalEntity: not active" );
}
@@ -291,1978 +291,3 @@ void CG_AddMarks( void ) {
trap_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts );
}
}
-
-// cg_particles.c
-
-#define BLOODRED 2
-#define EMISIVEFADE 3
-#define GREY75 4
-
-typedef struct particle_s
-{
- struct particle_s *next;
-
- float time;
- float endtime;
-
- vec3_t org;
- vec3_t vel;
- vec3_t accel;
- int color;
- float colorvel;
- float alpha;
- float alphavel;
- int type;
- qhandle_t pshader;
-
- float height;
- float width;
-
- float endheight;
- float endwidth;
-
- float start;
- float end;
-
- float startfade;
- qboolean rotate;
- int snum;
-
- qboolean link;
-
- // Ridah
- int shaderAnim;
- int roll;
-
- int accumroll;
-
-} cparticle_t;
-
-typedef enum
-{
- P_NONE,
- P_WEATHER,
- P_FLAT,
- P_SMOKE,
- P_ROTATE,
- P_WEATHER_TURBULENT,
- P_ANIM, // Ridah
- P_BAT,
- P_BLEED,
- P_FLAT_SCALEUP,
- P_FLAT_SCALEUP_FADE,
- P_WEATHER_FLURRY,
- P_SMOKE_IMPACT,
- P_BUBBLE,
- P_BUBBLE_TURBULENT,
- P_SPRITE
-} particle_type_t;
-
-#define MAX_SHADER_ANIMS 32
-#define MAX_SHADER_ANIM_FRAMES 64
-
-static char *shaderAnimNames[MAX_SHADER_ANIMS] = {
- "explode1",
- NULL
-};
-static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES];
-static int shaderAnimCounts[MAX_SHADER_ANIMS] = {
- 23
-};
-static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = {
- 1.0f
-};
-static int numShaderAnims;
-// done.
-
-#define PARTICLE_GRAVITY 40
-#define MAX_PARTICLES 1024
-
-cparticle_t *active_particles, *free_particles;
-cparticle_t particles[MAX_PARTICLES];
-int cl_numparticles = MAX_PARTICLES;
-
-qboolean initparticles = qfalse;
-vec3_t pvforward, pvright, pvup;
-vec3_t rforward, rright, rup;
-
-float oldtime;
-
-/*
-===============
-CL_ClearParticles
-===============
-*/
-void CG_ClearParticles (void)
-{
- int i;
-
- memset( particles, 0, sizeof(particles) );
-
- free_particles = &particles[0];
- active_particles = NULL;
-
- for (i=0 ;itype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY
- || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT)
- {// create a front facing polygon
-
- if (p->type != P_WEATHER_FLURRY)
- {
- if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT)
- {
- if (org[2] > p->end)
- {
- p->time = cg.time;
- VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground
-
- p->org[2] = ( p->start + crandom () * 4 );
-
-
- if (p->type == P_BUBBLE_TURBULENT)
- {
- p->vel[0] = crandom() * 4;
- p->vel[1] = crandom() * 4;
- }
-
- }
- }
- else
- {
- if (org[2] < p->end)
- {
- p->time = cg.time;
- VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground
-
- while (p->org[2] < p->end)
- {
- p->org[2] += (p->start - p->end);
- }
-
-
- if (p->type == P_WEATHER_TURBULENT)
- {
- p->vel[0] = crandom() * 16;
- p->vel[1] = crandom() * 16;
- }
-
- }
- }
-
-
- // Rafael snow pvs check
- if (!p->link)
- return;
-
- p->alpha = 1;
- }
-
- // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp
- if (Distance( cg.snap->ps.origin, org ) > 1024) {
- return;
- }
- // done.
-
- if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT)
- {
- VectorMA (org, -p->height, pvup, point);
- VectorMA (point, -p->width, pvright, point);
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, -p->height, pvup, point);
- VectorMA (point, p->width, pvright, point);
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, pvup, point);
- VectorMA (point, p->width, pvright, point);
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, pvup, point);
- VectorMA (point, -p->width, pvright, point);
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255 * p->alpha;
- }
- else
- {
- VectorMA (org, -p->height, pvup, point);
- VectorMA (point, -p->width, pvright, point);
- VectorCopy( point, TRIverts[0].xyz );
- TRIverts[0].st[0] = 1;
- TRIverts[0].st[1] = 0;
- TRIverts[0].modulate[0] = 255;
- TRIverts[0].modulate[1] = 255;
- TRIverts[0].modulate[2] = 255;
- TRIverts[0].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, pvup, point);
- VectorMA (point, -p->width, pvright, point);
- VectorCopy (point, TRIverts[1].xyz);
- TRIverts[1].st[0] = 0;
- TRIverts[1].st[1] = 0;
- TRIverts[1].modulate[0] = 255;
- TRIverts[1].modulate[1] = 255;
- TRIverts[1].modulate[2] = 255;
- TRIverts[1].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, pvup, point);
- VectorMA (point, p->width, pvright, point);
- VectorCopy (point, TRIverts[2].xyz);
- TRIverts[2].st[0] = 0;
- TRIverts[2].st[1] = 1;
- TRIverts[2].modulate[0] = 255;
- TRIverts[2].modulate[1] = 255;
- TRIverts[2].modulate[2] = 255;
- TRIverts[2].modulate[3] = 255 * p->alpha;
- }
-
- }
- else if (p->type == P_SPRITE)
- {
- vec3_t rr, ru;
- vec3_t rotate_ang;
-
- VectorSet (color, 1.0, 1.0, 0.5);
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
-
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
-
- if (p->roll) {
- vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- rotate_ang[ROLL] += p->roll;
- AngleVectors ( rotate_ang, NULL, rr, ru);
- }
-
- if (p->roll) {
- VectorMA (org, -height, ru, point);
- VectorMA (point, -width, rr, point);
- } else {
- VectorMA (org, -height, pvup, point);
- VectorMA (point, -width, pvright, point);
- }
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*height, ru, point);
- } else {
- VectorMA (point, 2*height, pvup, point);
- }
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*width, rr, point);
- } else {
- VectorMA (point, 2*width, pvright, point);
- }
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, -2*height, ru, point);
- } else {
- VectorMA (point, -2*height, pvup, point);
- }
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
- }
- else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT)
- {// create a front rotating facing polygon
-
- if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) {
- return;
- }
-
- if (p->color == BLOODRED)
- VectorSet (color, 0.22f, 0.0f, 0.0f);
- else if (p->color == GREY75)
- {
- float len;
- float greyit;
- float val;
- len = Distance (cg.snap->ps.origin, org);
- if (!len)
- len = 1;
-
- val = 4096/len;
- greyit = 0.25 * val;
- if (greyit > 0.5)
- greyit = 0.5;
-
- VectorSet (color, greyit, greyit, greyit);
- }
- else
- VectorSet (color, 1.0, 1.0, 1.0);
-
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
-
- if (cg.time > p->startfade)
- {
- invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) );
-
- if (p->color == EMISIVEFADE)
- {
- float fval;
- fval = (invratio * invratio);
- if (fval < 0)
- fval = 0;
- VectorSet (color, fval , fval , fval );
- }
- invratio *= p->alpha;
- }
- else
- invratio = 1 * p->alpha;
-
- if (invratio > 1)
- invratio = 1;
-
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
-
- if (p->type != P_SMOKE_IMPACT)
- {
- vec3_t temp;
-
- vectoangles (rforward, temp);
- p->accumroll += p->roll;
- temp[ROLL] += p->accumroll * 0.1;
- AngleVectors ( temp, NULL, rright2, rup2);
- }
- else
- {
- VectorCopy (rright, rright2);
- VectorCopy (rup, rup2);
- }
-
- if (p->rotate)
- {
- VectorMA (org, -height, rup2, point);
- VectorMA (point, -width, rright2, point);
- }
- else
- {
- VectorMA (org, -p->height, pvup, point);
- VectorMA (point, -p->width, pvright, point);
- }
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255 * color[0];
- verts[0].modulate[1] = 255 * color[1];
- verts[0].modulate[2] = 255 * color[2];
- verts[0].modulate[3] = 255 * invratio;
-
- if (p->rotate)
- {
- VectorMA (org, -height, rup2, point);
- VectorMA (point, width, rright2, point);
- }
- else
- {
- VectorMA (org, -p->height, pvup, point);
- VectorMA (point, p->width, pvright, point);
- }
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255 * color[0];
- verts[1].modulate[1] = 255 * color[1];
- verts[1].modulate[2] = 255 * color[2];
- verts[1].modulate[3] = 255 * invratio;
-
- if (p->rotate)
- {
- VectorMA (org, height, rup2, point);
- VectorMA (point, width, rright2, point);
- }
- else
- {
- VectorMA (org, p->height, pvup, point);
- VectorMA (point, p->width, pvright, point);
- }
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255 * color[0];
- verts[2].modulate[1] = 255 * color[1];
- verts[2].modulate[2] = 255 * color[2];
- verts[2].modulate[3] = 255 * invratio;
-
- if (p->rotate)
- {
- VectorMA (org, height, rup2, point);
- VectorMA (point, -width, rright2, point);
- }
- else
- {
- VectorMA (org, p->height, pvup, point);
- VectorMA (point, -p->width, pvright, point);
- }
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255 * color[0];
- verts[3].modulate[1] = 255 * color[1];
- verts[3].modulate[2] = 255 * color[2];
- verts[3].modulate[3] = 255 * invratio;
-
- }
- else if (p->type == P_BLEED)
- {
- vec3_t rr, ru;
- vec3_t rotate_ang;
- float alpha;
-
- alpha = p->alpha;
-
- if (p->roll)
- {
- vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- rotate_ang[ROLL] += p->roll;
- AngleVectors ( rotate_ang, NULL, rr, ru);
- }
- else
- {
- VectorCopy (pvup, ru);
- VectorCopy (pvright, rr);
- }
-
- VectorMA (org, -p->height, ru, point);
- VectorMA (point, -p->width, rr, point);
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 111;
- verts[0].modulate[1] = 19;
- verts[0].modulate[2] = 9;
- verts[0].modulate[3] = 255 * alpha;
-
- VectorMA (org, -p->height, ru, point);
- VectorMA (point, p->width, rr, point);
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 111;
- verts[1].modulate[1] = 19;
- verts[1].modulate[2] = 9;
- verts[1].modulate[3] = 255 * alpha;
-
- VectorMA (org, p->height, ru, point);
- VectorMA (point, p->width, rr, point);
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 111;
- verts[2].modulate[1] = 19;
- verts[2].modulate[2] = 9;
- verts[2].modulate[3] = 255 * alpha;
-
- VectorMA (org, p->height, ru, point);
- VectorMA (point, -p->width, rr, point);
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 111;
- verts[3].modulate[1] = 19;
- verts[3].modulate[2] = 9;
- verts[3].modulate[3] = 255 * alpha;
-
- }
- else if (p->type == P_FLAT_SCALEUP)
- {
- float width, height;
- float sinR, cosR;
-
- if (p->color == BLOODRED)
- VectorSet (color, 1, 1, 1);
- else
- VectorSet (color, 0.5, 0.5, 0.5);
-
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
-
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
-
- if (width > p->endwidth)
- width = p->endwidth;
-
- if (height > p->endheight)
- height = p->endheight;
-
- sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2);
- cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2);
-
- VectorCopy (org, verts[0].xyz);
- verts[0].xyz[0] -= sinR;
- verts[0].xyz[1] -= cosR;
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255 * color[0];
- verts[0].modulate[1] = 255 * color[1];
- verts[0].modulate[2] = 255 * color[2];
- verts[0].modulate[3] = 255;
-
- VectorCopy (org, verts[1].xyz);
- verts[1].xyz[0] -= cosR;
- verts[1].xyz[1] += sinR;
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255 * color[0];
- verts[1].modulate[1] = 255 * color[1];
- verts[1].modulate[2] = 255 * color[2];
- verts[1].modulate[3] = 255;
-
- VectorCopy (org, verts[2].xyz);
- verts[2].xyz[0] += sinR;
- verts[2].xyz[1] += cosR;
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255 * color[0];
- verts[2].modulate[1] = 255 * color[1];
- verts[2].modulate[2] = 255 * color[2];
- verts[2].modulate[3] = 255;
-
- VectorCopy (org, verts[3].xyz);
- verts[3].xyz[0] += cosR;
- verts[3].xyz[1] -= sinR;
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255 * color[0];
- verts[3].modulate[1] = 255 * color[1];
- verts[3].modulate[2] = 255 * color[2];
- verts[3].modulate[3] = 255;
- }
- else if (p->type == P_FLAT)
- {
-
- VectorCopy (org, verts[0].xyz);
- verts[0].xyz[0] -= p->height;
- verts[0].xyz[1] -= p->width;
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
-
- VectorCopy (org, verts[1].xyz);
- verts[1].xyz[0] -= p->height;
- verts[1].xyz[1] += p->width;
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
-
- VectorCopy (org, verts[2].xyz);
- verts[2].xyz[0] += p->height;
- verts[2].xyz[1] += p->width;
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
-
- VectorCopy (org, verts[3].xyz);
- verts[3].xyz[0] += p->height;
- verts[3].xyz[1] -= p->width;
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
-
- }
- // Ridah
- else if (p->type == P_ANIM) {
- vec3_t rr, ru;
- vec3_t rotate_ang;
- int i, j;
-
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
- if (ratio >= 1.0f) {
- ratio = 0.9999f;
- }
-
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
-
- // if we are "inside" this sprite, don't draw
- if (Distance( cg.snap->ps.origin, org ) < width/1.5) {
- return;
- }
-
- i = p->shaderAnim;
- j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]);
- p->pshader = shaderAnims[i][j];
-
- if (p->roll) {
- vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- rotate_ang[ROLL] += p->roll;
- AngleVectors ( rotate_ang, NULL, rr, ru);
- }
-
- if (p->roll) {
- VectorMA (org, -height, ru, point);
- VectorMA (point, -width, rr, point);
- } else {
- VectorMA (org, -height, pvup, point);
- VectorMA (point, -width, pvright, point);
- }
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*height, ru, point);
- } else {
- VectorMA (point, 2*height, pvup, point);
- }
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*width, rr, point);
- } else {
- VectorMA (point, 2*width, pvright, point);
- }
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, -2*height, ru, point);
- } else {
- VectorMA (point, -2*height, pvup, point);
- }
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
- }
- // done.
-
- if (!p->pshader) {
-// (SA) temp commented out for DM
-// CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type);
- return;
- }
-
- if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY)
- trap_R_AddPolyToScene( p->pshader, 3, TRIverts );
- else
- trap_R_AddPolyToScene( p->pshader, 4, verts );
-
-}
-
-// Ridah, made this static so it doesn't interfere with other files
-static float roll = 0.0;
-
-/*
-===============
-CG_AddParticles
-===============
-*/
-void CG_AddParticles (void)
-{
- cparticle_t *p, *next;
- float alpha;
- float time, time2;
- vec3_t org;
- int color;
- cparticle_t *active, *tail;
- int type;
- vec3_t rotate_ang;
-
- if (!initparticles)
- CG_ClearParticles ();
-
- VectorCopy( cg.refdef.viewaxis[0], pvforward );
- VectorCopy( cg.refdef.viewaxis[1], pvright );
- VectorCopy( cg.refdef.viewaxis[2], pvup );
-
- vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- roll += ((cg.time - oldtime) * 0.1) ;
- rotate_ang[ROLL] += (roll*0.9);
- AngleVectors ( rotate_ang, rforward, rright, rup);
-
- oldtime = cg.time;
-
- active = NULL;
- tail = NULL;
-
- for (p=active_particles ; p ; p=next)
- {
-
- next = p->next;
-
- time = (cg.time - p->time)*0.001;
-
- alpha = p->alpha + time*p->alphavel;
- if (alpha <= 0)
- { // faded out
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
- continue;
- }
-
- if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT)
- {
- if (cg.time > p->endtime)
- {
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
-
- continue;
- }
-
- }
-
- if (p->type == P_WEATHER_FLURRY)
- {
- if (cg.time > p->endtime)
- {
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
-
- continue;
- }
- }
-
-
- if (p->type == P_FLAT_SCALEUP_FADE)
- {
- if (cg.time > p->endtime)
- {
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
- continue;
- }
-
- }
-
- if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) {
- // temporary sprite
- CG_AddParticleToScene (p, p->org, alpha);
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
- continue;
- }
-
- p->next = NULL;
- if (!tail)
- active = tail = p;
- else
- {
- tail->next = p;
- tail = p;
- }
-
- if (alpha > 1.0)
- alpha = 1;
-
- color = p->color;
-
- time2 = time*time;
-
- org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2;
- org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2;
- org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2;
-
- type = p->type;
-
- CG_AddParticleToScene (p, org, alpha);
- }
-
- active_particles = active;
-}
-
-/*
-======================
-CG_AddParticles
-======================
-*/
-void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent)
-{
- cparticle_t *p;
- qboolean turb = qtrue;
-
- if (!pshader)
- CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->color = 0;
- p->alpha = 0.90f;
- p->alphavel = 0;
-
- p->start = cent->currentState.origin2[0];
- p->end = cent->currentState.origin2[1];
-
- p->endtime = cg.time + cent->currentState.time;
- p->startfade = cg.time + cent->currentState.time2;
-
- p->pshader = pshader;
-
- if (rand()%100 > 90)
- {
- p->height = 32;
- p->width = 32;
- p->alpha = 0.10f;
- }
- else
- {
- p->height = 1;
- p->width = 1;
- }
-
- p->vel[2] = -20;
-
- p->type = P_WEATHER_FLURRY;
-
- if (turb)
- p->vel[2] = -10;
-
- VectorCopy(cent->currentState.origin, p->org);
-
- p->org[0] = p->org[0];
- p->org[1] = p->org[1];
- p->org[2] = p->org[2];
-
- p->vel[0] = p->vel[1] = 0;
-
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16);
- p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16);
- p->vel[2] += cent->currentState.angles[2];
-
- if (turb)
- {
- p->accel[0] = crandom () * 16;
- p->accel[1] = crandom () * 16;
- }
-
-}
-
-void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum)
-{
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_ParticleSnow pshader == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->color = 0;
- p->alpha = 0.40f;
- p->alphavel = 0;
- p->start = origin[2];
- p->end = origin2[2];
- p->pshader = pshader;
- p->height = 1;
- p->width = 1;
-
- p->vel[2] = -50;
-
- if (turb)
- {
- p->type = P_WEATHER_TURBULENT;
- p->vel[2] = -50 * 1.3;
- }
- else
- {
- p->type = P_WEATHER;
- }
-
- VectorCopy(origin, p->org);
-
- p->org[0] = p->org[0] + ( crandom() * range);
- p->org[1] = p->org[1] + ( crandom() * range);
- p->org[2] = p->org[2] + ( crandom() * (p->start - p->end));
-
- p->vel[0] = p->vel[1] = 0;
-
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- if (turb)
- {
- p->vel[0] = crandom() * 16;
- p->vel[1] = crandom() * 16;
- }
-
- // Rafael snow pvs check
- p->snum = snum;
- p->link = qtrue;
-
-}
-
-void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum)
-{
- cparticle_t *p;
- float randsize;
-
- if (!pshader)
- CG_Printf ("CG_ParticleSnow pshader == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->color = 0;
- p->alpha = 0.40f;
- p->alphavel = 0;
- p->start = origin[2];
- p->end = origin2[2];
- p->pshader = pshader;
-
- randsize = 1 + (crandom() * 0.5);
-
- p->height = randsize;
- p->width = randsize;
-
- p->vel[2] = 50 + ( crandom() * 10 );
-
- if (turb)
- {
- p->type = P_BUBBLE_TURBULENT;
- p->vel[2] = 50 * 1.3;
- }
- else
- {
- p->type = P_BUBBLE;
- }
-
- VectorCopy(origin, p->org);
-
- p->org[0] = p->org[0] + ( crandom() * range);
- p->org[1] = p->org[1] + ( crandom() * range);
- p->org[2] = p->org[2] + ( crandom() * (p->start - p->end));
-
- p->vel[0] = p->vel[1] = 0;
-
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- if (turb)
- {
- p->vel[0] = crandom() * 4;
- p->vel[1] = crandom() * 4;
- }
-
- // Rafael snow pvs check
- p->snum = snum;
- p->link = qtrue;
-
-}
-
-void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent)
-{
-
- // using cent->density = enttime
- // cent->frame = startfade
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_ParticleSmoke == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + cent->currentState.time;
- p->startfade = cg.time + cent->currentState.time2;
-
- p->color = 0;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->start = cent->currentState.origin[2];
- p->end = cent->currentState.origin2[2];
- p->pshader = pshader;
- p->rotate = qfalse;
- p->height = 8;
- p->width = 8;
- p->endheight = 32;
- p->endwidth = 32;
- p->type = P_SMOKE;
-
- VectorCopy(cent->currentState.origin, p->org);
-
- p->vel[0] = p->vel[1] = 0;
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- p->vel[2] = 5;
-
- if (cent->currentState.frame == 1)// reverse gravity
- p->vel[2] *= -1;
-
- p->roll = 8 + (crandom() * 4);
-}
-
-
-void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration)
-{
-
- cparticle_t *p;
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + duration;
- p->startfade = cg.time + duration/2;
-
- p->color = EMISIVEFADE;
- p->alpha = 1.0;
- p->alphavel = 0;
-
- p->height = 0.5;
- p->width = 0.5;
- p->endheight = 0.5;
- p->endwidth = 0.5;
-
- p->pshader = cgs.media.tracerShader;
-
- p->type = P_SMOKE;
-
- VectorCopy(org, p->org);
-
- p->vel[0] = vel[0];
- p->vel[1] = vel[1];
- p->vel[2] = vel[2];
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- p->accel[2] = -60;
- p->vel[2] += -20;
-
-}
-
-/*
-======================
-CG_ParticleExplosion
-======================
-*/
-
-void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd)
-{
- cparticle_t *p;
- int anim;
-
- if (animStr < (char *)10)
- CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" );
-
- // find the animation string
- for (anim=0; shaderAnimNames[anim]; anim++) {
- if (!Q_stricmp( animStr, shaderAnimNames[anim] ))
- break;
- }
- if (!shaderAnimNames[anim]) {
- CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr);
- return;
- }
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->alpha = 0.5;
- p->alphavel = 0;
-
- if (duration < 0) {
- duration *= -1;
- p->roll = 0;
- } else {
- p->roll = crandom()*179;
- }
-
- p->shaderAnim = anim;
-
- p->width = sizeStart;
- p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction
-
- p->endheight = sizeEnd;
- p->endwidth = sizeEnd*shaderAnimSTRatio[anim];
-
- p->endtime = cg.time + duration;
-
- p->type = P_ANIM;
-
- VectorCopy( origin, p->org );
- VectorCopy( vel, p->vel );
- VectorClear( p->accel );
-
-}
-
-// Rafael Shrapnel
-void CG_AddParticleShrapnel (localEntity_t *le)
-{
- return;
-}
-// done.
-
-int CG_NewParticleArea (int num)
-{
- // const char *str;
- char *str;
- char *token;
- int type;
- vec3_t origin, origin2;
- int i;
- float range = 0;
- int turb;
- int numparticles;
- int snum;
-
- str = (char *) CG_ConfigString (num);
- if (!str[0])
- return (0);
-
- // returns type 128 64 or 32
- token = COM_Parse (&str);
- type = atoi (token);
-
- if (type == 1)
- range = 128;
- else if (type == 2)
- range = 64;
- else if (type == 3)
- range = 32;
- else if (type == 0)
- range = 256;
- else if (type == 4)
- range = 8;
- else if (type == 5)
- range = 16;
- else if (type == 6)
- range = 32;
- else if (type == 7)
- range = 64;
-
-
- for (i=0; i<3; i++)
- {
- token = COM_Parse (&str);
- origin[i] = atof (token);
- }
-
- for (i=0; i<3; i++)
- {
- token = COM_Parse (&str);
- origin2[i] = atof (token);
- }
-
- token = COM_Parse (&str);
- numparticles = atoi (token);
-
- token = COM_Parse (&str);
- turb = atoi (token);
-
- token = COM_Parse (&str);
- snum = atoi (token);
-
- for (i=0; i= 4)
- CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum);
- else
- CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum);
- }
-
- return (1);
-}
-
-void CG_SnowLink (centity_t *cent, qboolean particleOn)
-{
- cparticle_t *p, *next;
- int id;
-
- id = cent->currentState.frame;
-
- for (p=active_particles ; p ; p=next)
- {
- next = p->next;
-
- if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT)
- {
- if (p->snum == id)
- {
- if (particleOn)
- p->link = qtrue;
- else
- p->link = qfalse;
- }
- }
-
- }
-}
-
-void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin)
-{
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->alpha = 0.25;
- p->alphavel = 0;
- p->roll = crandom()*179;
-
- p->pshader = pshader;
-
- p->endtime = cg.time + 1000;
- p->startfade = cg.time + 100;
-
- p->width = rand()%4 + 8;
- p->height = rand()%4 + 8;
-
- p->endheight = p->height *2;
- p->endwidth = p->width * 2;
-
- p->endtime = cg.time + 500;
-
- p->type = P_SMOKE_IMPACT;
-
- VectorCopy( origin, p->org );
- VectorSet(p->vel, 0, 0, 20);
- VectorSet(p->accel, 0, 0, 20);
-
- p->rotate = qtrue;
-}
-
-void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration)
-{
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = pshader;
-
- p->endtime = cg.time + duration;
-
- if (fleshEntityNum)
- p->startfade = cg.time;
- else
- p->startfade = cg.time + 100;
-
- p->width = 4;
- p->height = 4;
-
- p->endheight = 4+rand()%3;
- p->endwidth = p->endheight;
-
- p->type = P_SMOKE;
-
- VectorCopy( start, p->org );
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = -20;
- VectorClear( p->accel );
-
- p->rotate = qfalse;
-
- p->roll = rand()%179;
-
- p->color = BLOODRED;
- p->alpha = 0.75;
-
-}
-
-void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent)
-{
- cparticle_t *p;
-
- int time;
- int time2;
- float ratio;
-
- float duration = 1500;
-
- time = cg.time;
- time2 = cg.time + cent->currentState.time;
-
- ratio =(float)1 - ((float)time / (float)time2);
-
- if (!pshader)
- CG_Printf ("CG_Particle_OilParticle == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = pshader;
-
- p->endtime = cg.time + duration;
-
- p->startfade = p->endtime;
-
- p->width = 1;
- p->height = 3;
-
- p->endheight = 3;
- p->endwidth = 1;
-
- p->type = P_SMOKE;
-
- VectorCopy(cent->currentState.origin, p->org );
-
- p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio));
- p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio));
- p->vel[2] = (cent->currentState.origin2[2]);
-
- p->snum = 1.0f;
-
- VectorClear( p->accel );
-
- p->accel[2] = -20;
-
- p->rotate = qfalse;
-
- p->roll = rand()%179;
-
- p->alpha = 0.75;
-
-}
-
-
-void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent)
-{
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_Particle_OilSlick == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
-
- if (cent->currentState.angles2[2])
- p->endtime = cg.time + cent->currentState.angles2[2];
- else
- p->endtime = cg.time + 60000;
-
- p->startfade = p->endtime;
-
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = pshader;
-
- if (cent->currentState.angles2[0] || cent->currentState.angles2[1])
- {
- p->width = cent->currentState.angles2[0];
- p->height = cent->currentState.angles2[0];
-
- p->endheight = cent->currentState.angles2[1];
- p->endwidth = cent->currentState.angles2[1];
- }
- else
- {
- p->width = 8;
- p->height = 8;
-
- p->endheight = 16;
- p->endwidth = 16;
- }
-
- p->type = P_FLAT_SCALEUP;
-
- p->snum = 1.0;
-
- VectorCopy(cent->currentState.origin, p->org );
-
- p->org[2]+= 0.55 + (crandom() * 0.5);
-
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = 0;
- VectorClear( p->accel );
-
- p->rotate = qfalse;
-
- p->roll = rand()%179;
-
- p->alpha = 0.75;
-
-}
-
-void CG_OilSlickRemove (centity_t *cent)
-{
- cparticle_t *p, *next;
- int id;
-
- id = 1.0f;
-
- if (!id)
- CG_Printf ("CG_OilSlickRevove NULL id\n");
-
- for (p=active_particles ; p ; p=next)
- {
- next = p->next;
-
- if (p->type == P_FLAT_SCALEUP)
- {
- if (p->snum == id)
- {
- p->endtime = cg.time + 100;
- p->startfade = p->endtime;
- p->type = P_FLAT_SCALEUP_FADE;
-
- }
- }
-
- }
-}
-
-qboolean ValidBloodPool (vec3_t start)
-{
-#define EXTRUDE_DIST 0.5
-
- vec3_t angles;
- vec3_t right, up;
- vec3_t this_pos, x_pos, center_pos, end_pos;
- float x, y;
- float fwidth, fheight;
- trace_t trace;
- vec3_t normal;
-
- fwidth = 16;
- fheight = 16;
-
- VectorSet (normal, 0, 0, 1);
-
- vectoangles (normal, angles);
- AngleVectors (angles, NULL, right, up);
-
- VectorMA (start, EXTRUDE_DIST, normal, center_pos);
-
- for (x= -fwidth/2; xendpos, start);
- legit = ValidBloodPool (start);
-
- if (!legit)
- return;
-
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + 3000;
- p->startfade = p->endtime;
-
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = pshader;
-
- rndSize = 0.4 + random()*0.6;
-
- p->width = 8*rndSize;
- p->height = 8*rndSize;
-
- p->endheight = 16*rndSize;
- p->endwidth = 16*rndSize;
-
- p->type = P_FLAT_SCALEUP;
-
- VectorCopy(start, p->org );
-
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = 0;
- VectorClear( p->accel );
-
- p->rotate = qfalse;
-
- p->roll = rand()%179;
-
- p->alpha = 0.75;
-
- p->color = BLOODRED;
-}
-
-#define NORMALSIZE 16
-#define LARGESIZE 32
-
-void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir)
-{
- float length;
- float dist;
- float crittersize;
- vec3_t angles, forward;
- vec3_t point;
- cparticle_t *p;
- int i;
-
- dist = 0;
-
- length = VectorLength (dir);
- vectoangles (dir, angles);
- AngleVectors (angles, forward, NULL, NULL);
-
- crittersize = LARGESIZE;
-
- if (length)
- dist = length / crittersize;
-
- if (dist < 1)
- dist = 1;
-
- VectorCopy (origin, point);
-
- for (i=0; inext;
- p->next = active_particles;
- active_particles = p;
-
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = cgs.media.smokePuffShader;
-
- p->endtime = cg.time + 350 + (crandom() * 100);
-
- p->startfade = cg.time;
-
- p->width = LARGESIZE;
- p->height = LARGESIZE;
- p->endheight = LARGESIZE;
- p->endwidth = LARGESIZE;
-
- p->type = P_SMOKE;
-
- VectorCopy( origin, p->org );
-
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = -1;
-
- VectorClear( p->accel );
-
- p->rotate = qfalse;
-
- p->roll = rand()%179;
-
- p->color = BLOODRED;
-
- p->alpha = 0.75;
-
- }
-
-
-}
-
-void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed)
-{
- cparticle_t *p;
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + duration;
- p->startfade = cg.time + duration/2;
-
- p->color = EMISIVEFADE;
- p->alpha = 0.4f;
- p->alphavel = 0;
-
- p->height = 0.5;
- p->width = 0.5;
- p->endheight = 0.5;
- p->endwidth = 0.5;
-
- p->pshader = cgs.media.tracerShader;
-
- p->type = P_SMOKE;
-
- VectorCopy(org, p->org);
-
- p->org[0] += (crandom() * x);
- p->org[1] += (crandom() * y);
-
- p->vel[0] = vel[0];
- p->vel[1] = vel[1];
- p->vel[2] = vel[2];
-
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- p->vel[0] += (crandom() * 4);
- p->vel[1] += (crandom() * 4);
- p->vel[2] += (20 + (crandom() * 10)) * speed;
-
- p->accel[0] = crandom () * 4;
- p->accel[1] = crandom () * 4;
-
-}
-
-void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir)
-{
- float length;
- float dist;
- float crittersize;
- vec3_t angles, forward;
- vec3_t point;
- cparticle_t *p;
- int i;
-
- dist = 0;
-
- VectorNegate (dir, dir);
- length = VectorLength (dir);
- vectoangles (dir, angles);
- AngleVectors (angles, forward, NULL, NULL);
-
- crittersize = LARGESIZE;
-
- if (length)
- dist = length / crittersize;
-
- if (dist < 1)
- dist = 1;
-
- VectorCopy (origin, point);
-
- for (i=0; inext;
- p->next = active_particles;
- active_particles = p;
-
- p->time = cg.time;
- p->alpha = 5.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = cgs.media.smokePuffShader;
-
- // RF, stay around for long enough to expand and dissipate naturally
- if (length)
- p->endtime = cg.time + 4500 + (crandom() * 3500);
- else
- p->endtime = cg.time + 750 + (crandom() * 500);
-
- p->startfade = cg.time;
-
- p->width = LARGESIZE;
- p->height = LARGESIZE;
-
- // RF, expand while falling
- p->endheight = LARGESIZE*3.0;
- p->endwidth = LARGESIZE*3.0;
-
- if (!length)
- {
- p->width *= 0.2f;
- p->height *= 0.2f;
-
- p->endheight = NORMALSIZE;
- p->endwidth = NORMALSIZE;
- }
-
- p->type = P_SMOKE;
-
- VectorCopy( point, p->org );
-
- p->vel[0] = crandom()*6;
- p->vel[1] = crandom()*6;
- p->vel[2] = random()*20;
-
- // RF, add some gravity/randomness
- p->accel[0] = crandom()*3;
- p->accel[1] = crandom()*3;
- p->accel[2] = -PARTICLE_GRAVITY*0.4;
-
- VectorClear( p->accel );
-
- p->rotate = qfalse;
-
- p->roll = rand()%179;
-
- p->alpha = 0.75;
-
- }
-
-
-}
-
-void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha)
-{
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n");
-
- if (!free_particles)
- return;
-
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = rand()%179;
-
- p->pshader = pshader;
-
- if (duration > 0)
- p->endtime = cg.time + duration;
- else
- p->endtime = duration;
-
- p->startfade = cg.time;
-
- p->width = size;
- p->height = size;
-
- p->endheight = size;
- p->endwidth = size;
-
- p->type = P_SPRITE;
-
- VectorCopy( origin, p->org );
-
- p->rotate = qfalse;
-}
-
diff --git a/source/cgame/cg_particles.c b/source/cgame/cg_particles.c
index d9c3e7e..76681a6 100644
--- a/source/cgame/cg_particles.c
+++ b/source/cgame/cg_particles.c
@@ -22,6 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// Rafael particles
// cg_particles.c
+// BFP - HIGHLY MODIFIED
+
#include "cg_local.h"
#define BLOODRED 2
@@ -39,15 +41,14 @@ typedef struct particle_s
vec3_t vel;
vec3_t accel;
int color;
- float colorvel;
float alpha;
float alphavel;
int type;
qhandle_t pshader;
-
+
float height;
float width;
-
+
float endheight;
float endwidth;
@@ -56,82 +57,60 @@ typedef struct particle_s
float startfade;
qboolean rotate;
- int snum;
+ int custom;
- qboolean link;
+ qboolean stopped;
// Ridah
- int shaderAnim;
- int roll;
+ int rollBounceCount;
int accumroll;
+ // BFP - Entity num
+ int entityNum;
+
+ // BFP - Model
+ qhandle_t pmodel;
} cparticle_t;
typedef enum
{
P_NONE,
- P_WEATHER,
- P_FLAT,
+ P_ANTIGRAV_ROCK, // BFP - Antigrav rock particles
P_SMOKE,
- P_ROTATE,
- P_WEATHER_TURBULENT,
- P_ANIM, // Ridah
- P_BAT,
- P_BLEED,
- P_FLAT_SCALEUP,
- P_FLAT_SCALEUP_FADE,
- P_WEATHER_FLURRY,
- P_SMOKE_IMPACT,
P_BUBBLE,
P_BUBBLE_TURBULENT,
+ P_AURA, // BFP - Particle aura
+ P_SPARK, // BFP - Beam struggle spark
+ P_ROCK_DEBRIS, // BFP - Bouncing rock fragment (explosion impact on ground)
+ P_WATER_SPLASH, // BFP - Water entry splash bubble (upward arc, stops at surface)
P_SPRITE
} particle_type_t;
-#define MAX_SHADER_ANIMS 32
-#define MAX_SHADER_ANIM_FRAMES 64
-
-static char *shaderAnimNames[MAX_SHADER_ANIMS] = {
- "explode1",
- "blacksmokeanim",
- "twiltb2",
- "expblue",
- "blacksmokeanimb", // uses 'explode1' sequence
- "blood",
- NULL
-};
-static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES];
-static int shaderAnimCounts[MAX_SHADER_ANIMS] = {
- 23,
- 25,
- 45,
- 25,
- 23,
- 5,
-};
-static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = {
- 1.405f,
- 1.0f,
- 1.0f,
- 1.0f,
- 1.0f,
- 1.0f,
-};
-static int numShaderAnims;
-// done.
-
#define PARTICLE_GRAVITY 40
-#define MAX_PARTICLES 1024 * 8
+#define MAX_PARTICLES (1024 * 6)
+#define MAX_PARTICLES_3D (1024 * 3)
+
+static cparticle_t *active_particles, *free_particles;
+static cparticle_t particles[MAX_PARTICLES];
+static int cl_numparticles = MAX_PARTICLES;
+
+static qboolean initparticles = qfalse;
+static vec3_t vforward, vright, vup;
+static vec3_t rforward, rright, rup;
-cparticle_t *active_particles, *free_particles;
-cparticle_t particles[MAX_PARTICLES];
-int cl_numparticles = MAX_PARTICLES;
+static float oldtime;
-qboolean initparticles = qfalse;
-vec3_t vforward, vright, vup;
-vec3_t rforward, rright, rup;
+// BFP - NOTE: Particles use non-timescaled, before it was timescaled by game using cg.time
+static int timenonscaled;
-float oldtime;
+#define NORMALSIZE 16
+#define LARGESIZE 32
+
+// BFP - Function to handle bubbles
+static void CG_BubblesWaterHandling( cparticle_t *p, vec3_t org );
+// BFP - Function to handle charge smoke particles
+static void CG_ChargeSmokeHandling( cparticle_t *p, vec3_t org );
/*
===============
@@ -142,6 +121,10 @@ void CG_ClearParticles (void)
{
int i;
+ // BFP - Limit the pool when 3D particles are active; the full array is
+ // always allocated but only the first cl_numparticles slots are chained
+ cl_numparticles = ( cg_3dparticles.integer > 0 ) ? MAX_PARTICLES_3D : MAX_PARTICLES;
+
memset( particles, 0, sizeof(particles) );
free_particles = &particles[0];
@@ -154,671 +137,527 @@ void CG_ClearParticles (void)
}
particles[cl_numparticles-1].next = NULL;
- oldtime = cg.time;
-
- // Ridah, init the shaderAnims
- for (i=0; shaderAnimNames[i]; i++) {
- int j;
-
- for (j=0; jtype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY
- || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT)
- {// create a front facing polygon
-
- if (p->type != P_WEATHER_FLURRY)
- {
- if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT)
- {
- if (org[2] > p->end)
- {
- p->time = cg.time;
- VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground
-
- p->org[2] = ( p->start + crandom () * 4 );
-
-
- if (p->type == P_BUBBLE_TURBULENT)
- {
- p->vel[0] = crandom() * 4;
- p->vel[1] = crandom() * 4;
- }
-
- }
- }
- else
- {
- if (org[2] < p->end)
- {
- p->time = cg.time;
- VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground
-
- while (p->org[2] < p->end)
- {
- p->org[2] += (p->start - p->end);
- }
-
-
- if (p->type == P_WEATHER_TURBULENT)
- {
- p->vel[0] = crandom() * 16;
- p->vel[1] = crandom() * 16;
- }
-
- }
- }
-
+ if ( !p->pshader ) {
+ return;
+ }
- // Rafael snow pvs check
- if (!p->link)
- return;
+ time = timenonscaled - p->time;
+ time2 = p->endtime - p->time;
+ ratio = time / time2;
- p->alpha = 1;
- }
-
- // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp
- if (Distance( cg.snap->ps.origin, org ) > 1024) {
- return;
- }
- // done.
-
- if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT)
- {
- VectorMA (org, -p->height, vup, point);
- VectorMA (point, -p->width, vright, point);
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, -p->height, vup, point);
- VectorMA (point, p->width, vright, point);
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, vup, point);
- VectorMA (point, p->width, vright, point);
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, vup, point);
- VectorMA (point, -p->width, vright, point);
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255 * p->alpha;
- }
- else
- {
- VectorMA (org, -p->height, vup, point);
- VectorMA (point, -p->width, vright, point);
- VectorCopy( point, TRIverts[0].xyz );
- TRIverts[0].st[0] = 1;
- TRIverts[0].st[1] = 0;
- TRIverts[0].modulate[0] = 255;
- TRIverts[0].modulate[1] = 255;
- TRIverts[0].modulate[2] = 255;
- TRIverts[0].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, vup, point);
- VectorMA (point, -p->width, vright, point);
- VectorCopy (point, TRIverts[1].xyz);
- TRIverts[1].st[0] = 0;
- TRIverts[1].st[1] = 0;
- TRIverts[1].modulate[0] = 255;
- TRIverts[1].modulate[1] = 255;
- TRIverts[1].modulate[2] = 255;
- TRIverts[1].modulate[3] = 255 * p->alpha;
-
- VectorMA (org, p->height, vup, point);
- VectorMA (point, p->width, vright, point);
- VectorCopy (point, TRIverts[2].xyz);
- TRIverts[2].st[0] = 0;
- TRIverts[2].st[1] = 1;
- TRIverts[2].modulate[0] = 255;
- TRIverts[2].modulate[1] = 255;
- TRIverts[2].modulate[2] = 255;
- TRIverts[2].modulate[3] = 255 * p->alpha;
- }
-
+ width = p->width + ratio * ( p->endwidth - p->width );
+ height = p->height + ratio * ( p->endheight - p->height );
+
+ if ( p->rollBounceCount )
+ {
+ vectoangles( cg.refdef.viewaxis[0], rotate_ang );
+ rotate_ang[ROLL] += p->rollBounceCount;
+ AngleVectors( rotate_ang, NULL, rr, ru );
+ }
+
+ if ( p->rollBounceCount )
+ {
+ VectorMA( org, -height, ru, point );
+ VectorMA( point, -width, rr, point );
}
- else if (p->type == P_SPRITE)
+ else
{
- vec3_t rr, ru;
- vec3_t rotate_ang;
+ VectorMA( org, -height, vup, point );
+ VectorMA( point, -width, vright, point );
+ }
+ VectorCopy( point, verts[0].xyz );
+ Vector2Set( verts[0].st, 0, 0 );
+ Byte4Set( verts[0].modulate, 255, 255, 255, 255 );
- VectorSet (color, 1.0, 1.0, 1.0);
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
+ if ( p->rollBounceCount )
+ {
+ VectorMA( point, 2*height, ru, point );
+ }
+ else
+ {
+ VectorMA( point, 2*height, vup, point );
+ }
+ VectorCopy( point, verts[1].xyz );
+ Vector2Set( verts[1].st, 0, 1 );
+ Byte4Set( verts[1].modulate, 255, 255, 255, 255 );
+
+ if ( p->rollBounceCount )
+ {
+ VectorMA( point, 2*width, rr, point );
+ }
+ else
+ {
+ VectorMA( point, 2*width, vright, point );
+ }
+ VectorCopy( point, verts[2].xyz );
+ Vector2Set( verts[2].st, 1, 1 );
+ Byte4Set( verts[2].modulate, 255, 255, 255, 255 );
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
+ if ( p->rollBounceCount )
+ {
+ VectorMA( point, -2*height, ru, point );
+ }
+ else
+ {
+ VectorMA( point, -2*height, vup, point );
+ }
+ VectorCopy( point, verts[3].xyz );
+ Vector2Set( verts[3].st, 1, 0 );
+ Byte4Set( verts[3].modulate, 255, 255, 255, 255 );
- if (p->roll) {
- vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- rotate_ang[ROLL] += p->roll;
- AngleVectors ( rotate_ang, NULL, rr, ru);
- }
+ trap_R_AddPolyToScene( p->pshader, 4, verts );
+}
- if (p->roll) {
- VectorMA (org, -height, ru, point);
- VectorMA (point, -width, rr, point);
- } else {
- VectorMA (org, -height, vup, point);
- VectorMA (point, -width, vright, point);
- }
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*height, ru, point);
- } else {
- VectorMA (point, 2*height, vup, point);
- }
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*width, rr, point);
- } else {
- VectorMA (point, 2*width, vright, point);
- }
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, -2*height, ru, point);
- } else {
- VectorMA (point, -2*height, vup, point);
- }
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
+/*
+===================
+AddSparkParticle
+===================
+*/
+static void AddSparkParticle( cparticle_t *p, vec3_t org, float alpha )
+{
+ vec3_t forward, right, endPoint;
+ polyVert_t verts[4];
+ float lifeFraction = (p->endtime - timenonscaled) / (p->endtime - p->time);
+ const float SPARK_MAX_LENGTH = 50.0f;
+ const float SPARK_MIN_LENGTH = 0.5f;
+ const float SPARK_MIN_WIDTH = 1.0f;
+ float length = SPARK_MIN_LENGTH + (SPARK_MAX_LENGTH - SPARK_MIN_LENGTH) * lifeFraction;
+ float currentWidth = SPARK_MIN_WIDTH + (p->width - SPARK_MIN_WIDTH) * lifeFraction;
+
+ if ( !p->pshader ) {
+ return;
}
- else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT)
- {// create a front rotating facing polygon
- if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) {
- return;
- }
+ p->width = currentWidth;
- if (p->color == BLOODRED)
- VectorSet (color, 0.22f, 0.0f, 0.0f);
- else if (p->color == GREY75)
- {
- float len;
- float greyit;
- float val;
- len = Distance (cg.snap->ps.origin, org);
- if (!len)
- len = 1;
-
- val = 4096/len;
- greyit = 0.25 * val;
- if (greyit > 0.5)
- greyit = 0.5;
-
- VectorSet (color, greyit, greyit, greyit);
- }
- else
- VectorSet (color, 1.0, 1.0, 1.0);
+ // direction of the particle
+ VectorNormalize2( p->vel, forward );
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
-
- if (cg.time > p->startfade)
- {
- invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) );
+ VectorMA( org, length, forward, endPoint );
+ PerpendicularVector( right, forward );
- if (p->color == EMISIVEFADE)
- {
- float fval;
- fval = (invratio * invratio);
- if (fval < 0)
- fval = 0;
- VectorSet (color, fval , fval , fval );
- }
- invratio *= p->alpha;
- }
- else
- invratio = 1 * p->alpha;
+ // bottom-left
+ VectorMA( org, -p->width, right, verts[0].xyz );
+ Vector2Set( verts[0].st, 0, 0 );
+ Byte4Set( verts[0].modulate, 255, 255, 255, 255 * alpha );
- if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO )
- invratio = 1;
+ // top-left
+ VectorMA( org, p->width, right, verts[1].xyz );
+ Vector2Set( verts[1].st, 0, 1 );
+ Byte4Set( verts[1].modulate, 255, 255, 255, 255 * alpha );
- if (invratio > 1)
- invratio = 1;
-
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
+ // top-right (tip narrower)
+ VectorMA( endPoint, p->width * 0.2f, right, verts[2].xyz );
+ Vector2Set( verts[2].st, 1, 1 );
+ Byte4Set( verts[2].modulate, 255, 255, 255, 255 * alpha );
- if (p->type != P_SMOKE_IMPACT)
- {
- vec3_t temp;
+ // bottom-right = top-right (triangle)
+ VectorCopy( verts[2].xyz, verts[3].xyz );
+ Vector2Set( verts[3].st, 1, 0 );
+ Byte4Set( verts[3].modulate, 255, 255, 255, 255 * alpha );
- vectoangles (rforward, temp);
- p->accumroll += p->roll;
- temp[ROLL] += p->accumroll * 0.1;
- AngleVectors ( temp, NULL, rright2, rup2);
- }
- else
- {
- VectorCopy (rright, rright2);
- VectorCopy (rup, rup2);
- }
-
- if (p->rotate)
- {
- VectorMA (org, -height, rup2, point);
- VectorMA (point, -width, rright2, point);
- }
- else
- {
- VectorMA (org, -p->height, vup, point);
- VectorMA (point, -p->width, vright, point);
- }
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255 * color[0];
- verts[0].modulate[1] = 255 * color[1];
- verts[0].modulate[2] = 255 * color[2];
- verts[0].modulate[3] = 255 * invratio;
-
- if (p->rotate)
- {
- VectorMA (org, -height, rup2, point);
- VectorMA (point, width, rright2, point);
- }
- else
- {
- VectorMA (org, -p->height, vup, point);
- VectorMA (point, p->width, vright, point);
- }
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255 * color[0];
- verts[1].modulate[1] = 255 * color[1];
- verts[1].modulate[2] = 255 * color[2];
- verts[1].modulate[3] = 255 * invratio;
-
- if (p->rotate)
- {
- VectorMA (org, height, rup2, point);
- VectorMA (point, width, rright2, point);
- }
- else
- {
- VectorMA (org, p->height, vup, point);
- VectorMA (point, p->width, vright, point);
- }
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255 * color[0];
- verts[2].modulate[1] = 255 * color[1];
- verts[2].modulate[2] = 255 * color[2];
- verts[2].modulate[3] = 255 * invratio;
-
- if (p->rotate)
- {
- VectorMA (org, height, rup2, point);
- VectorMA (point, -width, rright2, point);
- }
- else
+ trap_R_AddPolyToScene( p->pshader, 4, verts );
+}
+
+/*
+===================
+AddGenericParticle
+===================
+*/
+static void AddGenericParticle( cparticle_t *p, vec3_t org, float alpha )
+{
+ vec3_t point;
+ polyVert_t verts[4];
+ float width, height;
+ float time, time2, ratio, invratio;
+ vec3_t color = {1.0f, 1.0f, 1.0f};
+ vec3_t rright2, rup2;
+
+ time = timenonscaled - p->time;
+ time2 = p->endtime - p->time;
+ ratio = time / time2;
+
+ if (p->color == BLOODRED)
+ VectorSet (color, 0.22f, 0.0f, 0.0f);
+ else if (p->color == GREY75)
+ {
+ float len;
+ float greyit;
+ float val;
+ len = Distance (cg.snap->ps.origin, org);
+ if (!len)
+ len = 1;
+
+ val = 4096/len;
+ greyit = 0.25 * val;
+ if (greyit > 0.5)
+ greyit = 0.5;
+
+ VectorSet (color, greyit, greyit, greyit);
+ }
+ else
+ VectorSet (color, 1.0, 1.0, 1.0);
+
+ if ( timenonscaled > p->startfade )
+ {
+ invratio = 1.0f - ( (float)(timenonscaled - p->startfade) / (float)(p->endtime - p->startfade) );
+
+ if (p->color == EMISIVEFADE)
{
- VectorMA (org, p->height, vup, point);
- VectorMA (point, -p->width, vright, point);
+ float fval;
+ fval = (invratio * invratio);
+ if (fval < 0)
+ fval = 0;
+ VectorSet (color, fval , fval , fval );
}
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255 * color[0];
- verts[3].modulate[1] = 255 * color[1];
- verts[3].modulate[2] = 255 * color[2];
- verts[3].modulate[3] = 255 * invratio;
-
+ invratio *= p->alpha;
}
- else if (p->type == P_BLEED)
+ else
{
- vec3_t rr, ru;
- vec3_t rotate_ang;
- float alpha;
+ invratio = p->alpha;
+ }
- alpha = p->alpha;
-
- if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO )
- alpha = 1;
+ // BFP - Don't disappear opaquely the bubbles even the debris
+ if ( invratio > 1.0f
+ || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT
+ || p->type == P_ROCK_DEBRIS || p->type == P_WATER_SPLASH )
+ {
+ invratio = 1.0f;
+ }
- if (p->roll)
- {
- vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- rotate_ang[ROLL] += p->roll;
- AngleVectors ( rotate_ang, NULL, rr, ru);
+ width = p->width + ratio * ( p->endwidth - p->width );
+ height = p->height + ratio * ( p->endheight - p->height );
+
+ // compute rotated axes for non-smoke and non-aura types
+ if ( p->type != P_SMOKE && p->type != P_AURA
+ && p->type != P_BUBBLE && p->type != P_BUBBLE_TURBULENT
+ && p->type != P_WATER_SPLASH )
+ {
+ vec3_t temp;
+ vectoangles( rforward, temp );
+ if ( p->stopped ) {
+ p->rollBounceCount = 0;
}
- else
- {
- VectorCopy (vup, ru);
- VectorCopy (vright, rr);
+ if ( p->rollBounceCount > 0 ) {
+ p->accumroll += p->rollBounceCount;
+ temp[ROLL] += p->accumroll * 0.1f;
}
-
- VectorMA (org, -p->height, ru, point);
- VectorMA (point, -p->width, rr, point);
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 111;
- verts[0].modulate[1] = 19;
- verts[0].modulate[2] = 9;
- verts[0].modulate[3] = 255 * alpha;
-
- VectorMA (org, -p->height, ru, point);
- VectorMA (point, p->width, rr, point);
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 111;
- verts[1].modulate[1] = 19;
- verts[1].modulate[2] = 9;
- verts[1].modulate[3] = 255 * alpha;
-
- VectorMA (org, p->height, ru, point);
- VectorMA (point, p->width, rr, point);
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 111;
- verts[2].modulate[1] = 19;
- verts[2].modulate[2] = 9;
- verts[2].modulate[3] = 255 * alpha;
-
- VectorMA (org, p->height, ru, point);
- VectorMA (point, -p->width, rr, point);
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 111;
- verts[3].modulate[1] = 19;
- verts[3].modulate[2] = 9;
- verts[3].modulate[3] = 255 * alpha;
-
+ AngleVectors( temp, NULL, rright2, rup2 );
}
- else if (p->type == P_FLAT_SCALEUP)
+ else
{
- float width, height;
- float sinR, cosR;
-
- if (p->color == BLOODRED)
- VectorSet (color, 1, 1, 1);
- else
- VectorSet (color, 0.5, 0.5, 0.5);
-
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
-
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
-
- if (width > p->endwidth)
- width = p->endwidth;
-
- if (height > p->endheight)
- height = p->endheight;
-
- sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2);
- cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2);
-
- VectorCopy (org, verts[0].xyz);
- verts[0].xyz[0] -= sinR;
- verts[0].xyz[1] -= cosR;
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255 * color[0];
- verts[0].modulate[1] = 255 * color[1];
- verts[0].modulate[2] = 255 * color[2];
- verts[0].modulate[3] = 255;
-
- VectorCopy (org, verts[1].xyz);
- verts[1].xyz[0] -= cosR;
- verts[1].xyz[1] += sinR;
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255 * color[0];
- verts[1].modulate[1] = 255 * color[1];
- verts[1].modulate[2] = 255 * color[2];
- verts[1].modulate[3] = 255;
-
- VectorCopy (org, verts[2].xyz);
- verts[2].xyz[0] += sinR;
- verts[2].xyz[1] += cosR;
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255 * color[0];
- verts[2].modulate[1] = 255 * color[1];
- verts[2].modulate[2] = 255 * color[2];
- verts[2].modulate[3] = 255;
-
- VectorCopy (org, verts[3].xyz);
- verts[3].xyz[0] += cosR;
- verts[3].xyz[1] -= sinR;
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255 * color[0];
- verts[3].modulate[1] = 255 * color[1];
- verts[3].modulate[2] = 255 * color[2];
- verts[3].modulate[3] = 255;
+ VectorCopy( rright, rright2 );
+ VectorCopy( rup, rup2 );
}
- else if (p->type == P_FLAT)
+
+ // BFP - Bubble types here
+ if ( p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT )
{
+ CG_BubblesWaterHandling( p, org );
- VectorCopy (org, verts[0].xyz);
- verts[0].xyz[0] -= p->height;
- verts[0].xyz[1] -= p->width;
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
-
- VectorCopy (org, verts[1].xyz);
- verts[1].xyz[0] -= p->height;
- verts[1].xyz[1] += p->width;
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
-
- VectorCopy (org, verts[2].xyz);
- verts[2].xyz[0] += p->height;
- verts[2].xyz[1] += p->width;
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
-
- VectorCopy (org, verts[3].xyz);
- verts[3].xyz[0] += p->height;
- verts[3].xyz[1] -= p->width;
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
+ // BFP - Apply more end time to remove particles if the player stops charging
+ if ( p->type == P_BUBBLE ) {
+ if ( p->entityNum == cg.snap->ps.clientNum
+ && ( cg.snap->ps.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ && !p->stopped )
+ {
+ p->endtime = timenonscaled + 2500 + (crandom() * 150);
+ p->stopped = qtrue;
+ }
+ }
+ }
+ // BFP - Charge smoke particle handling here
+ if ( p->type == P_SMOKE && p->custom == 1 )
+ {
+ CG_ChargeSmokeHandling( p, org );
}
- // Ridah
- else if (p->type == P_ANIM) {
- vec3_t rr, ru;
- vec3_t rotate_ang;
- int i, j;
-
- time = cg.time - p->time;
- time2 = p->endtime - p->time;
- ratio = time / time2;
- if (ratio >= 1.0f) {
- ratio = 0.9999f;
- }
- width = p->width + ( ratio * ( p->endwidth - p->width) );
- height = p->height + ( ratio * ( p->endheight - p->height) );
+ // BFP - Antigrav rock type
+ if ( p->type == P_ANTIGRAV_ROCK )
+ {
+ // BFP - When the particle, checked to be fallen, won't be reactivated when entering ki charging status again
+ if ( p->stopped )
+ {
+ // BFP - To detect if there is something solid
+ trace_t trace;
+ vec3_t rockMins = {0, 0, -2}; // place a bit above
+ // contents should be CONTENTS_SOLID, so the particles don't touch any entity like the player
+ CG_Trace( &trace, p->org, rockMins, rockMins, org, -1, CONTENTS_SOLID );
+
+ p->time = timenonscaled;
+ p->custom = 1; // handle the p->custom when already entered in this phase for correction of client side visuals
+ // not hit anything or not a collider
+ if ( trace.fraction == 1.0f )
+ {
+ VectorCopy( org, p->org );
+ p->vel[2] -= 50;
+ p->accel[2] -= 200;
+ }
+ else // bouncing
+ {
+ if ( trace.plane.normal[2] >= 0.7f && Q_fabs( p->vel[2] ) < 1.0f ) {
+ // stop bouncing
+ VectorClear( p->vel );
+ VectorClear( p->accel );
+ VectorCopy( org, p->org );
+ }
+ else
+ {
+ // similar to CG_ReflectVelocity
+ const float BOUNCEFACTOR = 0.5f;
+ float dot = DotProduct( p->vel, trace.plane.normal );
+ if ( trace.plane.normal[2] < 0.91f && VectorLength( p->vel ) < 110.0f ) {
+ VectorMA( p->vel, 3.0f * dot, trace.plane.normal, p->vel );
+ VectorScale( p->vel, 3.0f, p->vel );
+ } else {
+ VectorMA( p->vel, -2.0f * dot, trace.plane.normal, p->vel );
+ VectorScale( p->vel, BOUNCEFACTOR, p->vel );
+ }
+ VectorCopy( trace.endpos, p->org );
+ }
+ }
+ }
- // if we are "inside" this sprite, don't draw
- if (Distance( cg.snap->ps.origin, org ) < width/1.5) {
+ // BFP - When reaching into this top, remove the particle!
+ if ( org[2] > p->end )
+ {
+ p->next = NULL;
+ p->color = p->alpha = 0;
return;
}
+ }
+ else if ( p->type == P_ROCK_DEBRIS || p->type == P_WATER_SPLASH )
+ {
+ // BFP - To detect if there is something solid
+ trace_t trace;
+ vec3_t debrisMins = {0, 0, -2}; // place a bit above
+ int contents;
+ // contents should be CONTENTS_SOLID, so the particles don't touch any entity like the player
+ CG_Trace( &trace, p->org, debrisMins, debrisMins, org, -1, CONTENTS_SOLID );
+
+ // keep detecting the position, also helps to pass through map bounds
+ VectorCopy( org, p->org );
+
+ p->time = timenonscaled;
+ // not hit anything or not a collider
+ contents = trap_CM_PointContents( trace.endpos, 0 );
+
+ // bouncing rock
+ if ( p->rollBounceCount <= 0 && !p->stopped ) { // stop
+ VectorClear( p->accel );
+ VectorClear( p->vel );
+ }
- i = p->shaderAnim;
- j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]);
- p->pshader = shaderAnims[i][j];
+ if ( trace.fraction == 1.0f ) {
+ p->vel[2] -= (p->stopped) ? 100 : 80;
+ p->accel[2] -= (p->stopped) ? 10 : 100;
+ }
+ else // bouncing
+ {
+ if ( p->stopped ) { // water touching something solid
+ VectorClear( p->vel );
+ VectorClear( p->accel );
+ VectorCopy( trace.endpos, p->org );
+ p->height = p->width *= 0.9f; // make it tinier when that happens
+ }
- if (p->roll) {
- vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- rotate_ang[ROLL] += p->roll;
- AngleVectors ( rotate_ang, NULL, rr, ru);
+ if ( trace.plane.normal[2] >= 0.7f && Q_fabs( p->vel[2] ) < 1.0f ) {
+ // stop bouncing
+ VectorClear( p->vel );
+ VectorClear( p->accel );
+ p->height = p->width *= 0.9f;
+ }
+ else
+ {
+ // similar to CG_ReflectVelocity
+ const float BOUNCEFACTOR = 0.55f;
+ float dot = DotProduct( p->vel, trace.plane.normal );
+ if ( trace.plane.normal[2] < 0.91f && VectorLength( p->vel ) < 110.0f ) {
+ VectorMA( p->vel, 3.0f * dot, trace.plane.normal, p->vel );
+ VectorScale( p->vel, 3.0f, p->vel );
+ } else {
+ VectorMA( p->vel, -2.5f * dot, trace.plane.normal, p->vel );
+ VectorScale( p->vel, BOUNCEFACTOR, p->vel );
+ }
+ }
}
- if (p->roll) {
- VectorMA (org, -height, ru, point);
- VectorMA (point, -width, rr, point);
- } else {
- VectorMA (org, -height, vup, point);
- VectorMA (point, -width, vright, point);
+ // if it's assigned to water, then detect when going underwater and changing to P_BUBBLE type
+ if ( p->stopped && ( contents & CONTENTS_WATER ) ) {
+ p->type = P_BUBBLE_TURBULENT;
+ p->endtime = timenonscaled + 600;
+ VectorCopy( trace.endpos, p->org );
+ p->vel[2] = p->accel[2] = 0;
}
- VectorCopy (point, verts[0].xyz);
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*height, ru, point);
- } else {
- VectorMA (point, 2*height, vup, point);
+
+ // for a short time, the debris begin to get tinier
+ if ( p->time > p->endtime - 500 ) {
+ p->height = p->width *= 0.9f;
}
- VectorCopy (point, verts[1].xyz);
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, 2*width, rr, point);
- } else {
- VectorMA (point, 2*width, vright, point);
+ }
+
+ // BFP - Render as 3D model
+ if ( cg_3dparticles.integer > 0 && p->pmodel
+ && ( p->type == P_ANTIGRAV_ROCK || p->type == P_AURA || p->type == P_ROCK_DEBRIS
+ || p->type == P_WATER_SPLASH || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT ) )
+ {
+ refEntity_t modelRef;
+ vec3_t angles;
+
+ memset( &modelRef, 0, sizeof(modelRef) );
+ modelRef.reType = RT_MODEL;
+ modelRef.hModel = p->pmodel;
+ if ( p->type == P_AURA || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT || p->type == P_WATER_SPLASH ) {
+ modelRef.customShader = p->pshader;
}
- VectorCopy (point, verts[2].xyz);
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
-
- if (p->roll) {
- VectorMA (point, -2*height, ru, point);
- } else {
- VectorMA (point, -2*height, vup, point);
+
+ // BFP - Advance yaw and pitch only while the rock is floating (ki active)
+ // endwidth/endheight hold per-particle spin speeds set at spawn
+ // startfade is reused as the pitch accumulator
+ if ( p->type == P_ANTIGRAV_ROCK && !p->stopped ) {
+ p->accumroll += p->endwidth;
+ if ( p->accumroll >= 360.0f ) {
+ p->accumroll -= 360.0f;
+ }
+ p->startfade += p->endheight;
+ if ( p->startfade >= 360.0f ) {
+ p->startfade -= 360.0f;
+ }
}
- VectorCopy (point, verts[3].xyz);
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
+
+ AxisClear( modelRef.axis );
+ VectorSet( angles,
+ ( p->type == P_ANTIGRAV_ROCK ) ? p->startfade : 0,
+ (float)p->accumroll,
+ 0 );
+ AnglesToAxis( angles, modelRef.axis );
+
+ VectorCopy( org, modelRef.origin );
+ VectorCopy( org, modelRef.oldorigin );
+
+ VectorScale( modelRef.axis[0], p->width, modelRef.axis[0] );
+ VectorScale( modelRef.axis[1], p->width, modelRef.axis[1] );
+ VectorScale( modelRef.axis[2], p->width, modelRef.axis[2] );
+ modelRef.nonNormalizedAxes = qtrue;
+
+ trap_R_AddRefEntityToScene( &modelRef );
+ return;
}
- // done.
-
- if (!p->pshader) {
+
+ // build the quad (non-3D case)
+ if ( !p->pshader ) {
// (SA) temp commented out for DM
// CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type);
return;
}
- if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY)
- trap_R_AddPolyToScene( p->pshader, 3, TRIverts );
+ if (p->rotate)
+ {
+ VectorMA (org, -height, rup2, point);
+ VectorMA (point, -width, rright2, point);
+ }
+ else
+ {
+ VectorMA (org, -p->height, vup, point);
+ VectorMA (point, -p->width, vright, point);
+ }
+ VectorCopy( point, verts[0].xyz );
+ Vector2Set( verts[0].st, 0, 0 );
+ Byte4Set( verts[0].modulate, 255 * color[0], 255 * color[1], 255 * color[2], 255 * invratio );
+
+ if (p->rotate)
+ {
+ VectorMA (org, -height, rup2, point);
+ VectorMA (point, width, rright2, point);
+ }
+ else
+ {
+ VectorMA (org, -p->height, vup, point);
+ VectorMA (point, p->width, vright, point);
+ }
+ VectorCopy( point, verts[1].xyz );
+ Vector2Set( verts[1].st, 0, 1 );
+ Byte4Set( verts[1].modulate, 255 * color[0], 255 * color[1], 255 * color[2], 255 * invratio );
+
+ if (p->rotate)
+ {
+ VectorMA (org, height, rup2, point);
+ VectorMA (point, width, rright2, point);
+ }
+ else
+ {
+ VectorMA (org, p->height, vup, point);
+ VectorMA (point, p->width, vright, point);
+ }
+ VectorCopy( point, verts[2].xyz );
+ Vector2Set( verts[2].st, 1, 1 );
+ Byte4Set( verts[2].modulate, 255 * color[0], 255 * color[1], 255 * color[2], 255 * invratio );
+
+ if (p->rotate)
+ {
+ VectorMA (org, height, rup2, point);
+ VectorMA (point, -width, rright2, point);
+ }
else
- trap_R_AddPolyToScene( p->pshader, 4, verts );
+ {
+ VectorMA (org, p->height, vup, point);
+ VectorMA (point, -p->width, vright, point);
+ }
+ VectorCopy( point, verts[3].xyz );
+ Vector2Set( verts[3].st, 1, 0 );
+ Byte4Set( verts[3].modulate, 255 * color[0], 255 * color[1], 255 * color[2], 255 * invratio );
+
+ trap_R_AddPolyToScene( p->pshader, 4, verts );
+}
+/*
+=====================
+CG_AddParticleToScene
+=====================
+*/
+void CG_AddParticleToScene (cparticle_t *p, vec3_t org, float alpha)
+{
+ // BFP - Don't draw if the particles are very far
+ if ( Distance( cg.snap->ps.origin, org ) > 20000 ) {
+ return;
+ }
+
+ switch (p->type)
+ {
+ case P_SPRITE:
+ AddSpriteParticle(p, org, alpha);
+ break;
+ case P_SPARK:
+ AddSparkParticle(p, org, alpha);
+ break;
+ default:
+ AddGenericParticle(p, org, alpha);
+ break;
+ }
}
+
// Ridah, made this static so it doesn't interfere with other files
static float roll = 0.0;
@@ -833,11 +672,11 @@ void CG_AddParticles (void)
float alpha;
float time, time2;
vec3_t org;
- int color;
cparticle_t *active, *tail;
- int type;
vec3_t rotate_ang;
+ timenonscaled = trap_Milliseconds(); // BFP - That's what the variable makes non-timescaled
+
if (!initparticles)
CG_ClearParticles ();
@@ -846,128 +685,88 @@ void CG_AddParticles (void)
VectorCopy( cg.refdef.viewaxis[2], vup );
vectoangles( cg.refdef.viewaxis[0], rotate_ang );
- roll += ((cg.time - oldtime) * 0.1) ;
+ roll += ((timenonscaled - oldtime) * 0.1) ;
rotate_ang[ROLL] += (roll*0.9);
AngleVectors ( rotate_ang, rforward, rright, rup);
- oldtime = cg.time;
+ oldtime = timenonscaled;
active = NULL;
tail = NULL;
for (p=active_particles ; p ; p=next)
{
-
next = p->next;
- time = (cg.time - p->time)*0.001;
+ time = (timenonscaled - p->time)*0.001;
- alpha = p->alpha + time*p->alphavel;
- if (alpha <= 0)
+ // BFP - Make alpha timescaled for smoke-like particles
+ alpha = p->alpha + time*p->alphavel*(cg_timescale.value <= 0.1 ? 0.1 : cg_timescale.value);
+ if (p->alphavel < 0.0f) p->alpha = alpha; // BFP - Alpha fading out
+ if (p->alpha <= 0)
{ // faded out
p->next = free_particles;
free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
+ p->type = p->color = p->alpha = 0;
continue;
}
- if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT)
+ if (p->type == P_SMOKE
+ || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT // BFP - Add P_BUBBLE types to remove particles
+ || p->type == P_ANTIGRAV_ROCK // BFP - Add P_ANTIGRAV_ROCK to remove particles
+ || p->type == P_AURA // BFP - Add P_AURA to remove particles
+ || p->type == P_ROCK_DEBRIS // BFP - Add P_ROCK_DEBRIS to remove particles
+ || p->type == P_WATER_SPLASH // BFP - Add P_WATER_SPLASH to remove particles
+ || p->type == P_SPARK) // BFP - Add P_SPARK to remove particles
{
- if (cg.time > p->endtime)
+ if (timenonscaled > p->endtime)
{
p->next = free_particles;
free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
-
+ p->color = p->alpha = 0;
+ p->height = p->width = p->endheight = p->endwidth = 0;
continue;
}
+ }
+ if (p->type == P_SPRITE && p->endtime < 0) {
+ // temporary sprite
+ CG_AddParticleToScene (p, p->org, alpha);
+ p->next = free_particles;
+ free_particles = p;
+ p->color = p->alpha = 0;
+ continue;
}
- if (p->type == P_WEATHER_FLURRY)
+ p->next = NULL;
+ if (!tail)
+ active = tail = p;
+ else
{
- if (cg.time > p->endtime)
- {
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
-
- continue;
- }
- }
-
-
- if (p->type == P_FLAT_SCALEUP_FADE)
- {
- if (cg.time > p->endtime)
- {
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
- continue;
- }
-
- }
-
- if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) {
- // temporary sprite
- CG_AddParticleToScene (p, p->org, alpha);
- p->next = free_particles;
- free_particles = p;
- p->type = 0;
- p->color = 0;
- p->alpha = 0;
- continue;
- }
-
- p->next = NULL;
- if (!tail)
- active = tail = p;
- else
- {
- tail->next = p;
- tail = p;
+ tail->next = p;
+ tail = p;
}
if (alpha > 1.0)
alpha = 1;
- color = p->color;
-
time2 = time*time;
org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2;
org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2;
org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2;
- type = p->type;
-
CG_AddParticleToScene (p, org, alpha);
}
active_particles = active;
}
-/*
-======================
-CG_AddParticles
-======================
-*/
-void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent)
+void CG_ParticleBubble (centity_t *cent, qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t origin2, int turbtime, float range, float size)
{
cparticle_t *p;
- qboolean turb = qtrue;
- if (!pshader)
- CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n");
+ // if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n");
if (!free_particles)
return;
@@ -975,536 +774,351 @@ void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent)
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
- p->color = 0;
- p->alpha = 0.90f;
- p->alphavel = 0;
+ p->time = timenonscaled;
- p->start = cent->currentState.origin2[0];
- p->end = cent->currentState.origin2[1];
-
- p->endtime = cg.time + cent->currentState.time;
- p->startfade = cg.time + cent->currentState.time2;
-
- p->pshader = pshader;
-
- if (rand()%100 > 90)
- {
- p->height = 32;
- p->width = 32;
- p->alpha = 0.10f;
- }
- else
- {
- p->height = 1;
- p->width = 1;
- }
+ // BFP - Keep entity number to identify who is using
+ p->entityNum = cent->currentState.number;
- p->vel[2] = -20;
+ // BFP - Add end time to remove particles, if there's no end time the particles will remain there
+ p->endtime = timenonscaled + 600;
+ p->startfade = timenonscaled + 200;
- p->type = P_WEATHER_FLURRY;
-
- if (turb)
- p->vel[2] = -10;
-
- VectorCopy(cent->currentState.origin, p->org);
-
- p->org[0] = p->org[0];
- p->org[1] = p->org[1];
- p->org[2] = p->org[2];
-
- p->vel[0] = p->vel[1] = 0;
-
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16);
- p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16);
- p->vel[2] += cent->currentState.angles[2];
-
- if (turb)
- {
- p->accel[0] = crandom () * 16;
- p->accel[1] = crandom () * 16;
+ // BFP - Monster gamemode, player monster bubble particles last a bit more
+ if ( cgs.gametype == GT_MONSTER
+ && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ p->endtime += 200;
+ p->startfade += 200;
}
-}
-
-void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum)
-{
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_ParticleSnow pshader == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
p->color = 0;
- p->alpha = 0.40f;
+ p->alpha = 1;
p->alphavel = 0;
- p->start = origin[2];
- p->end = origin2[2];
+ // BFP - Apply to entity's origin
+ p->start = cent->currentState.origin[2];
+ p->end = cent->currentState.origin2[2];
p->pshader = pshader;
- p->height = 1;
- p->width = 1;
-
- p->vel[2] = -50;
-
- if (turb)
- {
- p->type = P_WEATHER_TURBULENT;
- p->vel[2] = -50 * 1.3;
+ p->pmodel = pmodel;
+ p->height = p->width = (rand() % (int)size) + 1;
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->width = p->width * 0.075;
+ p->height = p->width;
}
- else
- {
- p->type = P_WEATHER;
- }
-
- VectorCopy(origin, p->org);
- p->org[0] = p->org[0] + ( crandom() * range);
- p->org[1] = p->org[1] + ( crandom() * range);
- p->org[2] = p->org[2] + ( crandom() * (p->start - p->end));
-
- p->vel[0] = p->vel[1] = 0;
-
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
+ VectorCopy(origin, p->org);
- if (turb)
+ if (turbtime)
{
- p->vel[0] = crandom() * 16;
- p->vel[1] = crandom() * 16;
- }
-
- // Rafael snow pvs check
- p->snum = snum;
- p->link = qtrue;
-
-}
+ p->type = P_BUBBLE_TURBULENT;
+ // BFP - Apply end time to remove particles in that case, if there's no end time the particles will remain there
+ p->endtime = timenonscaled + turbtime;
+ p->height = p->width = (rand() % 2) + size;
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->width = p->width * 0.075;
+ p->height = p->width;
+ }
-void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum)
-{
- cparticle_t *p;
- float randsize;
+ // BFP - Monster gamemode, player monster bubble particles has different spawning origin
+ if ( cgs.gametype == GT_MONSTER
+ && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ p->org[0] += (crandom() * range);
+ p->org[1] += (crandom() * range);
+ }
- if (!pshader)
- CG_Printf ("CG_ParticleSnow pshader == ZERO!\n");
+ VectorSet( p->vel,
+ crandom() * 300,
+ crandom() * 300,
+ 30 * (rand() % (int)range) );
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->color = 0;
- p->alpha = 0.40f;
- p->alphavel = 0;
- p->start = origin[2];
- p->end = origin2[2];
- p->pshader = pshader;
-
- randsize = 1 + (crandom() * 0.5);
-
- p->height = randsize;
- p->width = randsize;
-
- p->vel[2] = 50 + ( crandom() * 10 );
+ // dispersion
+ VectorSet( p->accel,
+ crandom() * 10,
+ crandom() * 10,
+ 20 * (rand() % (int)range) );
- if (turb)
- {
- p->type = P_BUBBLE_TURBULENT;
- p->vel[2] = 50 * 1.3;
+ // avoid if both upwards are zero or less
+ if ( p->vel[2] <= 0 ) p->vel[2] = 10 + (rand() % (int)range);
+ if ( p->accel[2] <= 0 ) p->accel[2] = 10 + (rand() % (int)range);
}
else
{
+ // spawn in one point
+ float angle = crandom() * M_PI;
+ float radius = crandom() * range;
+
p->type = P_BUBBLE;
- }
-
- VectorCopy(origin, p->org);
- p->org[0] = p->org[0] + ( crandom() * range);
- p->org[1] = p->org[1] + ( crandom() * range);
- p->org[2] = p->org[2] + ( crandom() * (p->start - p->end));
+ p->org[0] += cos( angle ) * radius;
+ p->org[1] += sin( angle ) * radius;
+ p->org[2] += (crandom() * 5);
- p->vel[0] = p->vel[1] = 0;
-
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
+ VectorSet( p->vel,
+ crandom() * 360,
+ crandom() * 360,
+ 20 );
- if (turb)
- {
- p->vel[0] = crandom() * 4;
- p->vel[1] = crandom() * 4;
+ // dispersion
+ VectorSet( p->accel,
+ crandom() * 10,
+ crandom() * 10,
+ 1200 );
}
- // Rafael snow pvs check
- p->snum = snum;
- p->link = qtrue;
-
+ p->accumroll = 0;
+ p->custom = 3 - (crandom() * 6); // used to randomize where the bubbles stop when these touches the surface
+ p->stopped = qfalse; // used to handle the bubbles when touching the surface
}
-void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent)
-{
-
- // using cent->density = enttime
- // cent->frame = startfade
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_ParticleSmoke == ZERO!\n");
-
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + cent->currentState.time;
- p->startfade = cg.time + cent->currentState.time2;
-
- p->color = 0;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->start = cent->currentState.origin[2];
- p->end = cent->currentState.origin2[2];
- p->pshader = pshader;
- p->rotate = qfalse;
- p->height = 8;
- p->width = 8;
- p->endheight = 32;
- p->endwidth = 32;
- p->type = P_SMOKE;
-
- VectorCopy(cent->currentState.origin, p->org);
-
- p->vel[0] = p->vel[1] = 0;
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
-
- p->vel[2] = 5;
+// BFP - Handle bubble particles when reaching to the top
+static void CG_BubblesWaterHandling( cparticle_t *p, vec3_t org ) {
+ trace_t trace;
+ vec3_t start, end;
+ int contents;
- if (cent->currentState.frame == 1)// reverse gravity
- p->vel[2] *= -1;
-
- p->roll = 8 + (crandom() * 4);
-}
+ VectorCopy( org, end );
+ end[2] -= 1;
+ VectorCopy( org, start );
+ start[2] += 10;
-void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration)
-{
+ // decelerate
+ if ( Q_fabs(p->vel[0]) > 0 ) {
+ p->vel[0] *= 0.995;
+ }
+ if ( Q_fabs(p->vel[1]) > 0 ) {
+ p->vel[1] *= 0.995;
+ }
- cparticle_t *p;
+ // trace down to find the surface
+ trap_CM_BoxTrace( &trace, start, end, vec3_origin, vec3_origin, 0, CONTENTS_WATER );
- if (!free_particles)
+ // if the particle is touching something solid, it will skip instead stopping
+ contents = trap_CM_PointContents( trace.endpos, 0 );
+ if ( contents & CONTENTS_SOLID ) { // remove when grazing something solid
+ p->next = NULL;
+ p->color = p->alpha = 0;
return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + duration;
- p->startfade = cg.time + duration/2;
-
- p->color = EMISIVEFADE;
- p->alpha = 1.0;
- p->alphavel = 0;
-
- p->height = 0.5;
- p->width = 0.5;
- p->endheight = 0.5;
- p->endwidth = 0.5;
-
- p->pshader = cgs.media.tracerShader;
-
- p->type = P_SMOKE;
-
- VectorCopy(org, p->org);
+ }
+ if ( !( contents & CONTENTS_WATER ) ) {
+ p->time = timenonscaled;
+ VectorCopy (trace.endpos, p->org);
+ p->org[2] = trace.endpos[2] - p->custom;
- p->vel[0] = vel[0];
- p->vel[1] = vel[1];
- p->vel[2] = vel[2];
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
+ // stop going up and decrease dispersion speed
+ p->vel[2] = 0;
+ VectorClear( p->accel );
- p->accel[2] = -60;
- p->vel[2] += -20;
-
+ // trace again if the bubble went outside, then set it near to the surface
+ contents = trap_CM_PointContents( p->org, 0 );
+ if ( !( contents & CONTENTS_WATER ) ) {
+ VectorCopy (trace.endpos, p->org);
+ }
+ if ( p->type == P_BUBBLE ) {
+ if ( p->vel[0] != 0 ) p->vel[0] *= 0.9;
+ if ( p->vel[1] != 0 ) p->vel[1] *= 0.9;
+ // stop after few milliseconds
+ if ( p->stopped && p->time > p->endtime - 2250 ) {
+ p->vel[0] = 0;
+ p->vel[1] = 0;
+ }
+ } else {
+ if ( p->vel[0] != 0 ) p->vel[0] *= 0.97;
+ if ( p->vel[1] != 0 ) p->vel[1] *= 0.97;
+ }
+ }
}
-/*
-======================
-CG_ParticleExplosion
-======================
-*/
-
-void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd)
+// BFP - Particle for dash smoke when using ki boost and moving on the ground
+void CG_ParticleDashSmoke (centity_t *cent, qhandle_t pshader, vec3_t origin, float size, float velocityDisp, float upVelocity, float accel)
{
cparticle_t *p;
- int anim;
- if (animStr < (char *)10)
- CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" );
+ // if (!pshader) CG_Printf ("CG_ParticleDashSmoke pshader == ZERO!\n");
- // find the animation string
- for (anim=0; shaderAnimNames[anim]; anim++) {
- if (!stricmp( animStr, shaderAnimNames[anim] ))
- break;
+ // BFP - Don't spawn on pause
+ if ( cg.frametime <= 0.0f ) {
+ return;
}
- if (!shaderAnimNames[anim]) {
- CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr);
+
+ // Too much smoke...
+ // That cent->trailTime can be handled to avoid spawning too much and only spawn when the game isn't paused, hehehe :P
+ if ( cent->trailTime > cg.time ) {
return;
}
+ cent->trailTime += 35;
+ if ( cent->trailTime < cg.time ) {
+ cent->trailTime = cg.time;
+ }
if (!free_particles)
return;
+
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- if (duration < 0) {
- duration *= -1;
- p->roll = 0;
- } else {
- p->roll = crandom()*179;
- }
+ p->time = timenonscaled;
- p->shaderAnim = anim;
+ // BFP - Keep entity number to identify who is using
+ p->entityNum = cent->currentState.clientNum;
- p->width = sizeStart;
- p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction
+ p->alpha = 0.45;
+ p->alphavel = -0.1;
- p->endheight = sizeEnd;
- p->endwidth = sizeEnd*shaderAnimSTRatio[anim];
+ p->pshader = pshader;
+ p->start = cent->currentState.origin[2];
+ p->end = cent->currentState.origin2[2];
- p->endtime = cg.time + duration;
+ p->endtime = timenonscaled + 2000;
+ p->startfade = timenonscaled + 100;
- p->type = P_ANIM;
+ p->height = p->width = size;
- VectorCopy( origin, p->org );
- VectorCopy( vel, p->vel );
- VectorClear( p->accel );
+ p->endheight = p->height * 2;
+ p->endwidth = p->width * 2;
-}
+ p->type = P_SMOKE;
-// Rafael Shrapnel
-void CG_AddParticleShrapnel (localEntity_t *le)
-{
- return;
+ VectorCopy( origin, p->org );
+ VectorSet( p->vel,
+ (rand() % (int)velocityDisp) - velocityDisp * 0.5,
+ (rand() % (int)velocityDisp) - velocityDisp * 0.5,
+ upVelocity );
+
+ // dispersion
+ VectorSet( p->accel,
+ crandom() * accel,
+ crandom() * accel,
+ 1800 );
+
+ p->stopped = qfalse; // to distinguish the type of smoke
}
-// done.
-int CG_NewParticleArea (int num)
+// BFP - Particle for charge smoke when using ki charge near the ground
+void CG_ParticleChargeSmoke (centity_t *cent, qhandle_t pshader, vec3_t origin, float size, float radialVel, float baseRadius)
{
- // const char *str;
- char *str;
- char *token;
- int type;
- vec3_t origin, origin2;
- int i;
- float range = 0;
- int turb;
- int numparticles;
- int snum;
-
- str = (char *) CG_ConfigString (num);
- if (!str[0])
- return (0);
-
- // returns type 128 64 or 32
- token = COM_Parse (&str);
- type = atoi (token);
-
- if (type == 1)
- range = 128;
- else if (type == 2)
- range = 64;
- else if (type == 3)
- range = 32;
- else if (type == 0)
- range = 256;
- else if (type == 4)
- range = 8;
- else if (type == 5)
- range = 16;
- else if (type == 6)
- range = 32;
- else if (type == 7)
- range = 64;
-
-
- for (i=0; i<3; i++)
- {
- token = COM_Parse (&str);
- origin[i] = atof (token);
- }
+ cparticle_t *p;
+ float angle, radius;
+ vec3_t dir, radial, angular;
- for (i=0; i<3; i++)
- {
- token = COM_Parse (&str);
- origin2[i] = atof (token);
- }
-
- token = COM_Parse (&str);
- numparticles = atoi (token);
-
- token = COM_Parse (&str);
- turb = atoi (token);
+ // if (!pshader) CG_Printf ("CG_ParticleChargeSmoke pshader == ZERO!\n");
- token = COM_Parse (&str);
- snum = atoi (token);
-
- for (i=0; i= 4)
- CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum);
- else
- CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum);
+ // Too much smoke...
+ // That cent->pe.chargeSmokeTime can be handled to avoid spawning too much and only spawn when the game isn't paused, hehehe :P
+ // It isn't possible reusing cent->trailTime, it would have client visual issues
+#if 0
+ if ( cent->pe.chargeSmokeTime > timenonscaled ) {
+ return;
}
-
- return (1);
-}
-
-void CG_SnowLink (centity_t *cent, qboolean particleOn)
-{
- cparticle_t *p, *next;
- int id;
-
- id = cent->currentState.frame;
-
- for (p=active_particles ; p ; p=next)
- {
- next = p->next;
-
- if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT)
- {
- if (p->snum == id)
- {
- if (particleOn)
- p->link = qtrue;
- else
- p->link = qfalse;
- }
- }
-
+ cent->pe.chargeSmokeTime += 10;
+ if ( cent->pe.chargeSmokeTime < timenonscaled ) {
+ cent->pe.chargeSmokeTime = timenonscaled;
}
-}
-
-void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin)
-{
- cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n");
+#endif
if (!free_particles)
return;
+
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
- p->alpha = 0.25;
- p->alphavel = 0;
- p->roll = crandom()*179;
+
+ p->time = timenonscaled;
+
+ // BFP - Keep entity number to identify who is using
+ p->entityNum = cent->currentState.clientNum;
+
+ p->alpha = 0.31;
+ p->alphavel = -0.09;
p->pshader = pshader;
+ p->start = cent->currentState.origin[2];
+ p->end = cent->currentState.origin2[2];
- p->endtime = cg.time + 1000;
- p->startfade = cg.time + 100;
+ p->endtime = timenonscaled + 2000;
+ p->startfade = timenonscaled + 100;
- p->width = rand()%4 + 8;
- p->height = rand()%4 + 8;
+ p->height = p->width = size;
- p->endheight = p->height *2;
+ p->endheight = p->height * 2;
p->endwidth = p->width * 2;
- p->endtime = cg.time + 500;
-
- p->type = P_SMOKE_IMPACT;
+ p->type = P_SMOKE;
VectorCopy( origin, p->org );
- VectorSet(p->vel, 0, 0, 20);
- VectorSet(p->accel, 0, 0, 20);
- p->rotate = qtrue;
-}
-
-void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration)
-{
- cparticle_t *p;
+ // randomize angle and radius for circular motion
+ angle = crandom() * 360.0f;
+ radius = baseRadius + crandom() * 10; // radius around the origin
- if (!pshader)
- CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n");
+ // compute radial direction
+ dir[0] = cos( DEG2RAD( angle ) );
+ dir[1] = sin( DEG2RAD( angle ) );
+ dir[2] = 0;
- if (!free_particles)
- return;
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
+ // initial position offset for circular spread
+ VectorMA( origin, radius, dir, p->org );
- p->pshader = pshader;
+ // velocity for circular motion
+ VectorScale( dir, radialVel, radial ); // radial component of velocity
+ VectorSet( angular, -dir[1], dir[0], 0 ); // perpendicular for tangential motion
+ VectorScale( angular, 100, angular ); // tangential speed
- p->endtime = cg.time + duration;
-
- if (fleshEntityNum)
- p->startfade = cg.time;
- else
- p->startfade = cg.time + 100;
+ // combine radial and angular velocity
+ p->vel[0] = radial[0] + angular[0];
+ p->vel[1] = radial[1] + angular[1];
+ p->vel[2] = radial[2] + angular[2];
- p->width = 4;
- p->height = 4;
+ // vertical lift
+ p->vel[2] = 50 + crandom() * 10;
- p->endheight = 4+rand()%3;
- p->endwidth = p->endheight;
+ // dispersion
+ VectorSet( p->accel,
+ crandom() * 20,
+ crandom() * 20,
+ 5);
- p->type = P_SMOKE;
+ p->stopped = qfalse;
+ p->custom = 1;
+}
- VectorCopy( start, p->org );
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = -20;
- VectorClear( p->accel );
+// BFP - Handle charge smoke particles when touching something solid
+static void CG_ChargeSmokeHandling( cparticle_t *p, vec3_t org ) {
+ trace_t trace;
+ vec3_t start, end;
+ int contents;
- p->rotate = qfalse;
+ VectorCopy( org, start );
+ VectorCopy( org, end );
- p->roll = rand()%179;
-
- p->color = BLOODRED;
- p->alpha = 0.75;
+ // trace to check the collision
+ trap_CM_BoxTrace( &trace, start, end, vec3_origin, vec3_origin, 0, CONTENTS_SOLID );
+ contents = trap_CM_PointContents( trace.endpos, 0 );
+ if ( contents & CONTENTS_SOLID ) { // remove when grazing something solid
+ p->next = NULL;
+ p->color = p->alpha = 0;
+ }
}
-void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent)
+// BFP - Antigrav rock particles for ki charging status
+void CG_ParticleAntigravRock (qhandle_t pshader, qhandle_t pmodel, centity_t *cent, int entityNum, vec3_t origin, float size, float spawnRange, float endTime)
{
cparticle_t *p;
- int time;
- int time2;
- float ratio;
-
- float duration = 1500;
-
- time = cg.time;
- time2 = cg.time + cent->currentState.time;
+ // if (!pshader) CG_Printf ("CG_ParticleAntigravRock == ZERO!\n");
- ratio =(float)1 - ((float)time / (float)time2);
-
- if (!pshader)
- CG_Printf ("CG_Particle_OilParticle == ZERO!\n");
+ // Too many rocks... That cent->dustTrailTime can be handled to avoid spawning too many, hehehe :P
+ // cent->dustTrailTime was unused on Q3 before, so now it's being used for this kind of particles
+ // reusing cent->trailTime would make the time more delayed to spawn the particles, so not visually good
+ if ( cent->dustTrailTime > timenonscaled ) {
+ return;
+ }
+ cent->dustTrailTime += 20;
+ if ( cent->dustTrailTime < timenonscaled ) {
+ cent->dustTrailTime = timenonscaled;
+ }
if (!free_particles)
return;
@@ -1512,52 +1126,83 @@ void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent)
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = pshader;
+ p->time = timenonscaled;
- p->endtime = cg.time + duration;
-
- p->startfade = p->endtime;
+ // BFP - Keep entity number to identify who is using
+ p->entityNum = entityNum;
- p->width = 1;
- p->height = 3;
+ p->endtime = timenonscaled + endTime + (crandom() * 20);
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->endtime += 350;
+ }
- p->endheight = 3;
- p->endwidth = 1;
+ p->color = 0;
+ p->alpha = 1;
+ p->alphavel = 0;
+ p->pshader = pshader;
+ p->pmodel = pmodel;
+ p->height = p->width = (rand() % (int)size) + size;
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->width *= 12;
+ p->height = p->width;
+ }
+ p->type = P_ANTIGRAV_ROCK;
- p->type = P_SMOKE;
+ VectorCopy( origin, p->org );
- VectorCopy(cent->currentState.origin, p->org );
-
- p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio));
- p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio));
- p->vel[2] = (cent->currentState.origin2[2]);
+ p->org[0] += (crandom() * spawnRange);
+ p->org[1] += (crandom() * spawnRange);
- p->snum = 1.0f;
+ p->start = cent->currentState.origin[2];
+ p->end = p->org[2] + 800 + (crandom() * 10);
- VectorClear( p->accel );
+ p->vel[0] = 0;
+ p->vel[1] = 0;
+ p->vel[2] = 450;
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->vel[2] = 300;
+ }
- p->accel[2] = -20;
+ p->accel[0] = 0;
+ p->accel[1] = 0;
+ p->accel[2] = 20;
- p->rotate = qfalse;
+ p->accumroll = rand() % 360;
+ p->startfade = crandom() * 360; // initial pitch (degrees) - reused field
+ p->endwidth = 1.5f + (crandom() * 2.5f); // yaw spin speed (deg/frame) - reused field
+ p->endheight = 0.8f + (crandom() * 1.5f); // pitch spin speed (deg/frame) - reused field
+ p->stopped = qfalse; // to handle the ki charging status
+ p->custom = 0; // to handle the client side visuals
+}
- p->roll = rand()%179;
-
- p->alpha = 0.75;
+// BFP - To handle the client side visuals of antigrav rock particles
+void CG_AntigravRockHandling (centity_t *cent)
+{
+ cparticle_t *p, *next;
+ for (p=active_particles ; p ; p=next)
+ {
+ next = p->next;
+ if ( p->type != P_ANTIGRAV_ROCK ) continue;
+
+ if ( p->entityNum == cent->currentState.clientNum
+ && !( cent->currentState.eFlags & EF_DEAD )
+ && ( ( !( cent->currentState.eFlags & EF_AURA ) && !( cent->currentState.eFlags & EF_AURA_TIER_UP ) )
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ || ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_CHARGE && !( cent->currentState.eFlags & EF_AURA ) && !( cent->currentState.eFlags & EF_AURA_TIER_UP ) ) )
+ && !p->stopped ) { // BFP - Make each particle fall when they aren't on ki charging status
+ p->endtime = timenonscaled + 1650;
+ p->stopped = qtrue;
+ }
+ }
}
-
-void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent)
+// BFP - Particle aura
+void CG_ParticleAura (centity_t *cent, int entityNum, qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t origin2, float range)
{
cparticle_t *p;
-
- if (!pshader)
- CG_Printf ("CG_Particle_OilSlick == ZERO!\n");
+
+ // if (!pshader) CG_Printf ("CG_ParticleAura pshader == ZERO!\n");
if (!free_particles)
return;
@@ -1565,274 +1210,132 @@ void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent)
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
-
- if (cent->currentState.angles2[2])
- p->endtime = cg.time + cent->currentState.angles2[2];
- else
- p->endtime = cg.time + 60000;
-
- p->startfade = p->endtime;
+ p->time = timenonscaled;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
+ // BFP - Keep entity number to identify who is using
+ p->entityNum = entityNum;
- p->pshader = pshader;
-
- if (cent->currentState.angles2[0] || cent->currentState.angles2[1])
- {
- p->width = cent->currentState.angles2[0];
- p->height = cent->currentState.angles2[0];
+ // BFP - Add end time to remove particles, if there's no end time the particles will remain there
+ p->endtime = timenonscaled + 400;
+ p->startfade = timenonscaled + 200;
- p->endheight = cent->currentState.angles2[1];
- p->endwidth = cent->currentState.angles2[1];
- }
- else
- {
- p->width = 8;
- p->height = 8;
+ p->color = 0;
+ p->alpha = 0.5;
+ p->alphavel = -0.075;
- p->endheight = 16;
- p->endwidth = 16;
+ // BFP - Apply to player's origin
+ p->start = cent->currentState.origin[2];
+ p->end = cent->currentState.origin2[2];
+ p->pshader = pshader;
+ p->pmodel = pmodel;
+ p->height = p->width = 40;
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->width *= 0.015f;
+ p->height = p->width;
}
- p->type = P_FLAT_SCALEUP;
-
- p->snum = 1.0;
-
- VectorCopy(cent->currentState.origin, p->org );
-
- p->org[2]+= 0.55 + (crandom() * 0.5);
-
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = 0;
- VectorClear( p->accel );
-
- p->rotate = qfalse;
+ VectorCopy(origin, p->org);
- p->roll = rand()%179;
-
- p->alpha = 0.75;
+ p->type = P_AURA;
+
+ p->org[0] += (crandom() * range);
+ p->org[1] += (crandom() * range);
+ p->org[2] += (crandom() * 5);
+
+ VectorSet( p->vel,
+ crandom() * 360,
+ crandom() * 360,
+ 100 );
+
+ // dispersion
+ VectorSet( p->accel,
+ crandom() * 20,
+ crandom() * 20,
+ 1200 );
+
+ // BFP - Monster gamemode, player monster aura particles are bigger
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ p->org[2] -= 50;
+ p->height *= 3.75;
+ p->width *= 3.75;
+ VectorScale( p->vel, 3.75, p->vel );
+ VectorScale( p->accel, 3.75, p->accel );
+ }
+ p->stopped = qfalse;
}
-void CG_OilSlickRemove (centity_t *cent)
+// BFP - To handle the client side visuals of aura particle
+void CG_ParticleAuraHandling (centity_t *cent)
{
cparticle_t *p, *next;
- int id;
-
- id = 1.0f;
-
- if (!id)
- CG_Printf ("CG_OilSlickRevove NULL id\n");
for (p=active_particles ; p ; p=next)
{
next = p->next;
-
- if (p->type == P_FLAT_SCALEUP)
- {
- if (p->snum == id)
- {
- p->endtime = cg.time + 100;
- p->startfade = p->endtime;
- p->type = P_FLAT_SCALEUP_FADE;
-
- }
+ if ( p->type != P_AURA ) continue;
+
+ if ( p->entityNum == cent->currentState.clientNum
+ && !( cent->currentState.eFlags & EF_DEAD )
+ && !( cent->currentState.eFlags & EF_AURA )
+ && !p->stopped ) { // BFP - Make each particle fall when there's no aura at this moment
+ p->alphavel = -0.03;
+ p->accel[0] = 0;
+ p->accel[1] = 0;
+ p->endtime = timenonscaled + 600;
+ p->stopped = qtrue;
}
-
}
}
-qboolean ValidBloodPool (vec3_t start)
+// BFP - Spawn a bouncing rock fragment (explosion debris on ground)
+void CG_ParticleRockDebris (qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t vel, float size, float velocity, float accel)
{
-#define EXTRUDE_DIST 0.5
-
- vec3_t angles;
- vec3_t right, up;
- vec3_t this_pos, x_pos, center_pos, end_pos;
- float x, y;
- float fwidth, fheight;
- trace_t trace;
- vec3_t normal;
-
- fwidth = 16;
- fheight = 16;
-
- VectorSet (normal, 0, 0, 1);
-
- vectoangles (normal, angles);
- AngleVectors (angles, NULL, right, up);
-
- VectorMA (start, EXTRUDE_DIST, normal, center_pos);
-
- for (x= -fwidth/2; xendpos, start);
- legit = ValidBloodPool (start);
-
- if (!legit)
- return;
-
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + 3000;
- p->startfade = p->endtime;
+ p->time = timenonscaled;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
+ p->startfade = timenonscaled + 200;
+ p->endtime = timenonscaled + 1200;
+ p->color = 0;
+ p->alpha = 1;
+ p->alphavel = 0;
p->pshader = pshader;
+ p->pmodel = pmodel;
+ p->height = p->width = (rand() % 6) + 3;
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->width *= 12;
+ p->height = p->width;
+ }
- rndSize = 0.4 + random()*0.6;
-
- p->width = 8*rndSize;
- p->height = 8*rndSize;
-
- p->endheight = 16*rndSize;
- p->endwidth = 16*rndSize;
-
- p->type = P_FLAT_SCALEUP;
-
- VectorCopy(start, p->org );
-
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = 0;
- VectorClear( p->accel );
-
- p->rotate = qfalse;
-
- p->roll = rand()%179;
-
- p->alpha = 0.75;
-
- p->color = BLOODRED;
-}
-
-#define NORMALSIZE 16
-#define LARGESIZE 32
-
-void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir)
-{
- float length;
- float dist;
- float crittersize;
- vec3_t angles, forward;
- vec3_t point;
- cparticle_t *p;
- int i;
-
- dist = 0;
-
- length = VectorLength (dir);
- vectoangles (dir, angles);
- AngleVectors (angles, forward, NULL, NULL);
-
- crittersize = LARGESIZE;
-
- if (length)
- dist = length / crittersize;
-
- if (dist < 1)
- dist = 1;
-
- VectorCopy (origin, point);
+ p->type = P_ROCK_DEBRIS;
- for (i=0; iorg );
+ p->start = origin[2];
- p = free_particles;
- free_particles = p->next;
- p->next = active_particles;
- active_particles = p;
-
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = cgs.media.smokePuffShader;
-
- p->endtime = cg.time + 350 + (crandom() * 100);
-
- p->startfade = cg.time;
-
- p->width = LARGESIZE;
- p->height = LARGESIZE;
- p->endheight = LARGESIZE;
- p->endwidth = LARGESIZE;
-
- p->type = P_SMOKE;
-
- VectorCopy( origin, p->org );
-
- p->vel[0] = 0;
- p->vel[1] = 0;
- p->vel[2] = -1;
-
- VectorClear( p->accel );
+ VectorCopy( vel, p->vel );
- p->rotate = qfalse;
+ p->accel[0] = (crandom() * accel);
+ p->accel[1] = (crandom() * accel);
+ p->accel[2] = accel + (crandom() * 50);
- p->roll = rand()%179;
-
- p->color = BLOODRED;
-
- p->alpha = 0.75;
-
- }
+ p->rollBounceCount = 3; // bounce counter
+ p->stopped = qfalse;
+ p->custom = 0;
-
+ // BFP - Random fixed yaw per rock
+ p->accumroll = rand() % 360;
}
-void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed)
+// BFP - Spawn a water entry splash bubble (arcs upward, stops at water surface)
+void CG_ParticleWaterSplash (qhandle_t pshader, qhandle_t pmodel, vec3_t origin, vec3_t vel, float size, float velocity, float accel)
{
cparticle_t *p;
@@ -1842,177 +1345,102 @@ void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y,
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
-
- p->endtime = cg.time + duration;
- p->startfade = cg.time + duration/2;
-
- p->color = EMISIVEFADE;
- p->alpha = 0.4f;
- p->alphavel = 0;
+ p->time = timenonscaled;
- p->height = 0.5;
- p->width = 0.5;
- p->endheight = 0.5;
- p->endwidth = 0.5;
+ p->startfade = timenonscaled + 200;
+ p->endtime = timenonscaled + 2450 + (crandom() * 20);
- p->pshader = cgs.media.tracerShader;
-
- p->type = P_SMOKE;
-
- VectorCopy(org, p->org);
+ p->color = 0;
+ p->alpha = 1;
+ p->alphavel = 0;
+ p->pshader = pshader;
+ p->pmodel = pmodel;
+ p->height = p->width = (rand() % (int)size) + size * 0.335f;
+ if ( cg_3dparticles.integer > 0 && pmodel ) {
+ p->width *= 0.075f;
+ p->height = p->width;
+ }
- p->org[0] += (crandom() * x);
- p->org[1] += (crandom() * y);
+ p->type = P_WATER_SPLASH;
- p->vel[0] = vel[0];
- p->vel[1] = vel[1];
- p->vel[2] = vel[2];
+ VectorCopy( origin, p->org );
+ p->org[0] += (crandom() * 15);
+ p->org[1] += (crandom() * 15);
+ p->start = origin[2];
- p->accel[0] = p->accel[1] = p->accel[2] = 0;
+ p->vel[0] = (crandom() * velocity);
+ p->vel[1] = (crandom() * velocity);
+ p->vel[2] = velocity * 7;
- p->vel[0] += (crandom() * 4);
- p->vel[1] += (crandom() * 4);
- p->vel[2] += (20 + (crandom() * 10)) * speed;
+ p->accel[0] = (crandom() * accel);
+ p->accel[1] = (crandom() * accel);
+ p->accel[2] = accel + (crandom() * 50);
- p->accel[0] = crandom () * 4;
- p->accel[1] = crandom () * 4;
-
+ p->rollBounceCount = 0;
+ p->accumroll = 0;
+ p->stopped = qtrue; // stops when it reaches the water surface
+ p->custom = 0;
}
-void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir)
+void CG_ParticleSparks (qhandle_t pshader, vec3_t origin, vec3_t vel)
{
- float length;
- float dist;
- float crittersize;
- vec3_t angles, forward;
- vec3_t point;
cparticle_t *p;
- int i;
-
- dist = 0;
-
- VectorNegate (dir, dir);
- length = VectorLength (dir);
- vectoangles (dir, angles);
- AngleVectors (angles, forward, NULL, NULL);
-
- crittersize = LARGESIZE;
-
- if (length)
- dist = length / crittersize;
-
- if (dist < 1)
- dist = 1;
-
- VectorCopy (origin, point);
-
- for (i=0; inext;
- p->next = active_particles;
- active_particles = p;
-
- p->time = cg.time;
- p->alpha = 5.0;
- p->alphavel = 0;
- p->roll = 0;
-
- p->pshader = cgs.media.smokePuffShader;
- // RF, stay around for long enough to expand and dissipate naturally
- if (length)
- p->endtime = cg.time + 4500 + (crandom() * 3500);
- else
- p->endtime = cg.time + 750 + (crandom() * 500);
-
- p->startfade = cg.time;
-
- p->width = LARGESIZE;
- p->height = LARGESIZE;
-
- // RF, expand while falling
- p->endheight = LARGESIZE*3.0;
- p->endwidth = LARGESIZE*3.0;
-
- if (!length)
- {
- p->width *= 0.2f;
- p->height *= 0.2f;
-
- p->endheight = NORMALSIZE;
- p->endwidth = NORMALSIZE;
- }
-
- p->type = P_SMOKE;
-
- VectorCopy( point, p->org );
-
- p->vel[0] = crandom()*6;
- p->vel[1] = crandom()*6;
- p->vel[2] = random()*20;
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ p->time = timenonscaled;
+ p->endtime = timenonscaled + 250;
- // RF, add some gravity/randomness
- p->accel[0] = crandom()*3;
- p->accel[1] = crandom()*3;
- p->accel[2] = -PARTICLE_GRAVITY*0.4;
+ p->color = 0;
+ p->alpha = 1;
+ p->alphavel = 0;
+ p->pshader = pshader;
+ p->height = p->width = (rand() % 6) + 5;
- VectorClear( p->accel );
+ p->type = P_SPARK;
- p->rotate = qfalse;
+ VectorCopy( origin, p->org );
+ p->start = origin[2];
+ VectorCopy( vel, p->vel );
- p->roll = rand()%179;
-
- p->alpha = 0.75;
-
- }
+ p->accel[0] = (crandom() * 600);
+ p->accel[1] = (crandom() * 600);
+ p->accel[2] = 50 + (crandom() * 25);
-
+ p->rollBounceCount = 0;
+ p->stopped = qfalse;
+ p->custom = 1; // enable gravity
}
-void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha)
+void CG_ParticleBeamStruggleSpark (qhandle_t pshader, vec3_t origin, vec3_t vel)
{
cparticle_t *p;
- if (!pshader)
- CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n");
-
if (!free_particles)
return;
-
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
- p->time = cg.time;
- p->alpha = 1.0;
- p->alphavel = 0;
- p->roll = rand()%179;
-
- p->pshader = pshader;
-
- if (duration > 0)
- p->endtime = cg.time + duration;
- else
- p->endtime = duration;
-
- p->startfade = cg.time;
-
- p->width = size;
- p->height = size;
-
- p->endheight = size;
- p->endwidth = size;
-
- p->type = P_SPRITE;
+ p->time = timenonscaled;
+ p->endtime = timenonscaled + 250;
VectorCopy( origin, p->org );
+ p->start = origin[2];
+ VectorCopy( vel, p->vel );
+
+ p->accel[0] = (crandom() * 300);
+ p->accel[1] = (crandom() * 300);
+ p->accel[2] = -10 - (crandom() * 20);
- p->rotate = qfalse;
+ p->type = P_SPARK;
+ p->color = 0;
+ p->alpha = 1.0;
+ p->alphavel = 0;
+ p->pshader = pshader;
+ p->height = p->width = (rand() % 4) + 2;
}
diff --git a/source/cgame/cg_players.c b/source/cgame/cg_players.c
index 1fc004c..91ff4b7 100644
--- a/source/cgame/cg_players.c
+++ b/source/cgame/cg_players.c
@@ -34,7 +34,7 @@ char *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = {
"*pain100_1.wav",
"*falling1.wav",
"*gasp.wav",
- "*drown.wav",
+ // "*drown.wav", // BFP - No drowning
"*fall1.wav",
"*taunt.wav"
};
@@ -87,7 +87,7 @@ Read a configuration file containing animation coutns and rates
models/players/visor/animation.cfg, etc
======================
*/
-static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) {
+qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) {
char *text_p, *prev;
int len;
int i;
@@ -102,7 +102,11 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
// load the file
len = trap_FS_FOpenFile( filename, &f, FS_READ );
+ if ( f == FS_INVALID_HANDLE ) {
+ return qfalse;
+ }
if ( len <= 0 ) {
+ trap_FS_FCloseFile( f );
return qfalse;
}
if ( len >= sizeof( text ) - 1 ) {
@@ -110,7 +114,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
return qfalse;
}
trap_FS_Read( text, len, f );
- text[len] = 0;
+ text[len] = '\0';
trap_FS_FCloseFile( f );
// parse the text
@@ -127,12 +131,12 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
while ( 1 ) {
prev = text_p; // so we can unget
token = COM_Parse( &text_p );
- if ( !token ) {
+ if ( !token[0] ) {
break;
}
if ( !Q_stricmp( token, "footsteps" ) ) {
token = COM_Parse( &text_p );
- if ( !token ) {
+ if ( !token[0] ) {
break;
}
if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) {
@@ -152,7 +156,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
} else if ( !Q_stricmp( token, "headoffset" ) ) {
for ( i = 0 ; i < 3 ; i++ ) {
token = COM_Parse( &text_p );
- if ( !token ) {
+ if ( !token[0] ) {
break;
}
ci->headOffset[i] = atof( token );
@@ -160,7 +164,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
continue;
} else if ( !Q_stricmp( token, "sex" ) ) {
token = COM_Parse( &text_p );
- if ( !token ) {
+ if ( !token[0] ) {
break;
}
if ( token[0] == 'f' || token[0] == 'F' ) {
@@ -184,14 +188,14 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
text_p = prev; // unget the token
break;
}
- Com_Printf( "unknown token '%s' is %s\n", token, filename );
+ Com_Printf( "unknown token '%s' in %s\n", token, filename );
}
// read information for each frame
for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
token = COM_Parse( &text_p );
- if ( !*token ) {
+ if ( !token[0] ) {
if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) {
animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame;
animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp;
@@ -214,7 +218,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
}
token = COM_Parse( &text_p );
- if ( !*token ) {
+ if ( !token[0] ) {
break;
}
animations[i].numFrames = atoi( token );
@@ -228,13 +232,13 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
}
token = COM_Parse( &text_p );
- if ( !*token ) {
+ if ( !token[0] ) {
break;
}
animations[i].loopFrames = atoi( token );
token = COM_Parse( &text_p );
- if ( !*token ) {
+ if ( !token[0] ) {
break;
}
fps = atof( token );
@@ -246,16 +250,17 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
}
if ( i != MAX_ANIMATIONS ) {
- CG_Printf( "Error parsing animation file: %s", filename );
+ CG_Printf( "Error parsing animation file: %s\n", filename );
return qfalse;
}
+ // BFP - memcpy is not necessary, that can be removed
// crouch backward animation
- memcpy(&animations[LEGS_WALKCR], &animations[LEGS_WALKCR], sizeof(animation_t)); // BFP - Crouch backwards animation tweak
- animations[LEGS_WALKCR].reversed = qtrue;
+ // memcpy(&animations[LEGS_WALKCR], &animations[LEGS_WALKCR], sizeof(animation_t)); // BFP - Crouch backwards animation tweak
+ animations[LEGS_WALKCR].reversed = qfalse; // BFP - Make the duck walking forward only
// walk backward animation
- memcpy(&animations[LEGS_WALK], &animations[LEGS_WALK], sizeof(animation_t)); // BFP - Walk backwards animation tweak
- animations[LEGS_WALK].reversed = qtrue;
+ // memcpy(&animations[LEGS_WALK], &animations[LEGS_WALK], sizeof(animation_t)); // BFP - Walk backwards animation tweak
+ animations[LEGS_WALK].reversed = qfalse; // BFP - Make the walk moving forward only
// flag moving fast
animations[FLAG_RUN].firstFrame = 0;
animations[FLAG_RUN].numFrames = 16;
@@ -294,23 +299,32 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
CG_FileExists
==========================
*/
-static qboolean CG_FileExists(const char *filename) {
+static qboolean CG_FileExists( const char *filename ) {
int len;
+ fileHandle_t f;
+
+ len = trap_FS_FOpenFile( filename, &f, FS_READ );
+
+ if ( f != FS_INVALID_HANDLE ) {
+ trap_FS_FCloseFile( f );
+ }
- len = trap_FS_FOpenFile( filename, 0, FS_READ );
- if (len>0) {
+ if ( len > 0 ) {
return qtrue;
}
+
return qfalse;
}
+// BFP - No CG_FindClientModelFile, remove in the future?
+#if 0
/*
==========================
CG_FindClientModelFile
==========================
*/
static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *base, const char *ext ) {
- char *team, *charactersFolder;
+ char *team;
int i;
if ( cgs.gametype >= GT_TEAM ) {
@@ -328,16 +342,15 @@ static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t
else {
team = "default";
}
- charactersFolder = "";
while(1) {
for ( i = 0; i < 2; i++ ) {
if ( i == 0 && teamName && *teamName ) {
- // "models/players/characters/james/stroggs/lower_lily_red.skin"
- Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, team, ext );
+ // "models/players/james/stroggs/lower_lily_red.skin"
+ Com_sprintf( filename, length, "models/players/%s/%s%s_%s_%s.%s", modelName, teamName, base, skinName, team, ext );
}
else {
- // "models/players/characters/james/lower_lily_red.skin"
- Com_sprintf( filename, length, "models/players/%s%s/%s_%s_%s.%s", charactersFolder, modelName, base, skinName, team, ext );
+ // "models/players/james/lower_lily_red.skin"
+ Com_sprintf( filename, length, "models/players/%s/%s_%s_%s.%s", modelName, base, skinName, team, ext );
}
if ( CG_FileExists( filename ) ) {
return qtrue;
@@ -347,27 +360,27 @@ static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t
#if 0
if ( cgs.gametype >= GT_TEAM ) {
if ( i == 0 && teamName && *teamName ) {
- // "models/players/characters/james/stroggs/lower_red.skin"
- Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, team, ext );
+ // "models/players/james/stroggs/lower_red.skin"
+ Com_sprintf( filename, length, "models/players/%s/%s%s_%s.%s", modelName, teamName, base, team, ext );
}
else {
- // "models/players/characters/james/lower_red.skin"
- Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, team, ext );
+ // "models/players/james/lower_red.skin"
+ Com_sprintf( filename, length, "models/players/%s/%s_%s.%s", modelName, base, team, ext );
}
}
else {
if ( i == 0 && teamName && *teamName ) {
- // "models/players/characters/james/stroggs/lower_lily.skin"
- Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, ext );
+ // "models/players/james/stroggs/lower_lily.skin"
+ Com_sprintf( filename, length, "models/players/%s/%s%s_%s.%s", modelName, teamName, base, skinName, ext );
}
else {
- // "models/players/characters/james/lower_lily.skin"
- Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, skinName, ext );
+ // "models/players/james/lower_lily.skin"
+ Com_sprintf( filename, length, "models/players/%s/%s_%s.%s", modelName, base, skinName, ext );
}
}
#endif
- Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, skinName, ext );
+ Com_sprintf( filename, length, "models/players/%s/%s_%s.%s", modelName, base, skinName, ext );
if ( CG_FileExists( filename ) ) {
return qtrue;
}
@@ -375,22 +388,18 @@ static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t
break;
}
}
- // if tried the heads folder first
- if ( charactersFolder[0] ) {
- break;
- }
- charactersFolder = "characters/";
}
return qfalse;
}
+#endif
/*
==========================
CG_FindClientHeadFile
==========================
*/
-static qboolean CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) {
+qboolean CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) {
char *team, *headsFolder;
int i;
@@ -475,28 +484,53 @@ CG_RegisterClientSkin
static qboolean CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName ) {
char filename[MAX_QPATH];
- /*
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/%slower_%s.skin", modelName, teamName, skinName );
+ // BFP - That was a way to load the skins, so CG_FindClientModelFile function does the job,
+ // but it tends to have more memory load. Also the characters folder check is disabled, BFP doesn't have any check of this
+
+ // BFP - Legs skin
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
ci->legsSkin = trap_R_RegisterSkin( filename );
if (!ci->legsSkin) {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%slower_%s.skin", modelName, teamName, skinName );
- ci->legsSkin = trap_R_RegisterSkin( filename );
- if (!ci->legsSkin) {
- Com_Printf( "Leg skin load failure: %s\n", filename );
- }
+ Com_Printf( "Leg skin load failure: %s\n", filename );
}
+ // BFP - Ultimate tier legs skin
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjlower.skin", modelName, skinName );
+ ci->ultTierLegsSkin = trap_R_RegisterSkin( filename );
+ // if (!ci->ultTierLegsSkin) {
+ // Com_Printf( "Ultimate tier leg skin load failure: %s\n", filename );
+ // }
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/%supper_%s.skin", modelName, teamName, skinName );
+ // BFP - Torso skin
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
ci->torsoSkin = trap_R_RegisterSkin( filename );
if (!ci->torsoSkin) {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%supper_%s.skin", modelName, teamName, skinName );
- ci->torsoSkin = trap_R_RegisterSkin( filename );
- if (!ci->torsoSkin) {
- Com_Printf( "Torso skin load failure: %s\n", filename );
- }
+ Com_Printf( "Torso skin load failure: %s\n", filename );
}
- */
+
+ // BFP - Ultimate tier torso skin
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjtorso.skin", modelName, skinName );
+ ci->ultTierTorsoSkin = trap_R_RegisterSkin( filename );
+ // if (!ci->ultTierTorsoSkin) {
+ // Com_Printf( "Ultimate tier torso skin load failure: %s\n", filename );
+ // }
+
+ // BFP - Head skin
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", modelName, skinName );
+ ci->headSkin = trap_R_RegisterSkin( filename );
+ if (!ci->headSkin) {
+ Com_Printf( "Head skin load failure: %s\n", filename );
+ }
+
+ // BFP - Ultimate tier head skin
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjhead.skin", modelName, skinName );
+ ci->ultTierHeadSkin = trap_R_RegisterSkin( filename );
+ // if (!ci->ultTierHeadSkin) {
+ // Com_Printf( "Ultimate tier head skin load failure: %s\n", filename );
+ // }
+
+ // BFP - Uses more memory load, not recommended, remove in the future?
+#if 0
if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "lower", "skin" ) ) {
ci->legsSkin = trap_R_RegisterSkin( filename );
}
@@ -517,6 +551,7 @@ static qboolean CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, c
if (!ci->headSkin) {
Com_Printf( "Head skin load failure: %s\n", filename );
}
+#endif
// if any skins failed to load
if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) {
@@ -535,6 +570,9 @@ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelN
const char *headName;
char newTeamName[MAX_QPATH*2];
+ // BFP - Most stuff is modified here to reduce memory load,
+ // anyone can modify and insert their own customized player folder/load error messages
+
if ( headModelName[0] == '\0' ) {
headName = modelName;
}
@@ -544,42 +582,60 @@ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelN
Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
ci->legsModel = trap_R_RegisterModel( filename );
if ( !ci->legsModel ) {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName );
- ci->legsModel = trap_R_RegisterModel( filename );
- if ( !ci->legsModel ) {
- Com_Printf( "Failed to load model file %s\n", filename );
- return qfalse;
- }
+ Com_Printf( "Failed to load model file %s\n", filename );
+ return qfalse;
}
+ // BFP - Ultimate tier legs model
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjlegs.md3", modelName );
+ ci->ultTierLegsModel = trap_R_RegisterModel( filename );
+ /*if ( !ci->ultTierLegsModel ) {
+ Com_Printf( "Failed to load ultimate tier legs model file %s\n", filename );
+ return qfalse;
+ }*/
+
Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
ci->torsoModel = trap_R_RegisterModel( filename );
if ( !ci->torsoModel ) {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName );
- ci->torsoModel = trap_R_RegisterModel( filename );
- if ( !ci->torsoModel ) {
- Com_Printf( "Failed to load model file %s\n", filename );
- return qfalse;
- }
+ Com_Printf( "Failed to load model file %s\n", filename );
+ return qfalse;
}
- if( headName[0] == '*' ) {
- Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] );
- }
- else {
+ // BFP - Ultimate tier torso model
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjtorso.md3", modelName );
+ ci->ultTierTorsoModel = trap_R_RegisterModel( filename );
+ /*if ( !ci->ultTierTorsoModel ) {
+ Com_Printf( "Failed to load ultimate tier torso model file %s\n", filename );
+ return qfalse;
+ }*/
+
+ // if( headName[0] == '*' ) {
+ // Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] );
+ // }
+ // else {
Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headName );
- }
+ // }
ci->headModel = trap_R_RegisterModel( filename );
+ /*
// if the head model could not be found and we didn't load from the heads folder try to load from there
if ( !ci->headModel && headName[0] != '*' ) {
Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName );
ci->headModel = trap_R_RegisterModel( filename );
}
+ */
if ( !ci->headModel ) {
Com_Printf( "Failed to load model file %s\n", filename );
return qfalse;
}
+ // BFP - Ultimate tier head model
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/ssjhead.md3", modelName );
+ ci->ultTierHeadModel = trap_R_RegisterModel( filename );
+ /*if ( !ci->ultTierHeadModel ) {
+ Com_Printf( "Failed to load ultimate tier head model file %s\n", filename );
+ return qfalse;
+ }*/
+
// if any skins failed to load, return failure
if ( !CG_RegisterClientSkin( ci, teamName, modelName, skinName, headName, headSkinName ) ) {
if ( teamName && *teamName) {
@@ -603,11 +659,8 @@ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelN
// load the animations
Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
if ( !CG_ParseAnimationFile( filename, ci ) ) {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName );
- if ( !CG_ParseAnimationFile( filename, ci ) ) {
- Com_Printf( "Failed to load animation file %s\n", filename );
- return qfalse;
- }
+ Com_Printf( "Failed to load animation file %s\n", filename );
+ return qfalse;
}
if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "skin" ) ) {
@@ -624,6 +677,8 @@ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelN
return qtrue;
}
+ // BFP - No color1
+#if 0
/*
====================
CG_ColorFromString
@@ -651,6 +706,7 @@ static void CG_ColorFromString( const char *v, vec3_t color ) {
color[0] = 1.0f;
}
}
+#endif
/*
===================
@@ -671,7 +727,9 @@ static void CG_LoadClientInfo( clientInfo_t *ci ) {
modelloaded = qtrue;
if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ) ) {
if ( cg_buildScript.integer ) {
- CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname );
+ // BFP - Use a different error message like original BFP
+ // CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname );
+ CG_Error( "Model \"%s\" not found.\n", ci->modelName );
modelloaded = qfalse; // BFP - if the model didn't load
}
@@ -694,6 +752,9 @@ static void CG_LoadClientInfo( clientInfo_t *ci ) {
}
}
#endif
+ if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", DEFAULT_MODEL, "default", teamname ) ) {
+ CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL );
+ }
}
ci->newAnims = qfalse;
@@ -737,6 +798,8 @@ static void CG_LoadClientInfo( clientInfo_t *ci ) {
}
}
+// BFP - Unused static functions, remove? I think so
+#if 0
/*
======================
CG_CopyClientInfoModel
@@ -870,6 +933,7 @@ static void CG_SetDeferredClientInfo( clientInfo_t *ci ) {
CG_LoadClientInfo( ci );
}
+#endif
/*
@@ -900,20 +964,23 @@ void CG_NewClientInfo( int clientNum ) {
v = Info_ValueForKey(configstring, "n");
Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
+ // BFP - No color1
+#if 0
// colors
v = Info_ValueForKey( configstring, "c1" );
CG_ColorFromString( v, newInfo.color1 );
-
- v = Info_ValueForKey( configstring, "c2" );
- CG_ColorFromString( v, newInfo.color2 );
+#endif
// bot skill
v = Info_ValueForKey( configstring, "skill" );
newInfo.botSkill = atoi( v );
+ // BFP - No handicap
+#if 0
// handicap
v = Info_ValueForKey( configstring, "hc" );
newInfo.handicap = atoi( v );
+#endif
// wins
v = Info_ValueForKey( configstring, "w" );
@@ -927,22 +994,10 @@ void CG_NewClientInfo( int clientNum ) {
v = Info_ValueForKey( configstring, "t" );
newInfo.team = atoi( v );
- // team task
- v = Info_ValueForKey( configstring, "tt" );
- newInfo.teamTask = atoi(v);
-
- // team leader
- v = Info_ValueForKey( configstring, "tl" );
- newInfo.teamLeader = atoi(v);
-
- v = Info_ValueForKey( configstring, "g_redteam" );
- Q_strncpyz(newInfo.redTeam, v, MAX_TEAMNAME);
-
- v = Info_ValueForKey( configstring, "g_blueteam" );
- Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME);
-
// model
v = Info_ValueForKey( configstring, "model" );
+ // BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?)
+#if 0
if ( cg_forceModel.integer ) {
// forcemodel makes everyone use a single model
// to prevent load hitches
@@ -971,22 +1026,27 @@ void CG_NewClientInfo( int clientNum ) {
Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
}
}
+ } else
+#endif
+ Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) );
+
+ slash = strchr( newInfo.modelName, '/' );
+ if ( !slash ) {
+ // modelName didn not include a skin name
+ Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
} else {
- Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) );
+ Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
+ // truncate modelName
+ *slash = 0;
+ }
- slash = strchr( newInfo.modelName, '/' );
- if ( !slash ) {
- // modelName didn not include a skin name
- Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
- } else {
- Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
- // truncate modelName
- *slash = 0;
- }
+ // BFP - cg_forceSkin, forces the default skin for each model
+ if ( cg_forceSkin.integer ) {
+ Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
}
- // head model
- v = Info_ValueForKey( configstring, "hmodel" );
+ // BFP - No force model (In the future, remove cg_forceModel, which wasn't removed originally?)
+#if 0
if ( cg_forceModel.integer ) {
// forcemodel makes everyone use a single model
// to prevent load hitches
@@ -1015,23 +1075,33 @@ void CG_NewClientInfo( int clientNum ) {
Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
}
}
+ } else
+#endif
+
+ // BFP - Set the head model name as the same as model name
+ Q_strncpyz( newInfo.headModelName, newInfo.modelName, sizeof( newInfo.headModelName ) );
+ slash = strchr( newInfo.headModelName, '/' );
+ if ( !slash ) {
+ // modelName didn not include a skin name
+ Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
} else {
- Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) );
+ Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
+ // truncate modelName
+ *slash = 0;
+ }
- slash = strchr( newInfo.headModelName, '/' );
- if ( !slash ) {
- // modelName didn not include a skin name
- Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
- } else {
- Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
- // truncate modelName
- *slash = 0;
- }
+ // BFP - cg_forceSkin, forces the default skin for each model
+ if ( cg_forceSkin.integer ) {
+ Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
}
+ // BFP - Change the model without impeding with defer
+ // BFP - TODO: Remove cg_deferPlayers cvar and its unnecessary code in the future
+#if 0
// scan for an existing clientinfo that matches this modelname
// so we can avoid loading checks if possible
- if ( !CG_ScanForExistingClientInfo( &newInfo ) ) {
+ // if ( !CG_ScanForExistingClientInfo( &newInfo ) )
+ // {
qboolean forceDefer;
forceDefer = trap_MemoryRemaining() < 4000000;
@@ -1046,9 +1116,10 @@ void CG_NewClientInfo( int clientNum ) {
newInfo.deferred = qfalse;
}
} else {
+#endif
CG_LoadClientInfo( &newInfo );
- }
- }
+ // }
+ // }
// replace whatever was there with the new one
newInfo.infoValid = qtrue;
@@ -1244,12 +1315,9 @@ static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float
// if ( cent->currentState.powerups & ( 1 << PW_HASTE ) ) {
// BFP - When using ki boost use the following speed as haste powerup
- if ( cent->currentState.number == cg.snap->ps.clientNum
- && ( cent->currentState.eFlags & EF_AURA )
- && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
- && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
- && ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND
- && ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_CHARGE ) {
+ if ( ( cent->currentState.eFlags & EF_AURA )
+ && ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_RUN
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_BACK ) ) {
speedScale = 1.5; // when using ki boost
} else {
speedScale = 1;
@@ -1398,9 +1466,18 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
// --------- yaw -------------
+ // BFP - Allow yaw while flying too
// allow yaw to drift a bit
- if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
- || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) {
+ if ( ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND )
+ && ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_FLYIDLE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND )
+ && ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLECR
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STUN )
+ && ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STUN )
+ && ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_CHARGE ) ) {
// if not standing still, always point all in the same direction
cent->pe.torso.yawing = qtrue; // always center
cent->pe.torso.pitching = qtrue; // always center
@@ -1420,9 +1497,10 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ];
torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ];
+ // BFP - Swing the angles to make the movements look smooth
// torso
- CG_SwingAngles( torsoAngles[YAW], 25, 90, cg_swingSpeed.value, ¢->pe.torso.yawAngle, ¢->pe.torso.yawing );
- CG_SwingAngles( legsAngles[YAW], 40, 90, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing );
+ CG_SwingAngles( torsoAngles[YAW], 40, 90, cg_swingSpeed.value, ¢->pe.torso.yawAngle, ¢->pe.torso.yawing ); // BFP - Before: 25, 90
+ CG_SwingAngles( legsAngles[YAW], 90, 90, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); // BFP - Before: 40, 90
torsoAngles[YAW] = cent->pe.torso.yawAngle;
legsAngles[YAW] = cent->pe.legs.yawAngle;
@@ -1436,7 +1514,14 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
} else {
dest = headAngles[PITCH] * 0.75f;
}
- CG_SwingAngles( dest, 15, 30, 0.1f, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching );
+ // BFP - When flying, set the legs in the first case
+ if ( cent->currentState.eFlags & EF_FLIGHT ) {
+ CG_SwingAngles( dest, 15, 30, 0.1f, ¢->pe.legs.pitchAngle, ¢->pe.legs.pitching );
+ legsAngles[PITCH] = cent->pe.legs.pitchAngle;
+ }
+
+ // BFP - When flying, set the torso correctly into these angles
+ CG_SwingAngles( dest, 30, 30, 0.1f, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching );
torsoAngles[PITCH] = cent->pe.torso.pitchAngle;
//
@@ -1450,6 +1535,8 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
// --------- roll -------------
+ // BFP - Prevent the player's head from tilting
+ headAngles[ROLL] = 0;
// lean towards the direction of travel
VectorCopy( cent->currentState.pos.trDelta, velocity );
@@ -1458,7 +1545,11 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
vec3_t axis[3];
float side;
- speed *= 0.01f; // BFP - adjust legs speed, before 0.05f
+ // BFP - Speed handling when moving too much the angles
+ if ( speed < -480.0f ) speed = -480.0f;
+ if ( speed > 480.0f ) speed = 480.0f;
+
+ speed *= 0.03f; // BFP - Adjust speed when rotate the angles (not a starting velocity), before: 0.05f
AnglesToAxis( legsAngles, axis );
side = speed * DotProduct( velocity, axis[1] );
@@ -1466,8 +1557,17 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
side = speed * DotProduct( velocity, axis[0] );
legsAngles[PITCH] += side;
+
+ // BFP - Make the torso move the pitch angle a bit in the flight
+ if ( cent->currentState.eFlags & EF_FLIGHT ) {
+ AnglesToAxis( torsoAngles, axis );
+ side = speed * DotProduct( velocity, axis[0] );
+ torsoAngles[PITCH] += side;
+ }
}
+ // BFP - Don't make every player forced to see this way with their legs to the others
+#if 0
//
clientNum = cent->currentState.clientNum;
if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
@@ -1478,13 +1578,7 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
legsAngles[ROLL] = 0.0f;
}
}
-
- // BFP - when flying, set correctly into these angles
- if ( cg.predictedPlayerState.pm_flags & PMF_FLYING ) {
- VectorCopy( cent->lerpAngles, headAngles );
- VectorCopy( cent->lerpAngles, torsoAngles );
- VectorCopy( cent->lerpAngles, legsAngles );
- }
+#endif
// pain twitch
CG_AddPainTwitch( cent, torsoAngles );
@@ -1500,6 +1594,8 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v
//==========================================================================
+// BFP - No smoke puff effect
+#if 0
/*
===============
CG_HasteTrail
@@ -1513,6 +1609,7 @@ static void CG_HasteTrail( centity_t *cent ) {
if ( cent->trailTime > cg.time ) {
return;
}
+
anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT;
if ( anim != LEGS_RUN && anim != LEGS_BACK ) {
return;
@@ -1527,17 +1624,18 @@ static void CG_HasteTrail( centity_t *cent ) {
origin[2] -= 16;
smoke = CG_SmokePuff( origin, vec3_origin,
- 8,
- 1, 1, 1, 1,
- 500,
- cg.time,
- 0,
- 0,
- cgs.media.hastePuffShader );
+ 8,
+ 1, 1, 1, 1,
+ 500,
+ cg.time,
+ 0,
+ 0,
+ cgs.media.hastePuffShader );
// use the optimized local entity add
smoke->leType = LE_SCALE_FADE;
}
+#endif
/*
===============
@@ -1549,6 +1647,11 @@ static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) {
vec3_t angles;
vec3_t axis[3];
+ // BFP - Don't show the model to the player itself
+ if ( cent->currentState.clientNum == cg.snap->ps.clientNum ) {
+ return;
+ }
+
VectorCopy( cent->lerpAngles, angles );
angles[PITCH] = 0;
angles[ROLL] = 0;
@@ -1701,12 +1804,13 @@ static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) {
trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 );
}
+ // BFP - No flight powerup
+#if 0
// flight plays a looped sound
- /*
if ( powerups & ( 1 << PW_FLIGHT ) ) {
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flightSound );
}
- */
+#endif
ci = &cgs.clientinfo[ cent->currentState.clientNum ];
// redflag
@@ -1731,21 +1835,13 @@ static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) {
trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1.0 );
}
- // neutralflag
- if ( powerups & ( 1 << PW_NEUTRALFLAG ) ) {
- if (ci->newAnims) {
- CG_PlayerFlag( cent, cgs.media.neutralFlagFlapSkin, torso );
- }
- else {
- CG_TrailItem( cent, cgs.media.neutralFlagModel );
- }
- trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 1.0, 1.0 );
- }
-
+ // BFP - No haste powerup handling
+#if 0
// haste leaves smoke trails
if ( powerups & ( 1 << PW_HASTE ) ) {
CG_HasteTrail( cent );
}
+#endif
}
@@ -1760,7 +1856,8 @@ static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) {
int rf;
refEntity_t ent;
- if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) {
+ // BFP - Don't show the float sprite to the player itself, only in mirrors
+ if ( cent->currentState.number == cg.snap->ps.clientNum ) { // && !cg.renderingThirdPerson ) {
rf = RF_THIRD_PERSON; // only show in mirrors
} else {
rf = 0;
@@ -1768,7 +1865,7 @@ static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) {
memset( &ent, 0, sizeof( ent ) );
VectorCopy( cent->lerpOrigin, ent.origin );
- ent.origin[2] += 48;
+ ent.origin[2] += 70; // BFP - BFP puts the floating sprite a bit up. Q3 default: 48
ent.reType = RT_SPRITE;
ent.customShader = shader;
ent.radius = 10;
@@ -1777,10 +1874,30 @@ static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) {
ent.shaderRGBA[1] = 255;
ent.shaderRGBA[2] = 255;
ent.shaderRGBA[3] = 255;
+ // BFP - Monster gamemode, player monster' floating sprite is bigger and higher than a normal one
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ ent.origin[2] += 250;
+ ent.radius = 50;
+ }
trap_R_AddRefEntityToScene( &ent );
}
+/*
+===============
+CG_FloatSpriteCheck
+
+Check if there's some EF flag enabled
+===============
+*/
+static qboolean CG_FloatSpriteCheck( centity_t *cent, int eFlag, qhandle_t shader ) { // BFP - Check EF flag and don't show float sprite to the player itself
+ if ( cent->currentState.eFlags & eFlag ) {
+ CG_PlayerFloatSprite( cent, shader );
+ return qtrue;
+ }
+ return qfalse;
+}
+
/*
===============
@@ -1792,54 +1909,110 @@ Float sprites over the player's head
static void CG_PlayerSprites( centity_t *cent ) {
int team;
- if ( cent->currentState.eFlags & EF_CONNECTION ) {
- CG_PlayerFloatSprite( cent, cgs.media.connectionShader );
- return;
- }
+ if ( CG_FloatSpriteCheck( cent, EF_CONNECTION, cgs.media.connectionShader ) ) return;
+ if ( CG_FloatSpriteCheck( cent, EF_TALK, cgs.media.balloonShader ) ) return;
+ if ( CG_FloatSpriteCheck( cent, EF_AWARD_EXCELLENT, cgs.media.medalExcellent ) ) return;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+#if 0
+ if ( CG_FloatSpriteCheck( cent, EF_AWARD_IMPRESSIVE, cgs.media.medalImpressive ) ) return;
+ if ( CG_FloatSpriteCheck( cent, EF_AWARD_GAUNTLET, cgs.media.medalGauntlet ) ) return;
+ if ( CG_FloatSpriteCheck( cent, EF_AWARD_DEFEND, cgs.media.medalDefend ) ) return;
+ if ( CG_FloatSpriteCheck( cent, EF_AWARD_ASSIST, cgs.media.medalAssist ) ) return;
+ if ( CG_FloatSpriteCheck( cent, EF_AWARD_CAP, cgs.media.medalCapture ) ) return;
+#endif
- if ( cent->currentState.eFlags & EF_TALK ) {
- CG_PlayerFloatSprite( cent, cgs.media.balloonShader );
+ team = cgs.clientinfo[ cent->currentState.clientNum ].team;
+ if ( !(cent->currentState.eFlags & EF_DEAD) &&
+ cg.snap->ps.persistant[PERS_TEAM] == team &&
+ cgs.gametype >= GT_TEAM) {
+ // BFP - BFP doesn't use cg_drawFriend to draw that floating friend sprite, keeps enabled always.
+ // Just wonder if it was some kind of logic reason and looks like that cvar to be removed was also forgotten
+ // if (cg_drawFriend.integer) {
+ CG_PlayerFloatSprite( cent, cgs.media.friendShader );
+ // }
return;
}
+}
- if ( cent->currentState.eFlags & EF_AWARD_IMPRESSIVE ) {
- CG_PlayerFloatSprite( cent, cgs.media.medalImpressive );
- return;
- }
+/*
+===============
+CG_ChargeSmokeBubbles
- if ( cent->currentState.eFlags & EF_AWARD_EXCELLENT ) {
- CG_PlayerFloatSprite( cent, cgs.media.medalExcellent );
- return;
- }
+Spawns charge smoke and bubble particles when being near something solid or water
+===============
+*/
+static void CG_ChargeSmokeBubbles( centity_t *cent, vec3_t mins, vec3_t maxs,
+ float chargeSmokeSize, float chargeSmokeRadialVel,
+ float chargeSmokeBaseRadius, float chargeSmokeUpOrigin,
+ float bubbleSize, float bubbleRange ) { // BFP - Charging smoke and bubble particles
+ int distance = 300;
+ vec3_t end;
+ trace_t trace;
+ vec3_t chargeSmokePos;
+ int waterContents;
- if ( cent->currentState.eFlags & EF_AWARD_GAUNTLET ) {
- CG_PlayerFloatSprite( cent, cgs.media.medalGauntlet );
- return;
+ // BFP - Monster gamemode, increase the distance for the particles if the player is the monster
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ distance = 600;
}
+
+ // send a trace down from the player to the ground
+ VectorCopy( cent->lerpOrigin, end );
+ end[2] -= distance;
- if ( cent->currentState.eFlags & EF_AWARD_DEFEND ) {
- CG_PlayerFloatSprite( cent, cgs.media.medalDefend );
- return;
- }
+ trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
- if ( cent->currentState.eFlags & EF_AWARD_ASSIST ) {
- CG_PlayerFloatSprite( cent, cgs.media.medalAssist );
- return;
+ VectorCopy( trace.endpos, chargeSmokePos );
+ chargeSmokePos[2] += chargeSmokeUpOrigin; // put a bit above
+
+ waterContents = CG_PointContents( trace.endpos, -1 ); // detect if the player is entirely under water
+ if ( !( cent->currentState.eFlags & EF_KI_BOOST )
+ && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_CHARGE
+ && !( waterContents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
+#if 0 /* if the player isn't moving */
+ && !cent->currentState.pos.trDelta[0]
+ && !cent->currentState.pos.trDelta[1]
+ && !cent->currentState.pos.trDelta[2]
+#endif
+ && ( trace.fraction < 1.0f || cent->currentState.groundEntityNum != ENTITYNUM_NONE ) ) {
+ // if stepping a mover
+ if ( !( trace.fraction <= 0.75f )
+ && cent->currentState.groundEntityNum != ENTITYNUM_NONE ) {
+ chargeSmokePos[2] += 280;
+ // BFP - Monster gamemode, increase the charge smoke pos distance if the player is the monster
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ chargeSmokePos[2] += 230;
+ }
+ }
+ CG_ParticleChargeSmoke( cent, cgs.media.particleSmokeShader, chargeSmokePos, chargeSmokeSize, chargeSmokeRadialVel, chargeSmokeBaseRadius );
}
- if ( cent->currentState.eFlags & EF_AWARD_CAP ) {
- CG_PlayerFloatSprite( cent, cgs.media.medalCapture );
+ // don't spawn other bubble particles if already underwater
+ waterContents = CG_PointContents( cent->lerpOrigin, -1 );
+ if ( waterContents & CONTENTS_WATER ) {
return;
}
- team = cgs.clientinfo[ cent->currentState.clientNum ].team;
- if ( !(cent->currentState.eFlags & EF_DEAD) &&
- cg.snap->ps.persistant[PERS_TEAM] == team &&
- cgs.gametype >= GT_TEAM) {
- if (cg_drawFriend.integer) {
- CG_PlayerFloatSprite( cent, cgs.media.friendShader );
+ // water surface
+ if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_RUN
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_BACK
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYA
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYB
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_CHARGE ) { // apply on ki charging status too
+ trace_t waterTrace;
+
+ // tracing the water surface
+ trap_CM_BoxTrace( &waterTrace, cent->lerpOrigin, end, mins, maxs, 0, CONTENTS_WATER );
+
+ waterTrace.endpos[2] -= 8; // put a bit down to make the bubbles move
+ if ( cent->currentState.eFlags & EF_MONSTER ) { // BFP - Monster gamemode, put a bit more down the bubbles
+ waterTrace.endpos[2] -= 20;
+ }
+ if ( waterTrace.fraction >= 0 && waterTrace.fraction <= 0.70f ) {
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, waterTrace.endpos, end, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, waterTrace.endpos, end, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, waterTrace.endpos, end, 700, bubbleRange, bubbleSize );
}
- return;
}
}
@@ -1852,28 +2025,176 @@ Returns the Z component of the surface being shadowed
should it return a full plane instead of a Z?
===============
*/
-#define SHADOW_DISTANCE 128
static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) {
+ // BFP - Shadow distance variable isn't constant anymore
+ int shadow_distance = 128;
vec3_t end, mins = {-15, -15, 0}, maxs = {15, 15, 2};
trace_t trace;
float alpha;
+ int contents, waterContents; // BFP - To detect if there is water or lava
+ float radius = 24.0f; // BFP - Shadow radius
+
+ // BFP - Bubble particle size and range
+ float bubbleSize = 2;
+ float bubbleRange = 10;
+
+ // BFP - Dash smoke particle size, velocity and acceleration (dispersion)
+ float dashSmokeSize = 25;
+ float dashSmokeVelDisp = 401;
+ float dashSmokeUpVel = 20;
+ float dashSmokeAccel = 10;
+
+ // BFP - Antigrav rock particle size, spawn range and end time
+ float antigravRockSize = 2;
+ float antigravRockSpawnRange = 50;
+ float antigravRockEndTime = 450;
+
+ // BFP - Charge smoke particle size, radial velocity, base radius and up origin
+ float chargeSmokeSize = 40;
+ float chargeSmokeRadialVel = 450;
+ float chargeSmokeBaseRadius = 80;
+ float chargeSmokeUpOrigin = 20;
+
+ // BFP - Monster gamemode, resize shadow and particles if the player is the monster
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ shadow_distance = 400;
+ mins[0] *= 2.5;
+ mins[1] *= 2.5;
+
+ maxs[0] *= 2.5;
+ maxs[1] *= 2.5;
+ radius = 180;
+ bubbleSize = 8;
+ bubbleRange = 150;
+
+ dashSmokeSize = 120;
+ dashSmokeVelDisp = 1201;
+ dashSmokeUpVel = 1400;
+ dashSmokeAccel = 260;
+
+ antigravRockSize = 8;
+ antigravRockSpawnRange = 220;
+ antigravRockEndTime = 2050;
+
+ chargeSmokeSize = 150;
+ chargeSmokeRadialVel = 760;
+ chargeSmokeBaseRadius = 300;
+ chargeSmokeUpOrigin = 50;
+ }
*shadowPlane = 0;
+ // send a trace down from the player to the ground
+ VectorCopy( cent->lerpOrigin, end );
+ end[2] -= shadow_distance;
+
+ trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
+
+ // BFP - Dash smoke and bubble particles when using ki boost on the ground or above the water
+ contents = CG_PointContents( trace.endpos, -1 );
+ if ( ( cent->currentState.eFlags & EF_AURA ) || ( cent->currentState.eFlags & EF_AURA_TIER_UP ) ) {
+ if ( ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_RUN
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_BACK
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYA
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYB
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_CHARGE ) // apply on ki charging status too
+ && ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
+ && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ && ( trace.fraction <= 0.70f
+ // If the player is stepping a mover:
+ || cent->currentState.groundEntityNum != ENTITYNUM_NONE ) ) ) {
+ // BFP - Apply dash smoke particle for the trail, if the function were used directly, it would generate too many particles than we expected
+ vec3_t dashSmokePos;
+ VectorCopy( trace.endpos, dashSmokePos );
+
+ // if stepping a mover
+ if ( !( trace.fraction <= 0.70f )
+ && cent->currentState.groundEntityNum != ENTITYNUM_NONE ) {
+ dashSmokePos[2] += 100;
+ }
+ CG_ParticleDashSmoke( cent, cgs.media.particleSmokeShader, dashSmokePos, dashSmokeSize, dashSmokeVelDisp, dashSmokeUpVel, dashSmokeAccel );
+ }
+
+ // BFP - Charging smoke and bubble particles
+ CG_ChargeSmokeBubbles( cent, mins, maxs,
+ chargeSmokeSize, chargeSmokeRadialVel,
+ chargeSmokeBaseRadius, chargeSmokeUpOrigin,
+ bubbleSize, bubbleRange );
+
+ waterContents = CG_PointContents( cent->lerpOrigin, -1 ); // BFP - Detect if the player is entirely under water
+ // BFP - Antigrav rock particles on ki charging status
+ if ( !( cent->currentState.eFlags & EF_KI_BOOST )
+ && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_CHARGE
+ && !( waterContents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
+#if 0 /* if the player isn't moving */
+ && !cent->currentState.pos.trDelta[0]
+ && !cent->currentState.pos.trDelta[1]
+ && !cent->currentState.pos.trDelta[2]
+#endif
+ && ( trace.fraction <= 0.75f
+ // If the player is stepping a mover:
+ || cent->currentState.groundEntityNum != ENTITYNUM_NONE )
+ && !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
+ // BFP - Spawn randomly the antigrav rock shaders with the particles
+ vec3_t antigravRockPos;
+ VectorCopy( trace.endpos, antigravRockPos );
+
+ // if stepping a mover
+ if ( !( trace.fraction <= 0.75f )
+ && cent->currentState.groundEntityNum != ENTITYNUM_NONE ) {
+ antigravRockPos[2] += 100;
+ }
+
+ // BFP - Rock models on ki recharge
+ if ( cg_3dparticles.integer > 0
+ && cgs.media.pebbleMdl1
+ && cgs.media.pebbleMdl2
+ && cgs.media.pebbleMdl3 ) {
+ int rockModelIndex = rand() % 3;
+ switch ( rockModelIndex ) {
+ case 0: {
+ CG_ParticleAntigravRock( cgs.media.pebbleShader1, cgs.media.pebbleMdl1, cent, cent->currentState.clientNum, antigravRockPos, antigravRockSize, antigravRockSpawnRange, antigravRockEndTime );
+ break;
+ }
+ case 1: {
+ CG_ParticleAntigravRock( cgs.media.pebbleShader2, cgs.media.pebbleMdl2, cent, cent->currentState.clientNum, antigravRockPos, antigravRockSize, antigravRockSpawnRange, antigravRockEndTime );
+ break;
+ }
+ default: {
+ CG_ParticleAntigravRock( cgs.media.pebbleShader3, cgs.media.pebbleMdl3, cent, cent->currentState.clientNum, antigravRockPos, antigravRockSize, antigravRockSpawnRange, antigravRockEndTime );
+ }
+ }
+ } else {
+ int shaderIndex = rand() % 3;
+ switch ( shaderIndex ) {
+ case 0: {
+ CG_ParticleAntigravRock( cgs.media.pebbleShader1, 0, cent, cent->currentState.clientNum, antigravRockPos, antigravRockSize, antigravRockSpawnRange, antigravRockEndTime );
+ break;
+ }
+ case 1: {
+ CG_ParticleAntigravRock( cgs.media.pebbleShader2, 0, cent, cent->currentState.clientNum, antigravRockPos, antigravRockSize, antigravRockSpawnRange, antigravRockEndTime );
+ break;
+ }
+ default: {
+ CG_ParticleAntigravRock( cgs.media.pebbleShader3, 0, cent, cent->currentState.clientNum, antigravRockPos, antigravRockSize, antigravRockSpawnRange, antigravRockEndTime );
+ }
+ }
+ }
+
+ }
+ }
+
if ( cg_shadows.integer == 0 ) {
return qfalse;
}
+ // BFP - No invis powerup
+#if 0
// no shadows when invisible
if ( cent->currentState.powerups & ( 1 << PW_INVIS ) ) {
return qfalse;
}
-
- // send a trace down from the player to the ground
- VectorCopy( cent->lerpOrigin, end );
- end[2] -= SHADOW_DISTANCE;
-
- trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
+#endif
// no shadow if too high
if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) {
@@ -1895,7 +2216,7 @@ static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) {
// add the mark as a temporary, so it goes directly to the renderer
// without taking a spot in the cg_marks array
CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal,
- cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, 24, qtrue );
+ cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, radius, qtrue );
return qtrue;
}
@@ -1913,6 +2234,7 @@ static void CG_PlayerSplash( centity_t *cent ) {
trace_t trace;
int contents;
polyVert_t verts[4];
+ float markSize = 32;
if ( !cg_shadows.integer ) {
return;
@@ -1921,6 +2243,12 @@ static void CG_PlayerSplash( centity_t *cent ) {
VectorCopy( cent->lerpOrigin, end );
end[2] -= 24;
+ // BFP - Monster gamemode, player monster water surface mark size and position
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ markSize = 192;
+ end[2] -= 114;
+ }
+
// if the feet aren't in liquid, don't make a mark
// this won't handle moving water brushes, but they wouldn't draw right anyway...
contents = trap_CM_PointContents( end, 0 );
@@ -1931,6 +2259,11 @@ static void CG_PlayerSplash( centity_t *cent ) {
VectorCopy( cent->lerpOrigin, start );
start[2] += 32;
+ // BFP - Monster gamemode, player monster water surface mark position
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ start[2] += 164;
+ }
+
// if the head isn't out of liquid, don't make a mark
contents = trap_CM_PointContents( start, 0 );
if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
@@ -1946,50 +2279,238 @@ static void CG_PlayerSplash( centity_t *cent ) {
// create a mark polygon
VectorCopy( trace.endpos, verts[0].xyz );
- verts[0].xyz[0] -= 32;
- verts[0].xyz[1] -= 32;
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
+ verts[0].xyz[0] -= markSize;
+ verts[0].xyz[1] -= markSize;
+ Vector2Set( verts[0].st, 0, 0 );
+ Byte4Set( verts[0].modulate, 255, 255, 255, 255 );
VectorCopy( trace.endpos, verts[1].xyz );
- verts[1].xyz[0] -= 32;
- verts[1].xyz[1] += 32;
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
+ verts[1].xyz[0] -= markSize;
+ verts[1].xyz[1] += markSize;
+ Vector2Set( verts[1].st, 0, 1 );
+ Byte4Set( verts[1].modulate, 255, 255, 255, 255 );
VectorCopy( trace.endpos, verts[2].xyz );
- verts[2].xyz[0] += 32;
- verts[2].xyz[1] += 32;
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
+ verts[2].xyz[0] += markSize;
+ verts[2].xyz[1] += markSize;
+ Vector2Set( verts[2].st, 1, 1 );
+ Byte4Set( verts[2].modulate, 255, 255, 255, 255 );
VectorCopy( trace.endpos, verts[3].xyz );
- verts[3].xyz[0] += 32;
- verts[3].xyz[1] -= 32;
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
+ verts[3].xyz[0] += markSize;
+ verts[3].xyz[1] -= markSize;
+ Vector2Set( verts[3].st, 1, 0 );
+ Byte4Set( verts[3].modulate, 255, 255, 255, 255 );
trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts );
}
+/*
+===============
+CG_ModelSize
+
+Change/scale model size
+===============
+*/
+void CG_ModelSize( refEntity_t *model, float size ) { // BFP - Model size
+ model->axis[0][0] *= size;
+ model->axis[0][1] *= size;
+ model->axis[0][2] *= size;
+
+ model->axis[1][0] *= size;
+ model->axis[1][1] *= size;
+ model->axis[1][2] *= size;
+
+ model->axis[2][0] *= size;
+ model->axis[2][1] *= size;
+ model->axis[2][2] *= size;
+}
+
+/*
+==========================
+CG_AuraPowerlevelSetShaderColor
+
+Powerlevel is divided into tiers.
+While most powerlevel effects are based on the powerlevel itself,
+aura color is determined by the tier.
+
+- Tier 1: < 100,000 PL Blue aura
+- Tier 2: 100,000 – 250,000 PL Red aura
+- Tier 3: 250,000 – 500,000 PL Red aura
+- Tier 4: 500,000 – 999,000 PL Red aura
+- Ultimate Tier: 1 mil PL Yellow aura
+==========================
+*/
+static qhandle_t CG_AuraPowerlevelSetShaderColor( entityState_t *state ) {
+ qhandle_t auraShader = cgs.media.auraRedTinyShader;
+ int powerlevel = state->frame;
+
+ if ( state->clientNum == cg.snap->ps.clientNum ) { // fixes a weird bug when trying to see the powerlevel of itself
+ powerlevel = cg.snap->ps.persistant[PERS_POWERLEVEL];
+ }
+
+ // red
+ if ( cg_lightweightAuras.integer <= 0
+ && cg_polygonAura.integer <= 0
+ && cg_spriteAura.integer <= 0
+ && cg_particleAura.integer <= 0 ) {
+ auraShader = cgs.media.auraRedChargeShader;
+ if ( ( state->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ && !( state->eFlags & EF_AURA_TIER_UP ) ) {
+ auraShader = cgs.media.auraRedUseShader;
+ }
+ }
+ // blue
+ if ( powerlevel < 100
+ || ( cgs.gametype >= GT_TEAM && cgs.clientinfo[ state->clientNum ].team == TEAM_BLUE ) ) {
+ auraShader = cgs.media.auraBlueTinyShader;
+
+ if ( cg_lightweightAuras.integer <= 0
+ && cg_polygonAura.integer <= 0
+ && cg_spriteAura.integer <= 0
+ && cg_particleAura.integer <= 0 ) {
+ auraShader = cgs.media.auraBlueChargeShader;
+ if ( ( state->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ && !( state->eFlags & EF_AURA_TIER_UP ) ) {
+ auraShader = cgs.media.auraBlueUseShader;
+ }
+ }
+ }
+ // yellow
+ if ( powerlevel >= 1000
+ && !( cgs.gametype >= GT_TEAM ) ) {
+ auraShader = cgs.media.auraYellowTinyShader;
+
+ if ( cg_lightweightAuras.integer <= 0
+ && cg_polygonAura.integer <= 0
+ && cg_spriteAura.integer <= 0
+ && cg_particleAura.integer <= 0 ) {
+ auraShader = cgs.media.auraYellowChargeShader;
+ if ( ( state->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ && !( state->eFlags & EF_AURA_TIER_UP ) ) {
+ auraShader = cgs.media.auraYellowUseShader;
+ }
+ }
+ }
+ return auraShader;
+}
+
+/*
+===============
+CG_AuraAnims
+
+Handle aura animations, when idling it sets the aura vertical rotation, so the aura rotates vertically
+===============
+*/
+static void CG_AuraAnims( centity_t *cent, refEntity_t *aura, qboolean reversed, vec3_t auraInverseRotation ) { // BFP - Aura animations (change model shaders)
+ if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_RUN
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMP ) {
+ aura->hModel = cgs.media.runauraModel;
+ } else if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_BACK
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMPB
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYB ) {
+ aura->hModel = cgs.media.backauraModel;
+ } else if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYA ) {
+ aura->hModel = cgs.media.flyauraModel;
+ } else {
+ aura->hModel = cgs.media.auraModel;
+ if ( reversed ) {
+ VectorNegate( cg.autoAngles, auraInverseRotation );
+ AnglesToAxis( auraInverseRotation, aura->axis );
+ } else {
+ AnglesToAxis( cg.autoAngles, aura->axis );
+ }
+ }
+}
+
+
+/*
+===============
+CG_DynamicAuraLight
+
+Dynamic aura light, note: when charging it changes the shinning a bit
+Aura lights like cg_smallOwnAura only can be shown to itself and not the other clients,
+the other clients only show small lights.
+===============
+*/
+static void CG_DynamicAuraLight( centity_t *cent, int clientNum, float r, float g, float b ) { // BFP - Dynamic aura light
+ int dLightSize = 200;
+ int rndDLight = dLightSize * 0.7845;
+ int firstRndDlight = dLightSize * 1.26;
+
+ // BFP - Monster gamemode, player monster dynamic light size
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ dLightSize = 1000;
+ }
+ // BFP - NOTE: Originally, if cg_spriteAura or cg_particleAura is on, the lights aren't displayed.
+ // But in that case, that can displayed, so it makes no sense not being displayed and
+ // maybe these things were broken on original BFP.
+ if ( cg_lightAuras.integer > 0 ) {
+ if ( clientNum == cg.snap->ps.clientNum && cg_smallOwnAura.integer > 0 ) {
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize, r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize, r, g, b );
+ if ( !( cg.predictedPlayerState.pm_flags & PMF_KI_CHARGE ) ) {
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&rndDLight), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&rndDLight), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&rndDLight), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&rndDLight), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&rndDLight), r, g, b );
+ } else {
+ dLightSize = 100;
+ // BFP - Monster gamemode, player monster dynamic light size
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ dLightSize = 500;
+ }
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&dLightSize), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&dLightSize), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&dLightSize), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&dLightSize), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&dLightSize), r, g, b );
+ }
+ } else if ( clientNum != cg.snap->ps.clientNum || cg_lightweightAuras.integer > 0 || cg_polygonAura.integer > 0 || cg_highPolyAura.integer > 0 ) {
+ dLightSize = 50;
+ // BFP - Monster gamemode, player monster dynamic light size
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ dLightSize = 250;
+ }
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&(dLightSize * 2)), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&(dLightSize * 2)), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&(dLightSize * 2)), r, g, b );
+ } else {
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&firstRndDlight), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&rndDLight), r, g, b );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&rndDLight), r, g, b );
+ if ( !( cg.predictedPlayerState.pm_flags & PMF_KI_CHARGE ) ) {
+ dLightSize = 100;
+ // BFP - Monster gamemode, player monster dynamic light size
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ dLightSize = 500;
+ }
+ firstRndDlight = dLightSize * 1.5;
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&firstRndDlight), r, g, b );
+ }
+ }
+ }
+}
+
+
+/*
+===============
+CG_RemoveKiTrails
+
+Handle aura animations, when idling it sets the aura vertical rotation, so the aura rotates vertically
+===============
+*/
+static void CG_RemoveKiTrails( centity_t *cent, int clientNum, vec3_t kiTrailOrigin, qhandle_t kiTrailShader, qboolean fastRemove ) { // BFP - Remove ki trails
+ if ( cg.time > cent->pe.kiTrailTime ) { // reset ki trail position avoid being zeroed
+ CG_ResetTrail( KI_TRAIL, clientNum, kiTrailOrigin );
+ } else { // ki trails keep running in that moment, but their segments are being removed
+ CG_KiTrail( clientNum, kiTrailOrigin, fastRemove, kiTrailShader );
+ }
+}
+
/*
===============
CG_AddRefEntityWithPowerups
@@ -1998,45 +2519,316 @@ Adds a piece with modifications or duplications for powerups
Also called by CG_Missile for quad rockets, but nobody can tell...
===============
*/
-void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ) {
+void CG_AddRefEntityWithPowerups( refEntity_t ent, entityState_t *state, int team ) {
+ // BFP - Powerlevel
+ int powerlevel = state->frame;
+
+ if ( state->clientNum == cg.snap->ps.clientNum ) { // fixes a weird bug when trying to see the powerlevel of itself
+ powerlevel = cg.snap->ps.persistant[PERS_POWERLEVEL];
+ }
+ // BFP - No invis powerup
+#if 0
if ( state->powerups & ( 1 << PW_INVIS ) ) {
- ent->customShader = cgs.media.invisShader;
- trap_R_AddRefEntityToScene( ent );
- } else {
- /*
- if ( state->eFlags & EF_KAMIKAZE ) {
- if (team == TEAM_BLUE)
- ent->customShader = cgs.media.blueKamikazeShader;
- else
- ent->customShader = cgs.media.redKamikazeShader;
- trap_R_AddRefEntityToScene( ent );
+ ent.customShader = cgs.media.invisShader;
+ trap_R_AddRefEntityToScene( &ent );
+ return;
+ }
+#endif
+
+ // render main model
+ trap_R_AddRefEntityToScene( &ent );
+
+ // BFP - Render ultimate perma-glow when already transformed
+ if ( cg_permaglowUltimate.integer > 0
+ && powerlevel >= 1000 ) {
+ ent.customShader = cgs.media.ultimateAuraShader;
+ if ( ent.customShader ) {
+ trap_R_AddRefEntityToScene( &ent );
+ }
+ }
+ ent.customShader = CG_AuraPowerlevelSetShaderColor( state );
+
+ if ( ( state->eFlags & EF_AURA ) || ( state->eFlags & EF_AURA_TIER_UP ) ) {
+ // BFP - Transformation aura
+ if ( ( state->eFlags & EF_AURA_TIER_UP ) && cg_transformationAura.integer <= 0 ) {
+ return;
+ }
+ // BFP - If the player is using lightweight auras or their own small aura
+ if ( ( cg_lightweightAuras.integer > 0
+ || ( state->clientNum == cg.snap->ps.clientNum
+ && cg_smallOwnAura.integer > 0 ) )
+ && cg_spriteAura.integer <= 0
+ && cg_particleAura.integer <= 0 ) {
+ trap_R_AddRefEntityToScene( &ent );
+ }
+
+ // BFP - Resize shader aura when tier is up
+ if ( state->eFlags & EF_AURA_TIER_UP ) {
+ CG_ModelSize( &ent, 1.4f );
+ }
+
+ // BFP - Shader aura
+ if ( cg_lightweightAuras.integer <= 0
+ && cg_polygonAura.integer <= 0
+ && cg_spriteAura.integer <= 0
+ && cg_particleAura.integer <= 0 ) {
+ if ( state->clientNum == cg.snap->ps.clientNum
+ && cg_smallOwnAura.integer > 0 ) {
+ trap_R_AddRefEntityToScene( &ent );
+ return;
+ }
+ ent.customShader = CG_AuraPowerlevelSetShaderColor( state );
+ trap_R_AddRefEntityToScene( &ent );
+ }
+ }
+}
+
+/*
+============
+CG_SpriteAura
+
+Adds sprite aura, just one quad
+============
+*/
+static void CG_SpriteAura( refEntity_t aura ) { // BFP - Sprite aura
+ // BFP - NOTE: What shader was added?? Originally, BFP didn't finish the shader to attach or they forgot...
+ // That radius looks a bit big for an aura, maybe they thought to fit the texture that way or some circular aura?
+ // And... What the heck? This sprite view depends of pitch angle until some client connects?
+ // Also when cg_smallOwnAura cvar is enabled, it doesn't display any aura to the client itself.
+ // Moreover, the lights are disabled as mentioned previously in CG_DynamicAuraLight function comments
+ // In the future, the shader should be added, not sure what kind of aura is this...
+ float pitchView = cg.refdefViewAngles[PITCH];
+ int i, connectedClients = 1;
+
+ for ( i = 0; i < MAX_CLIENTS; ++i ) {
+ if ( cg_entities[i].currentValid ) {
+ ++connectedClients;
+ }
+ }
+ aura.reType = RT_SPRITE;
+ aura.customShader = 0;
+ aura.radius += 75;
+ if ( connectedClients > 1 ) {
+ pitchView = -15;
+ }
+ aura.rotation = pitchView;
+
+ aura.shaderRGBA[0] = 255;
+ aura.shaderRGBA[1] = 255;
+ aura.shaderRGBA[2] = 255;
+ aura.shaderRGBA[3] = 255;
+ trap_R_AddRefEntityToScene( &aura );
+}
+
+
+/*
+============
+CG_Aura
+
+Adds aura and ki trails
+============
+*/
+static void CG_Aura( centity_t *cent, int clientNum, clientInfo_t *ci, int renderfx, refEntity_t legs, qhandle_t kiTrailShader ) { // BFP - Aura and ki trails
+ refEntity_t aura;
+ refEntity_t aura2; // secondary aura
+ vec3_t auraInverseRotation; // for aura inverse rotation
+ vec3_t kiTrailOrigin;
+ int powerlevel = cent->currentState.frame;
+ const int KI_TRAIL_ZPOS = 5;
+
+ if ( clientNum == cg.snap->ps.clientNum ) { // fixes a weird bug when trying to see the powerlevel of itself
+ powerlevel = cg.snap->ps.persistant[PERS_POWERLEVEL];
+ }
+
+ memset( &aura, 0, sizeof(aura) );
+ memset( &aura2, 0, sizeof(aura2) );
+
+ // origin setup for ki trails
+ VectorCopy( cent->lerpOrigin, kiTrailOrigin );
+ kiTrailOrigin[2] += KI_TRAIL_ZPOS;
+
+ if ( ( cent->currentState.eFlags & EF_AURA ) || ( cent->currentState.eFlags & EF_AURA_TIER_UP ) ) {
+ // trace for bubble particles only when moving in the water and charging
+ int destContentType = CG_PointContents( legs.origin, -1 );
+
+ // BFP - Ki trail
+ if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_CHARGE
+ && cg_kiTrail.integer > 0 ) {
+ // apply time for using ki trail
+ cent->pe.kiTrailTime = cg.time + cg_kiTrail.integer*7;
+
+ CG_KiTrail( clientNum, kiTrailOrigin, qfalse, kiTrailShader );
+ } else { // handle when the ki trail was being used previously
+ CG_RemoveKiTrails( cent, clientNum, kiTrailOrigin, kiTrailShader, qtrue );
+ }
+
+ // spawning bubble particles
+ if ( destContentType & CONTENTS_WATER ) {
+ trace_t trace;
+ vec3_t start, bubbleOrigin;
+ float bubbleSize = 2;
+ float bubbleRange = 10;
+
+ VectorCopy( legs.origin, bubbleOrigin );
+ trap_CM_BoxTrace( &trace, start, bubbleOrigin, NULL, NULL, 0, CONTENTS_WATER );
+
+ bubbleOrigin[2] += -17; // put the origin below the character's feet
+
+ // BFP - Monster gamemode, player monster bubble particle size, range and position
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ bubbleSize = 8;
+ bubbleRange = 100;
+ bubbleOrigin[2] += -85; // put the origin below the character's feet
+ trace.endpos[2] += 100;
+ }
+
+ if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYA
+ || ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_FLYB ) {
+ bubbleOrigin[2] += 6; // put the origin near the player origin point
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 700, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 700, bubbleRange, bubbleSize );
+ } else if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_CHARGE ) {
+ bubbleOrigin[2] += -3; // put the origin a little below
+ bubbleRange *= 2;
+
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 0, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 0, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 0, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 0, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 0, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 0, bubbleRange, bubbleSize );
+ CG_ParticleBubble( cent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, bubbleOrigin, trace.endpos, 0, bubbleRange, bubbleSize );
+ }
+ }
+
+ // apply the render type
+ aura.reType = aura2.reType = RT_MODEL;
+
+ // clear the axis to keep the position
+ AxisClear( aura.axis );
+ AxisClear( aura2.axis );
+
+ // if the player is moving like going forward and backwards, then use other aura model
+ CG_AuraAnims( cent, &aura, 0, auraInverseRotation );
+ CG_AuraAnims( cent, &aura2, 1, auraInverseRotation );
+
+ // resize the aura
+ CG_ModelSize( &aura, 1.3f );
+ CG_ModelSize( &aura2, 1.5f );
+
+ // set aura position to the player
+ VectorCopy( legs.origin, aura.origin );
+ VectorCopy( legs.lightingOrigin, aura.lightingOrigin );
+ VectorCopy( legs.origin, aura2.origin );
+ VectorCopy( legs.lightingOrigin, aura2.lightingOrigin );
+
+ // apply light blinking
+ aura.customShader = aura2.customShader = CG_AuraPowerlevelSetShaderColor( ¢->currentState );
+ // blue
+ if ( powerlevel < 100
+ || ( cgs.gametype >= GT_TEAM && ci->team == TEAM_BLUE ) ) {
+ CG_DynamicAuraLight( cent, clientNum, 0.2f, 0.2f, 1.0 );
+ }
+ // yellow
+ else if ( powerlevel >= 1000
+ && !( cgs.gametype >= GT_TEAM ) ) {
+ //CG_DynamicAuraLight( cent, clientNum, 1.0, 1.0, 0 );
+ CG_DynamicAuraLight( cent, clientNum, 1.0, 1.0, 0.2f );
+ }
+ // red
+ else {
+ CG_DynamicAuraLight( cent, clientNum, 1.0, 0.2f, 0.2f );
+ }
+
+ aura.renderfx = aura2.renderfx = renderfx;
+ VectorCopy( aura.origin, aura.oldorigin ); // don't positionally lerp at all
+ VectorCopy( aura2.origin, aura2.oldorigin ); // don't positionally lerp at all
+
+ // ki boost and ki charge sounds
+ if ( !( cent->currentState.eFlags & EF_AURA_TIER_UP ) ) {
+ if ( !( cent->currentState.eFlags & EF_KI_BOOST )
+ && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_CHARGE ) {
+ trap_S_AddLoopingSound( cent->currentState.clientNum, cent->lerpOrigin,
+ vec3_origin, cgs.media.kiChargeSound );
+ } else {
+ trap_S_AddLoopingSound( cent->currentState.clientNum, cent->lerpOrigin,
+ vec3_origin, cgs.media.kiUseSound );
+ }
+ }
+
+ // aura tier
+ if ( cent->currentState.eFlags & EF_AURA_TIER_UP ) {
+ // resize the aura
+ CG_ModelSize( &aura, 1.3f );
+ CG_ModelSize( &aura2, 1.5f );
+ }
+
+ // keep the aura pivot tagged in tag_torso
+ CG_PositionRotatedEntityOnTag( &aura, &legs, legs.hModel /*ci->legsModel*/, "tag_torso" );
+ CG_PositionRotatedEntityOnTag( &aura2, &legs, legs.hModel /*ci->legsModel*/, "tag_torso" );
+
+ // BFP - Sprite aura
+ if ( ( cg_spriteAura.integer > 0 && cg_smallOwnAura.integer <= 0 )
+ || ( cg_spriteAura.integer > 0 && cg_smallOwnAura.integer > 0 && clientNum != cg.snap->ps.clientNum ) ) {
+ // BFP - Monster gamemode, player monster sprite aura is bigger
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ aura.radius = 325;
+ }
+ CG_SpriteAura( aura );
+ return;
}
- else {*/
- trap_R_AddRefEntityToScene( ent );
- //}
- if ( state->powerups & ( 1 << PW_QUAD ) )
- {
- if (team == TEAM_RED)
- ent->customShader = cgs.media.redQuadShader;
- else
- ent->customShader = cgs.media.quadShader;
- trap_R_AddRefEntityToScene( ent );
+ // BFP - Particle aura
+ if ( ( cg_particleAura.integer > 0 && cg_smallOwnAura.integer <= 0 )
+ || ( cg_particleAura.integer > 0 && cg_smallOwnAura.integer > 0 && clientNum != cg.snap->ps.clientNum ) ) {
+ // BFP - NOTE: Particle aura wasn't fully implemented on original BFP.
+ // Originally, particle aura wasn't correctly placed on player's origin, it was zeroed and
+ // when the player moves up, the aura was moving to right, and when moves down, it was moving to left;
+ // moreover, spawns too many particles; also the shader uses bubble ones and the particle size is a bit big.
+ // It's unknown what they planned in their future.
+ // But this time, it's placed to player's origin like when being underwater, more fading is added,
+ // also it doesn't spawn too many particles.
+ // In the future, that should be tweaked, bubble shader doesn't seem to fit well.
+ vec3_t pAuraOrigin;
+ VectorCopy( legs.origin, pAuraOrigin );
+
+ pAuraOrigin[2] += -18; // put the origin a little below
+
+ CG_ParticleAura( cent, clientNum, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, pAuraOrigin, NULL, 20 );
+ CG_ParticleAura( cent, clientNum, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, pAuraOrigin, NULL, 20 );
+ return;
}
- if ( state->powerups & ( 1 << PW_REGEN ) ) {
- if ( ( ( cg.time / 100 ) % 10 ) == 1 ) {
- ent->customShader = cgs.media.regenShader;
- trap_R_AddRefEntityToScene( ent );
+
+ // BFP - Small own aura only can be shown to the one who enables it for themself, not everyone
+ if ( clientNum != cg.snap->ps.clientNum || cg_smallOwnAura.integer <= 0 ) {
+ // BFP - Transformation aura
+ if ( ( cent->currentState.eFlags & EF_AURA_TIER_UP ) && cg_transformationAura.integer <= 0 ) {
+ return;
+ }
+
+ // add aura
+ if ( cg_spriteAura.integer <= 0 && cg_particleAura.integer <= 0
+ && cg_polygonAura.integer > 0 && cg_lightweightAuras.integer <= 0 ) {
+ trap_R_AddRefEntityToScene( &aura );
+ }
+
+ // add secondary aura to make look cooler, a bit bigger than the other
+ if ( cg_spriteAura.integer <= 0 && cg_particleAura.integer <= 0
+ && cg_polygonAura.integer > 0 && cg_highPolyAura.integer > 0 && cg_lightweightAuras.integer <= 0 ) {
+ trap_R_AddRefEntityToScene( &aura2 );
}
}
- if ( state->powerups & ( 1 << PW_BATTLESUIT ) ) {
- ent->customShader = cgs.media.battleSuitShader;
- trap_R_AddRefEntityToScene( ent );
+ } else {
+ // BFP - Ki trail being removed
+ if ( cg_kiTrail.integer > 0 ) {
+ CG_RemoveKiTrails( cent, clientNum, kiTrailOrigin, kiTrailShader, qtrue );
}
}
}
+
/*
=================
CG_LightVerts
@@ -2055,10 +2847,7 @@ int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts )
for (i = 0; i < numVerts; i++) {
incoming = DotProduct (normal, lightDir);
if ( incoming <= 0 ) {
- verts[i].modulate[0] = ambientLight[0];
- verts[i].modulate[1] = ambientLight[1];
- verts[i].modulate[2] = ambientLight[2];
- verts[i].modulate[3] = 255;
+ Byte4Set( verts[i].modulate, ambientLight[0], ambientLight[1], ambientLight[2], 255 );
continue;
}
j = ( ambientLight[0] + incoming * directedLight[0] );
@@ -2084,6 +2873,36 @@ int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts )
return qtrue;
}
+
+/*
+===============
+CG_FindAttachMuzzleTag
+===============
+*/
+static void CG_FindAttachMuzzleTag( centity_t *cent, clientInfo_t *ci, refEntity_t *torso, refEntity_t *legs, refEntity_t *head, char *attackTagPart, char *attackTagName ) { // BFP - Finds the tag to attach the muzzle
+ orientation_t tagOrient;
+
+ // legs
+ if ( attackTagPart && attackTagPart[0] && !Q_stricmp( attackTagPart, "legs" )
+ && trap_R_LerpTag( &tagOrient, legs->hModel, legs->oldframe, legs->frame, 1.0 - legs->backlerp, attackTagName ) ) {
+ CG_AddPlayerWeapon( legs, NULL, cent, ci->team, attackTagName );
+ return;
+ }
+
+ // torso
+ if ( attackTagPart && attackTagPart[0] && !Q_stricmp( attackTagPart, "torso" )
+ && trap_R_LerpTag( &tagOrient, torso->hModel, torso->oldframe, torso->frame, 1.0 - torso->backlerp, attackTagName ) ) {
+ CG_AddPlayerWeapon( torso, NULL, cent, ci->team, attackTagName );
+ return;
+ }
+
+ // head
+ if ( attackTagPart && attackTagPart[0] && !Q_stricmp( attackTagPart, "head" )
+ && trap_R_LerpTag( &tagOrient, head->hModel, head->oldframe, head->frame, 1.0 - head->backlerp, attackTagName ) ) {
+ CG_AddPlayerWeapon( head, NULL, cent, ci->team, attackTagName );
+ }
+}
+
/*
===============
CG_Player
@@ -2094,15 +2913,25 @@ void CG_Player( centity_t *cent ) {
refEntity_t legs;
refEntity_t torso;
refEntity_t head;
- refEntity_t aura; // BFP - Aura
int clientNum;
int renderfx;
qboolean shadow;
float shadowPlane;
- // BFP - Aura vertical rotation variables
- float auraRotation;
- float sinAura, cosAura;
- float tmp0, tmp1;
+ qhandle_t kiTrailShader;
+ // BFP - Powerlevel for the aura
+ int powerlevel = -1;
+ // BFP - Save head for first person vis mode
+ refEntity_t savedHead;
+ // BFP - Monster gamemode with g_monster enabled, temporarily swap animations
+ animation_t backupAnimations[MAX_TOTALANIMATIONS];
+
+ // BFP - Weapon info to extract
+ weaponInfo_t *weap = &cg_weapons[ cent->currentState.weapon ];
+
+ // BFP - That macro makes all body being transformed during tier up.
+ // Enabled by default, originally on BFP only shows the head as transformation transition
+ // while the body is already transformed.
+#define TIER_UP_TRANSFORM_ENTIRE_BODY 1
// the client number is stored in clientNum. It can't be derived
// from the entity number, because a single client may have
@@ -2112,6 +2941,26 @@ void CG_Player( centity_t *cent ) {
CG_Error( "Bad clientNum on player entity");
}
ci = &cgs.clientinfo[ clientNum ];
+
+ // BFP - Powerlevel for the aura
+ powerlevel = cent->currentState.frame;
+ if ( clientNum == cg.snap->ps.clientNum ) { // fixes a weird bug when trying to see the powerlevel of itself
+ powerlevel = cg.snap->ps.persistant[PERS_POWERLEVEL];
+ }
+
+ // BFP - Ki trail shader set
+ // red
+ kiTrailShader = cgs.media.kiTrailRedShader;
+ // blue
+ if ( powerlevel < 100
+ || ( cgs.gametype >= GT_TEAM && ci->team == TEAM_BLUE ) ) {
+ kiTrailShader = cgs.media.kiTrailBlueShader;
+ }
+ // yellow
+ if ( powerlevel >= 1000
+ && !( cgs.gametype >= GT_TEAM ) ) {
+ kiTrailShader = cgs.media.kiTrailYellowShader;
+ }
// it is possible to see corpses from disconnected players that may
// not have valid clientinfo
@@ -2121,7 +2970,7 @@ void CG_Player( centity_t *cent ) {
// get the player model information
renderfx = 0;
- if ( cent->currentState.number == cg.snap->ps.clientNum) {
+ if ( clientNum == cg.snap->ps.clientNum ) {
if (!cg.renderingThirdPerson) {
renderfx = RF_THIRD_PERSON; // only draw in mirrors
} /*else { // BFP - cg_cameraMode cvar doesn't exist
@@ -2135,7 +2984,14 @@ void CG_Player( centity_t *cent ) {
memset( &legs, 0, sizeof(legs) );
memset( &torso, 0, sizeof(torso) );
memset( &head, 0, sizeof(head) );
- memset( &aura, 0, sizeof(aura) ); // BFP - Aura
+
+ // BFP - Monster gamemode with g_monster enabled, temporarily swap animations
+ if ( cgs.gametype == GT_MONSTER && cgs.monster > 0
+ && ( cent->currentState.eFlags & EF_MONSTER )
+ && cgs.media.monsterAnimations[0].numFrames != 0 ) {
+ memcpy( backupAnimations, ci->animations, sizeof(ci->animations) );
+ memcpy( ci->animations, cgs.media.monsterAnimations, sizeof(ci->animations) );
+ }
// get the rotation information
CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis );
@@ -2144,78 +3000,25 @@ void CG_Player( centity_t *cent ) {
CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp,
&torso.oldframe, &torso.frame, &torso.backlerp );
+ // BFP - Monster gamemode with g_monster enabled, temporarily swap animations
+ if ( cgs.gametype == GT_MONSTER && cgs.monster > 0
+ && ( cent->currentState.eFlags & EF_MONSTER )
+ && cgs.media.monsterAnimations[0].numFrames != 0 ) {
+ memcpy( ci->animations, backupAnimations, sizeof(ci->animations) );
+ }
+
// add the talk baloon or disconnect icon
CG_PlayerSprites( cent );
- // BFP - Aura ki using
- if ( cent->currentState.eFlags & EF_AURA ) {
- aura.reType = RT_MODEL;
- aura.customShader = cgs.media.auraEffectShader;
- aura.hModel = cgs.media.auraModel;
-
- // reset axis model postion
- AxisClear( aura.axis );
-
- // fixes rotation when player rotates down/up/right/left... even while flying
- // according legs position
- // cent->lerpAngles is according player base entity position
- AnglesToAxis( ¢->pe.legs.pitchAngle, aura.axis );
-
- // set aura position to the player
- VectorCopy( cent->lerpOrigin, aura.origin );
-
- // BFP - when flying, set correctly into these angles
- if ( cg.predictedPlayerState.pm_flags & PMF_FLYING ) {
- AnglesToAxis( cent->lerpAngles, aura.axis );
- }
-
- // BFP - TODO: Aura vertical rotation, aura rotates vertically,
- // but when player is flying, Z-axis isn't respecting player's position and angles
- /*
- auraRotation = cg.time * 0.15f * ( M_PI / 180.0f );
- sinAura = sin( auraRotation );
- cosAura = cos( auraRotation );
- tmp0 = aura.axis[0][0];
- tmp1 = aura.axis[0][1];
-
- aura.axis[0][0] = cosAura * tmp0 - sinAura * aura.axis[1][0];
- aura.axis[0][1] = cosAura * tmp1 - sinAura * aura.axis[1][1];
- aura.axis[1][0] = sinAura * tmp0 + cosAura * aura.axis[1][0];
- aura.axis[1][1] = sinAura * tmp1 + cosAura * aura.axis[1][1];
- */
-
- trap_R_AddRefEntityToScene( &aura );
-
- aura.renderfx = renderfx;
-
- // BFP - TODO: Add secondary aura and with little size
- // who knows why, BFP does that as well
- //VectorScale( aura.axis[0], 0.9, aura.axis[0] );
- //VectorScale( aura.axis[1], 0.9, aura.axis[1] );
- //VectorScale( aura.axis[2], 0.9, aura.axis[2] );
-
- // BFP - TODO: rotate secondary aura in the opposite direction
- //trap_R_AddRefEntityToScene( &aura ); // add secondary aura, but it doesn't respect the angles to the player model
- // or should do that so?
- // CG_AddRefEntityWithPowerups( &aura, ¢->currentState, 0 );
-
- // light blinking
- trap_R_AddLightToScene( aura.origin, 100 + (rand()&32), 1, 0.01f, 0.002f );
- trap_R_AddLightToScene( cent->lerpOrigin, 80 + (rand()&11), 1, 0.15f, 0.001f );
-
- // BFP - Ki boost and ki charge sounds
- if ( ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_CHARGE ) {
- trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin,
- vec3_origin, cgs.media.kiUseSound );
- } else if ( ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_CHARGE ) {
- trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin,
- vec3_origin, cgs.media.kiChargeSound );
- }
- }
-
// add the shadow
shadow = CG_PlayerShadow( cent, &shadowPlane );
+ // BFP - Handle the antigrav rock particles when the player is charging
+ CG_AntigravRockHandling( cent );
+
+ // BFP - Handle particle aura
+ CG_ParticleAuraHandling( cent );
+
// add a water splash if partially in and out of water
CG_PlayerSplash( cent );
@@ -2230,15 +3033,63 @@ void CG_Player( centity_t *cent ) {
legs.hModel = ci->legsModel;
legs.customSkin = ci->legsSkin;
+ // BFP - Ultimate tier legs model and skin
+ if ( powerlevel >= 1000
+#if TIER_UP_TRANSFORM_ENTIRE_BODY
+ && cg.time > cent->pe.ultTierTransformTime - 800
+#endif
+ ) {
+ if ( ci->ultTierLegsModel ) {
+ legs.hModel = ci->ultTierLegsModel;
+ }
+ if ( ci->ultTierLegsSkin ) {
+ legs.customSkin = ci->ultTierLegsSkin;
+ }
+ }
+
+ // BFP - Monster gamemode, set legs model and skin
+ if ( cgs.gametype == GT_MONSTER && cgs.monster > 0
+ && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ legs.hModel = cgs.media.monsterLegsModel;
+ legs.customSkin = cgs.media.monsterLegsSkin;
+ // BFP - Ultimate tier monster legs model and skin
+ if ( powerlevel >= 1000 && cg.time > cent->pe.ultTierTransformTime - 800 ) {
+ if ( cgs.media.monsterUltTierLegsModel ) {
+ legs.hModel = cgs.media.monsterUltTierLegsModel;
+ }
+ if ( cgs.media.monsterUltTierLegsSkin ) {
+ legs.customSkin = cgs.media.monsterUltTierLegsSkin;
+ }
+ }
+ }
+
+ // BFP - Super Deformed (Chibi style) easter egg for the base model (the legs apply all parts of the body)
+ if ( cg_superdeformed.integer > 0 ) {
+ CG_ModelSize( &legs, 0.8f );
+ }
+
VectorCopy( cent->lerpOrigin, legs.origin );
VectorCopy( cent->lerpOrigin, legs.lightingOrigin );
+ // BFP - Monster gamemode, the player monster is bigger than a normal player
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ // monster model position
+ if ( cgs.monster > 0 ) {
+ CG_ModelSize( &legs, 4.75 );
+ } else { // tweak a bit because of the hit box upside... huh... :/
+ CG_ModelSize( &legs, 7.9 );
+ }
+ // adjust the model a bit up to see the legs correctly
+ legs.origin[2] -= 22;
+ }
+
legs.shadowPlane = shadowPlane;
legs.renderfx = renderfx;
+ legs.nonNormalizedAxes = qtrue; // BFP - Keep legs visible while being near to the camera
VectorCopy (legs.origin, legs.oldorigin); // don't positionally lerp at all
- CG_AddRefEntityWithPowerups( &legs, ¢->currentState, ci->team );
+ CG_AddRefEntityWithPowerups( legs, ¢->currentState, ci->team );
// if the model failed, allow the default nullmodel to be displayed
if (!legs.hModel) {
@@ -2249,97 +3100,187 @@ void CG_Player( centity_t *cent ) {
// add the torso
//
torso.hModel = ci->torsoModel;
- if (!torso.hModel) {
- return;
+ torso.customSkin = ci->torsoSkin;
+
+ // BFP - Ultimate tier torso model and skin
+ if ( powerlevel >= 1000
+#if TIER_UP_TRANSFORM_ENTIRE_BODY
+ && cg.time > cent->pe.ultTierTransformTime - 800
+#endif
+ ) {
+ if ( ci->ultTierTorsoModel ) {
+ torso.hModel = ci->ultTierTorsoModel;
+ }
+ if ( ci->ultTierTorsoSkin ) {
+ torso.customSkin = ci->ultTierTorsoSkin;
+ }
}
- torso.customSkin = ci->torsoSkin;
+ // BFP - Monster gamemode, set torso model and skin
+ if ( cgs.gametype == GT_MONSTER && cgs.monster > 0
+ && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ torso.hModel = cgs.media.monsterTorsoModel;
+ torso.customSkin = cgs.media.monsterTorsoSkin;
+ // BFP - Ultimate tier monster torso model and skin
+ if ( powerlevel >= 1000 && cg.time > cent->pe.ultTierTransformTime - 800 ) {
+ if ( cgs.media.monsterUltTierTorsoModel ) {
+ torso.hModel = cgs.media.monsterUltTierTorsoModel;
+ }
+ if ( cgs.media.monsterUltTierTorsoSkin ) {
+ torso.customSkin = cgs.media.monsterUltTierTorsoSkin;
+ }
+ }
+ }
+
+ if ( !torso.hModel ) {
+ return;
+ }
VectorCopy( cent->lerpOrigin, torso.lightingOrigin );
- CG_PositionRotatedEntityOnTag( &torso, &legs, ci->legsModel, "tag_torso");
+ CG_PositionRotatedEntityOnTag( &torso, &legs, legs.hModel /*ci->legsModel*/, "tag_torso" );
torso.shadowPlane = shadowPlane;
torso.renderfx = renderfx;
- CG_AddRefEntityWithPowerups( &torso, ¢->currentState, ci->team );
+ CG_AddRefEntityWithPowerups( torso, ¢->currentState, ci->team );
//
// add the head
//
head.hModel = ci->headModel;
+ head.customSkin = ci->headSkin;
+
+ // BFP - Ultimate tier head model and skin
+ if ( powerlevel >= 1000 && cg.time > cent->pe.ultTierTransformTime - 800 ) {
+ if ( ci->ultTierHeadModel ) {
+ head.hModel = ci->ultTierHeadModel;
+ }
+ if ( ci->ultTierHeadSkin ) {
+ head.customSkin = ci->ultTierHeadSkin;
+ }
+ }
+
+ // BFP - Monster gamemode, set head model and skin
+ if ( cgs.gametype == GT_MONSTER && cgs.monster > 0
+ && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ head.hModel = cgs.media.monsterHeadModel;
+ head.customSkin = cgs.media.monsterHeadSkin;
+ // BFP - Ultimate tier monster head model and skin
+ if ( powerlevel >= 1000 && cg.time > cent->pe.ultTierTransformTime - 800 ) {
+ if ( cgs.media.monsterUltTierHeadModel ) {
+ head.hModel = cgs.media.monsterUltTierHeadModel;
+ }
+ if ( cgs.media.monsterUltTierHeadSkin ) {
+ head.customSkin = cgs.media.monsterUltTierHeadSkin;
+ }
+ }
+ }
+
if ( !head.hModel ) {
return;
}
+
+ // BFP - Make a model changing effect when aura tier is up
+ if ( ( cent->currentState.eFlags & EF_AURA_TIER_UP )
+ && cg.time > cent->pe.ultTierTransformTime ) {
+ cent->pe.ultTierTransformTime = cg.time + 1100;
+ }
+ if ( !( cent->currentState.eFlags & EF_AURA_TIER_UP ) ) {
+ cent->pe.ultTierTransformTime = 0;
+ }
+
if ( cg_yrgolroxor.integer > 0 ) { // BFP - Yrgol Roxor easter egg
head.hModel = 0; // 0: no head model display, display pivot only
}
- // BFP - First person vis mode doesn't have head model to be displayed
- if ( cg_drawOwnModel.integer >= 1 && cg_thirdPerson.integer <= 0
- && cent->currentState.number == cg.snap->ps.clientNum
- && !( cent->currentState.eFlags & EF_DEAD ) ) {
- head.hModel = 2; // 2: no head model display and no pivot display
+ // BFP - Super Deformed (Chibi style) easter egg for the head model
+ if ( cg_superdeformed.integer > 0 ) {
+ CG_ModelSize( &head, 3.0f );
}
- head.customSkin = ci->headSkin;
VectorCopy( cent->lerpOrigin, head.lightingOrigin );
- CG_PositionRotatedEntityOnTag( &head, &torso, ci->torsoModel, "tag_head");
+ CG_PositionRotatedEntityOnTag( &head, &torso, torso.hModel /*ci->torsoModel*/, "tag_head" );
head.shadowPlane = shadowPlane;
head.renderfx = renderfx;
- CG_AddRefEntityWithPowerups( &head, ¢->currentState, ci->team );
+ // BFP - First person vis mode doesn't have head model to be displayed
+ savedHead = head;
+ if ( cg_drawOwnModel.integer >= 1 && cg_thirdPerson.integer <= 0
+ && clientNum == cg.snap->ps.clientNum
+ && !( cent->currentState.eFlags & EF_DEAD ) ) {
+ memset( &head, 0, sizeof(head) );
+ }
+
+ CG_AddRefEntityWithPowerups( head, ¢->currentState, ci->team );
+
+ // BFP - If the entity is a corpse, avoid drawing ki trails to the dead
+ if ( cent->currentState.eFlags & EF_DEAD ) {
+ return;
+ }
//
- // add the gun / barrel / flash
+ // BFP - Aura and ki trails
//
- CG_AddPlayerWeapon( &torso, NULL, cent, ci->team );
+ CG_Aura( cent, clientNum, ci, renderfx, legs, kiTrailShader );
- // add powerups floating behind the player
- CG_PlayerPowerups( cent, &torso );
-
- // BFP - Smoke trail when using Ki boost in the ground
- if ( cent->currentState.eFlags & EF_AURA ) {
- CG_HasteTrail( cent );
+ // BFP - Render ultimate perma-glow dynamic lights when already transformed
+ if ( cg_lightAuras.integer > 0 && cg_permaglowUltimate.integer > 0 && powerlevel >= 1000 ) {
+ int dLightSize = 50;
+ // BFP - Monster gamemode, player monster' dynamic lights are bigger than a normal one
+ if ( cent->currentState.eFlags & EF_MONSTER ) {
+ dLightSize = 270;
+ }
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&( dLightSize + 30 )), 1.0, 1.0, 0.2f );
+ trap_R_AddLightToScene( cent->lerpOrigin, dLightSize + (rand()&( dLightSize + 30 )), 1.0, 1.0, 0.2f );
}
+
// BFP - First person camera setup
if ( cg_thirdPerson.integer <= 0
- && cent->currentState.number == cg.snap->ps.clientNum ) { // BFP - Avoid every time some player/bot enters in the server and changes the view into the other player
- CG_OffsetFirstPersonView( cent, &torso, ci->torsoModel );
+ && clientNum == cg.snap->ps.clientNum ) { // BFP - Avoid every time some player/bot enters in the server and changes the view into the other player
+ static vec3_t deadOriginDrawOwnModel;
+
+ if ( !( cent->currentState.eFlags & EF_DEAD ) ) {
+ // BFP - Set dead origin where the player was alive when First person vis mode is being used
+ VectorCopy( cg.refdef.vieworg, deadOriginDrawOwnModel );
+ CG_PositionRotatedEntityOnTag( &savedHead, &savedHead, savedHead.hModel /*ci->headModel*/, "tag_eyes" );
+ CG_OffsetFirstPersonView( cent, &savedHead, savedHead.hModel /*ci->headModel*/ );
+ } else if ( cg.snap->ps.stats[STAT_HEALTH] <= 0
+ && ( cent->currentState.eFlags & EF_DEAD )
+ && cg_drawOwnModel.integer >= 1 ) { // BFP - Death camera only for First person vis
+ VectorCopy( deadOriginDrawOwnModel, cg.refdef.vieworg );
+ cg.refdefViewAngles[YAW] = cg.snap->ps.damageYaw + cg.snap->ps.damagePitch;
+ }
}
-}
-/*
-===============
-CG_GetTagOrientationFromPlayerEntityParentModel
-===============
-*/
-qboolean CG_GetTagOrientationFromPlayerEntityParentModel( centity_t *cent, refEntity_t *parent,
- qhandle_t parentModel, char *tagName, orientation_t *tagOrient ) { // BFP - Parent model tag orientation, used for first person vis mode
- int i;
- orientation_t lerped;
- vec3_t tempAxis[3];
+ //
+ // add the gun / barrel / flash
+ //
+ // CG_AddPlayerWeapon( &torso, NULL, cent, ci->team );
- if ( cent->currentState.eType != ET_PLAYER || !tagName[0] ) {
- return qfalse;
+ // BFP - NOTE: Here's where the player gets the muzzle attached from some of the tags (apply that to client cfg side) (tag_left, tag_right, tag_eyes, ...)
+
+ // BFP - Monster gamemode, for the monster feature option, attach the beam to the mouth, just testing
+ if ( cgs.gametype == GT_MONSTER && cgs.monster > 0 && ( cent->currentState.eFlags & EF_MONSTER ) ) {
+ CG_FindAttachMuzzleTag( cent, ci, &torso, &legs, &savedHead, "head", "tag_mouth" );
+ } else {
+ if ( cent->currentState.weapon == WP_BFG ) { // BFP - Testing disk muzzle
+ CG_FindAttachMuzzleTag( cent, ci, &torso, &legs, &savedHead, "torso", "tag_right" );
+ } else {
+ CG_FindAttachMuzzleTag( cent, ci, &torso, &legs, &savedHead, "torso", "tag_left" );
+ }
}
-
- // Prepare the destination orientation_t
- AxisClear( tagOrient->axis );
-
- // Try to find the tag and return its coordinates
- if ( trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ) ) {
- VectorCopy( parent->origin, tagOrient->origin );
- for ( i = 0 ; i < 3 ; i++ ) {
- VectorMA( tagOrient->origin, lerped.origin[i], parent->axis[i], tagOrient->origin );
- }
- MatrixMultiply( tagOrient->axis, lerped.axis, tempAxis );
- MatrixMultiply( tempAxis, parent->axis, tagOrient->axis );
- return qtrue;
- }
- return qfalse;
+
+ // BFP - Forcefield test
+ if ( cent->currentState.weapon == WP_SHOTGUN
+ && cent->pe.chargeAutoFire && weap->noExplosion ) {
+ CG_ForceFieldEffect( cent, torso.origin, "models/weaphits/sphere_hi.md3", "AGAAttackShader" );
+ }
+
+ // add powerups floating behind the player
+ CG_PlayerPowerups( cent, &torso );
}
//=====================================================================
diff --git a/source/cgame/cg_playerstate.c b/source/cgame/cg_playerstate.c
index 9eb34d2..b59fbda 100644
--- a/source/cgame/cg_playerstate.c
+++ b/source/cgame/cg_playerstate.c
@@ -37,9 +37,11 @@ If the ammo has gone low enough to generate the warning, play a sound
void CG_CheckAmmo( void ) {
int i;
int total;
- int previous;
int weapons;
+ // BFP - Unused variable
+ // int previous;
+
// BFP - TODO: Replace to ki
// see about how many seconds of ammo we have remaining
@@ -204,15 +206,17 @@ void CG_Respawn( void ) {
cg.weaponSelect = cg.snap->ps.weapon;
}
-extern char *eventnames[];
/*
==============
CG_CheckPlayerstateEvents
==============
*/
-void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {
- int i;
+extern int eventStack;
+extern int eventParm2[ MAX_PREDICTED_EVENTS ];
+
+static void CG_CheckPlayerstateEvents( const playerState_t *ps, const playerState_t *ops ) {
+ int i, n;
int event;
centity_t *cent;
@@ -220,10 +224,12 @@ void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {
cent = &cg_entities[ ps->clientNum ];
cent->currentState.event = ps->externalEvent;
cent->currentState.eventParm = ps->externalEventParm;
- CG_EntityEvent( cent, cent->lerpOrigin );
+ CG_EntityEvent( cent, cent->lerpOrigin, -1 );
}
cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ];
+ n = eventStack - MAX_PS_EVENTS;
+ if ( n < 0 ) n = 0;
// go through the predictable events buffer
for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) {
// if we have a new predictable event
@@ -233,9 +239,12 @@ void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {
|| (i > ops->eventSequence - MAX_PS_EVENTS && ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]) ) {
event = ps->events[ i & (MAX_PS_EVENTS-1) ];
+ if ( event == EV_NONE ) // ignore empty events
+ continue;
cent->currentState.event = event;
cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ];
- CG_EntityEvent( cent, cent->lerpOrigin );
+
+ CG_EntityEvent( cent, cent->lerpOrigin, eventParm2[ n++ ] );
cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event;
@@ -250,11 +259,13 @@ CG_CheckChangedPredictableEvents
==================
*/
void CG_CheckChangedPredictableEvents( playerState_t *ps ) {
- int i;
+ int i, n;
int event;
centity_t *cent;
cent = &cg.predictedPlayerEntity;
+ n = eventStack - MAX_PS_EVENTS;
+ if ( n < 0 ) n = 0;
for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) {
//
if (i >= cg.eventSequence) {
@@ -268,7 +279,7 @@ void CG_CheckChangedPredictableEvents( playerState_t *ps ) {
event = ps->events[ i & (MAX_PS_EVENTS-1) ];
cent->currentState.event = event;
cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ];
- CG_EntityEvent( cent, cent->lerpOrigin );
+ CG_EntityEvent( cent, cent->lerpOrigin, eventParm2[ n++ ] );
cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event;
@@ -300,7 +311,9 @@ CG_CheckLocalSounds
==================
*/
void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
- int highScore, health, armor, reward;
+ int highScore, reward;
+ // BFP - Unused in Q3? What?
+ // int health, armor;
sfxHandle_t sfx;
// don't play the sounds if the player just changed teams
@@ -311,9 +324,14 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
// hit changes
if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) {
cg.opponentHitBlendTime = cg.time + 0.5f; // BFP - For crosshair opponent hit effect
- armor = ps->persistant[PERS_ATTACKEE_ARMOR] & 0xff;
- health = ps->persistant[PERS_ATTACKEE_ARMOR] >> 8;
- trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND );
+ // BFP - Unused in Q3? What?
+ // armor = ps->persistant[PERS_ATTACKEE_ARMOR] & 0xff;
+ // health = ps->persistant[PERS_ATTACKEE_ARMOR] >> 8;
+
+ // BFP - Play hit sound
+ if ( cg_playHitSound.integer > 0 ) {
+ trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND );
+ }
} else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) {
trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND );
}
@@ -333,6 +351,14 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
// reward sounds
reward = qfalse;
+ if (ps->persistant[PERS_EXCELLENT_COUNT] != ops->persistant[PERS_EXCELLENT_COUNT]) {
+ sfx = cgs.media.excellentSound;
+ pushReward(sfx, cgs.media.medalExcellent, ps->persistant[PERS_EXCELLENT_COUNT]);
+ reward = qtrue;
+ //Com_Printf("excellent\n");
+ }
+ // BFP - No impressive, gauntlet, defend, assist and capture counters
+#if 0
if (ps->persistant[PERS_CAPTURES] != ops->persistant[PERS_CAPTURES]) {
pushReward(cgs.media.captureAwardSound, cgs.media.medalCapture, ps->persistant[PERS_CAPTURES]);
reward = qtrue;
@@ -344,12 +370,6 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
reward = qtrue;
//Com_Printf("impressive\n");
}
- if (ps->persistant[PERS_EXCELLENT_COUNT] != ops->persistant[PERS_EXCELLENT_COUNT]) {
- sfx = cgs.media.excellentSound;
- pushReward(sfx, cgs.media.medalExcellent, ps->persistant[PERS_EXCELLENT_COUNT]);
- reward = qtrue;
- //Com_Printf("excellent\n");
- }
if (ps->persistant[PERS_GAUNTLET_FRAG_COUNT] != ops->persistant[PERS_GAUNTLET_FRAG_COUNT]) {
sfx = cgs.media.humiliationSound;
pushReward(sfx, cgs.media.medalGauntlet, ps->persistant[PERS_GAUNTLET_FRAG_COUNT]);
@@ -366,8 +386,11 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
reward = qtrue;
//Com_Printf("assist\n");
}
+#endif
// if any of the player event bits changed
if (ps->persistant[PERS_PLAYEREVENTS] != ops->persistant[PERS_PLAYEREVENTS]) {
+ // BFP - No denied and gauntlet's humiliation sounds
+#if 0
if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD) !=
(ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD)) {
trap_S_StartLocalSound( cgs.media.deniedSound, CHAN_ANNOUNCER );
@@ -376,7 +399,9 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
(ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD)) {
trap_S_StartLocalSound( cgs.media.humiliationSound, CHAN_ANNOUNCER );
}
- else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT) !=
+ else
+#endif
+ if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT) !=
(ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT)) {
trap_S_StartLocalSound( cgs.media.holyShitSound, CHAN_ANNOUNCER );
}
@@ -386,8 +411,7 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
// check for flag pickup
if ( cgs.gametype >= GT_TEAM ) {
if ((ps->powerups[PW_REDFLAG] != ops->powerups[PW_REDFLAG] && ps->powerups[PW_REDFLAG]) ||
- (ps->powerups[PW_BLUEFLAG] != ops->powerups[PW_BLUEFLAG] && ps->powerups[PW_BLUEFLAG]) ||
- (ps->powerups[PW_NEUTRALFLAG] != ops->powerups[PW_NEUTRALFLAG] && ps->powerups[PW_NEUTRALFLAG]) )
+ (ps->powerups[PW_BLUEFLAG] != ops->powerups[PW_BLUEFLAG] && ps->powerups[PW_BLUEFLAG]) )
{
trap_S_StartLocalSound( cgs.media.youHaveFlagSound, CHAN_ANNOUNCER );
}
diff --git a/source/cgame/cg_predict.c b/source/cgame/cg_predict.c
index 9e1e1cb..4533079 100644
--- a/source/cgame/cg_predict.c
+++ b/source/cgame/cg_predict.c
@@ -253,6 +253,140 @@ static void CG_InterpolatePlayerState( qboolean grabAngles ) {
}
+
+int eventStack;
+entity_event_t events[ MAX_PREDICTED_EVENTS ];
+int eventParms[ MAX_PREDICTED_EVENTS ];
+int eventParm2[ MAX_PREDICTED_EVENTS ]; // client entity index
+
+void CG_AddFallDamage( int damage );
+
+/*
+===================
+CG_StoreEvents
+
+Save events that may be dropped during prediction
+===================
+*/
+void CG_StoreEvent( entity_event_t evt, int eventParm, int entityNum )
+{
+ if ( eventStack >= MAX_PREDICTED_EVENTS )
+ return;
+
+// BFP - Remove that in the future >:(
+#if 0
+ if ( evt == EV_FALL_FAR ) {
+ CG_AddFallDamage( 10 );
+ } else if ( evt == EV_FALL_MEDIUM ) {
+ CG_AddFallDamage( 5 );
+ }
+#endif
+
+ events[ eventStack ] = evt;
+ eventParms[ eventStack ] = eventParm;
+ eventParm2[ eventStack ] = entityNum;
+ eventStack++;
+}
+
+
+/*
+===================
+CG_PlayDroppedEvents
+===================
+*/
+void CG_PlayDroppedEvents( playerState_t *ps, playerState_t *ops ) {
+ centity_t *cent;
+ entity_event_t oldEvent;
+ int i, oldParam;
+
+ if ( ps == ops ) {
+ return;
+ }
+
+ if ( eventStack <= MAX_PS_EVENTS ) {
+ return;
+ }
+
+ cent = &cg.predictedPlayerEntity;
+
+ oldEvent = cent->currentState.event;
+ oldParam = cent->currentState.eventParm;
+
+ for ( i = 0; i < eventStack - MAX_PS_EVENTS ; i++ ) {
+ cent->currentState.event = events[ i ];
+ cent->currentState.eventParm = eventParms[ i ];
+ if ( cg_showmiss.integer )
+ {
+ CG_Printf( "Playing dropped event: %s %i", eventnames[ events[ i ] ], eventParms[ i ] );
+ }
+ CG_EntityEvent( cent, cent->lerpOrigin, eventParm2[ i ] );
+ cg.eventSequence++;
+ }
+
+ cent->currentState.event = oldEvent;
+ cent->currentState.eventParm = oldParam;
+}
+
+// BFP - When powerlevel is lesser than 10, (notorious between 0 and 4),
+// the camera yaw view changes abruptly when the player is spawned at the first time falling far.
+// Remove that in the future
+// It doesn't look useful at all, it's a bug or an annoying glitch >:(
+#if 0
+static int CG_CheckArmor( int damage ) {
+ int save;
+ int count;
+
+ count = cg.predictedPlayerState.stats[STAT_ARMOR];
+
+ save = ceil( damage * ARMOR_PROTECTION );
+
+ if (save >= count)
+ save = count;
+
+ if ( !save )
+ return 0;
+
+ cg.predictedPlayerState.stats[STAT_ARMOR] -= save;
+
+ return save;
+}
+
+
+void CG_AddFallDamage( int damage )
+{
+ int take, asave;
+
+ // BFP - No battlesuit powerup
+#if 0
+ if ( cg.predictedPlayerState.powerups[ PW_BATTLESUIT ] )
+ return;
+#endif
+
+ if ( cg.predictedPlayerState.clientNum != cg.snap->ps.clientNum || cg.snap->ps.pm_flags & PMF_FOLLOW ) {
+ return;
+ }
+
+ take = damage;
+
+ asave = CG_CheckArmor( take );
+
+ take -= asave;
+
+ cg.predictedPlayerState.stats[STAT_HEALTH] -= take;
+
+#if 0
+ CG_Printf( "take: %i asave:%i health:%i armor:%i\n", take, asave,
+ cg.predictedPlayerState.stats[STAT_HEALTH], cg.predictedPlayerState.stats[STAT_ARMOR] );
+#endif
+
+ cg.predictedPlayerState.damagePitch = 255;
+ cg.predictedPlayerState.damageYaw = 255;
+ //cg.predictedPlayerState.damageEvent++;
+ cg.predictedPlayerState.damageCount = take + asave;
+}
+#endif
+
+
/*
===================
CG_TouchItem
@@ -291,7 +425,7 @@ static void CG_TouchItem( centity_t *cent ) {
}
// grab it
- BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState);
+ BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState, cent - cg_entities );
// remove it from the frame so it won't be drawn
cent->currentState.eFlags |= EF_NODRAW;
@@ -564,9 +698,30 @@ void CG_PredictPlayerState( void ) {
}
}
- // don't predict gauntlet firing, which is only supposed to happen
- // when it actually inflicts damage
- cg_pmove.gauntletHit = qfalse;
+ // BFP - don't predict melee attack, which is only supposed to happen
+ // when it actually inflicts damage (on Q3 comment says the same, but with gauntlet :P)
+ cg_pmove.meleeHit = qfalse;
+
+ // BFP - Melee only and no flight pmove handling
+ {
+ int gValue;
+ const char *info;
+
+ info = CG_ConfigString( CS_SERVERINFO );
+ // BFP - Melee only
+ cg_pmove.meleeOnly = qfalse;
+ gValue = atoi( Info_ValueForKey( info, "g_meleeOnly" ) );
+ if ( gValue > 0 ) {
+ cg_pmove.meleeOnly = qtrue;
+ }
+
+ // BFP - No flight
+ cg_pmove.noFlight = qfalse;
+ gValue = atoi( Info_ValueForKey( info, "g_noFlight" ) );
+ if ( gValue > 0 ) {
+ cg_pmove.noFlight = qtrue;
+ }
+ }
if ( cg_pmove.pmove_fixed ) {
cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
diff --git a/source/cgame/cg_scoreboard.c b/source/cgame/cg_scoreboard.c
index 00efc41..8eab883 100644
--- a/source/cgame/cg_scoreboard.c
+++ b/source/cgame/cg_scoreboard.c
@@ -95,14 +95,7 @@ static void CG_DrawClientScore( int y, score_t *score, float *color, float fade,
headx = SB_HEAD_X + (SB_RATING_WIDTH / 2);
// draw the handicap or bot skill marker (unless player has flag)
- if ( ci->powerups & ( 1 << PW_NEUTRALFLAG ) ) {
- if( largeFormat ) {
- CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_FREE, qfalse );
- }
- else {
- CG_DrawFlagModel( iconx, y, 16, 16, TEAM_FREE, qfalse );
- }
- } else if ( ci->powerups & ( 1 << PW_REDFLAG ) ) {
+ if ( ci->powerups & ( 1 << PW_REDFLAG ) ) {
if( largeFormat ) {
CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_RED, qfalse );
}
@@ -126,21 +119,29 @@ static void CG_DrawClientScore( int y, score_t *score, float *color, float fade,
CG_DrawPic( iconx, y, 16, 16, cgs.media.botSkillShaders[ ci->botSkill - 1 ] );
}
}
- } else if ( ci->handicap < 100 ) {
+ }
+ // BFP - No handicap
+#if 0
+ else if ( ci->handicap < 100 ) {
Com_sprintf( string, sizeof( string ), "%i", ci->handicap );
if ( cgs.gametype == GT_TOURNAMENT )
CG_DrawSmallStringColor( iconx, y - SMALLCHAR_HEIGHT/2, string, color );
else
CG_DrawSmallStringColor( iconx, y, string, color );
}
+#endif
// draw the wins / losses
if ( cgs.gametype == GT_TOURNAMENT ) {
Com_sprintf( string, sizeof( string ), "%i/%i", ci->wins, ci->losses );
+ // BFP - No handicap
+#if 0
if( ci->handicap < 100 && !ci->botSkill ) {
CG_DrawSmallStringColor( iconx, y + SMALLCHAR_HEIGHT/2, string, color );
}
- else {
+ else
+#endif
+ {
CG_DrawSmallStringColor( iconx, y, string, color );
}
}
@@ -161,9 +162,14 @@ static void CG_DrawClientScore( int y, score_t *score, float *color, float fade,
if ( score->ping == -1 ) {
Com_sprintf(string, sizeof(string),
" connecting %s", ci->name);
- } else if ( ci->team == TEAM_SPECTATOR ) {
+ } else if ( ci->team == TEAM_SPECTATOR
+ && cgs.gametype != GT_SURVIVAL ) { // BFP - Survival, don't draw SPECT word on score column
Com_sprintf(string, sizeof(string),
" SPECT %3i %4i %s", score->ping, score->time, ci->name);
+ } else if ( cgs.gametype == GT_MONSTER // BFP - Monster gamemode, mark the player who is monster
+ && ( cg_entities[score->client].currentState.eFlags & EF_MONSTER ) ) {
+ Com_sprintf(string, sizeof(string),
+ "MON%2i %3i %4i %s", score->score, score->ping, score->time, ci->name);
} else {
Com_sprintf(string, sizeof(string),
"%5i %4i %4i %s", score->score, score->ping, score->time, ci->name);
@@ -379,17 +385,37 @@ qboolean CG_DrawOldScoreboard( void ) {
y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
maxClients -= n2;
}
+
+ // BFP - Team Last Man Standing, a second entry drawn... to identify the gametype, maybe?
+ if ( cgs.gametype == GT_TLMS ) {
+ y += lineHeight/2;
+ n1 = CG_TeamScoreboard( y, TEAM_RED, fade, 0, lineHeight );
+ CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED );
+ y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
+ n2 = CG_TeamScoreboard( y, TEAM_BLUE, fade, 0, lineHeight );
+ CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE );
+ y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
+ }
n1 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients, lineHeight );
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
} else {
- //
- // free for all scoreboard
- //
- n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight );
- y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
- n2 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients - n1, lineHeight );
- y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
+ // BFP - Survival gametype, draws the blue line status for these who are in the match
+ if ( cgs.gametype == GT_SURVIVAL ) {
+ n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight );
+ CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE );
+ y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
+ n1 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients, lineHeight );
+ y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
+ } else {
+ //
+ // free for all scoreboard
+ //
+ n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight );
+ y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
+ n2 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients - n1, lineHeight );
+ y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
+ }
}
if (!localClient) {
diff --git a/source/cgame/cg_servercmds.c b/source/cgame/cg_servercmds.c
index e1f45a7..f86dcc3 100644
--- a/source/cgame/cg_servercmds.c
+++ b/source/cgame/cg_servercmds.c
@@ -71,6 +71,7 @@ static void CG_ParseScores( void ) {
}
}
+
/*
=================
CG_ParseTeamInfo
@@ -118,6 +119,10 @@ void CG_ParseServerinfo( void ) {
cgs.capturelimit = atoi( Info_ValueForKey( info, "capturelimit" ) );
cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) );
cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
+
+ // BFP - Monster gamemode, check if it can load the monster model
+ cgs.monster = atoi( Info_ValueForKey( info, "g_monster" ) );
+
mapname = Info_ValueForKey( info, "mapname" );
Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname );
Q_strncpyz( cgs.redTeam, Info_ValueForKey( info, "g_redTeam" ), sizeof(cgs.redTeam) );
@@ -462,7 +467,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in
}
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if ( !f ) {
+ if ( f == FS_INVALID_HANDLE ) {
trap_Print( va( S_COLOR_RED "voice chat file not found: %s\n", filename ) );
return qfalse;
}
@@ -485,7 +490,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in
voiceChats[i].id[0] = 0;
}
token = COM_ParseExt(p, qtrue);
- if (!token || token[0] == 0) {
+ if (token[0] == '\0') {
return qtrue;
}
if (!Q_stricmp(token, "female")) {
@@ -505,7 +510,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in
voiceChatList->numVoiceChats = 0;
while ( 1 ) {
token = COM_ParseExt(p, qtrue);
- if (!token || token[0] == 0) {
+ if (token[0] == '\0') {
return qtrue;
}
Com_sprintf(voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token);
@@ -517,7 +522,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in
voiceChats[voiceChatList->numVoiceChats].numSounds = 0;
while(1) {
token = COM_ParseExt(p, qtrue);
- if (!token || token[0] == 0) {
+ if (token[0] == '\0') {
return qtrue;
}
if (!Q_stricmp(token, "}"))
@@ -525,7 +530,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in
sound = trap_S_RegisterSound( token, compress );
voiceChats[voiceChatList->numVoiceChats].sounds[voiceChats[voiceChatList->numVoiceChats].numSounds] = sound;
token = COM_ParseExt(p, qtrue);
- if (!token || token[0] == 0) {
+ if (token[0] == '\0') {
return qtrue;
}
Com_sprintf(voiceChats[voiceChatList->numVoiceChats].chats[
@@ -575,7 +580,7 @@ int CG_HeadModelVoiceChats( char *filename ) {
char *token;
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if ( !f ) {
+ if ( f == FS_INVALID_HANDLE ) {
//trap_Print( va( "voice chat file not found: %s\n", filename ) );
return -1;
}
@@ -593,7 +598,7 @@ int CG_HeadModelVoiceChats( char *filename ) {
p = &ptr;
token = COM_ParseExt(p, qtrue);
- if (!token || token[0] == 0) {
+ if ( token[0] == '\0' ) {
return -1;
}
@@ -687,7 +692,7 @@ voiceChatList_t *CG_VoiceChatListForClient( int clientNum ) {
for (k = 0; k < 2; k++) {
// just pick the first with the right gender
for ( i = 0; i < MAX_VOICEFILES; i++ ) {
- if (strlen(voiceChatLists[i].name)) {
+ if (voiceChatLists[i].name[0] != '\0') {
if (voiceChatLists[i].gender == gender) {
// store this head model with voice chat for future reference
for ( j = 0; j < MAX_HEADMODELS; j++ ) {
@@ -879,7 +884,7 @@ static void CG_ServerCommand( void ) {
}
// loaddeferred can be both a servercmd and a consolecmd
- if ( !strcmp( cmd, "loaddefered" ) ) { // FIXME: spelled wrong, but not changing for demo
+ if ( !strcmp( cmd, "loaddefered" ) ) {
CG_LoadDeferredPlayers();
return;
}
diff --git a/source/cgame/cg_snapshot.c b/source/cgame/cg_snapshot.c
index f337e08..f066af1 100644
--- a/source/cgame/cg_snapshot.c
+++ b/source/cgame/cg_snapshot.c
@@ -186,6 +186,14 @@ static void CG_TransitionSnapshot( void ) {
}
}
+ // BFP - When the player is flying and looking vertically,
+ // adjust the mouse's yaw direction to prevent the player from
+ // looking in the incorrect horizontal direction
+ if ( cg.refdefViewAngles[PITCH] <= -100 || cg.refdefViewAngles[PITCH] >= 100 ) {
+ trap_SendConsoleCommand( "seta m_yaw -0.022\n" );
+ } else {
+ trap_SendConsoleCommand( "seta m_yaw 0.022\n" );
+ }
}
diff --git a/source/cgame/cg_syscalls.c b/source/cgame/cg_syscalls.c
index 77449a6..8d6779a 100644
--- a/source/cgame/cg_syscalls.c
+++ b/source/cgame/cg_syscalls.c
@@ -31,7 +31,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1;
-void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) {
+DLLEXPORT void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) {
syscall = syscallptr;
}
@@ -98,7 +98,7 @@ void trap_FS_FCloseFile( fileHandle_t f ) {
syscall( CG_FS_FCLOSEFILE, f );
}
-int trap_FS_Seek( fileHandle_t f, long offset, int origin ) {
+int trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin ) {
return syscall( CG_FS_SEEK, f, offset, origin );
}
diff --git a/source/cgame/cg_trails.c b/source/cgame/cg_trails.c
new file mode 100644
index 0000000..47f863a
--- /dev/null
+++ b/source/cgame/cg_trails.c
@@ -0,0 +1,560 @@
+/*
+===========================================================================
+
+BFP TRAILS
+
+===========================================================================
+*/
+
+
+#include "cg_local.h"
+
+#define TRAIL_SEGMENTS 99
+#define CORKSCREW_SEGMENTS 150
+#define MISSILE_TRAIL_SEGMENTS 120
+
+typedef struct {
+ vec3_t segments[MISSILE_TRAIL_SEGMENTS];
+ int segmentTime[MISSILE_TRAIL_SEGMENTS];
+ vec3_t color;
+ qboolean rainbow;
+ int rainbowStartTime;
+ qhandle_t shader;
+ int numSegments;
+} trail_t;
+
+static trail_t cg_trails[MAX_GENTITIES][3];
+static vec3_t spiralSegments[CORKSCREW_SEGMENTS];
+
+/*
+===============
+CG_InitTrails
+
+Initializes the array of trails for all centities.
+Should be called from CG_Init in cg_main.c
+===============
+*/
+void CG_InitTrails( void ) {
+ memset( &cg_trails, 0, sizeof(cg_trails) );
+}
+
+/*
+===============
+CG_ResetTrail
+
+Reset entity's trail.
+Should be called whenever an entity that has to use a trail, wasn't in the PVS the previous frame.
+TRAIL_TYPE: Trail type
+entityNum: Valid entity number
+origin: Point from where the trail should start.
+ (This should be equal to the entity's current position.)
+=====================
+*/
+void CG_ResetTrail( const int TRAIL_TYPE, int entityNum, vec3_t origin ) {
+ int i;
+
+ for ( i = 0; i < TRAIL_SEGMENTS; ++i ) {
+ VectorCopy( origin, cg_trails[entityNum][TRAIL_TYPE].segments[i] );
+ cg_trails[entityNum][TRAIL_TYPE].segmentTime[i] = 0;
+ }
+ cg_trails[entityNum][TRAIL_TYPE].numSegments = 0;
+}
+
+/*
+=====================
+CG_KiTrail
+
+Adds ki trail segments
+=====================
+*/
+void CG_KiTrail( int entityNum, vec3_t origin, qboolean remove, qhandle_t hShader ) {
+ int i;
+ int kiTrailLength = cg_kiTrail.integer;
+ trail_t *kiTrail = &cg_trails[entityNum][KI_TRAIL];
+ const int NORMAL_KI_TRAIL_WIDTH = 15;
+ const int MONSTER_KI_TRAIL_WIDTH = 150;
+ const int OVERLAP_TIMES = 3;
+ float kiTrailWidth = // for the player monster
+ ( cg_entities[entityNum].currentState.eFlags & EF_MONSTER )
+ ? MONSTER_KI_TRAIL_WIDTH
+ : NORMAL_KI_TRAIL_WIDTH;
+// BFP - A macro to enable/disable poly/refEntity_t rendering. If enabled, render polys, if disabled render the original BFP refEntity_t trails
+#define POLYVERT_KI_TRAILS 1
+
+ if ( entityNum < 0 || entityNum >= MAX_GENTITIES
+ || kiTrailLength <= OVERLAP_TIMES ) { // don't draw if ki trail length is less than the number of overlap times
+ return;
+ }
+
+ if ( kiTrailLength > TRAIL_SEGMENTS ) {
+ kiTrailLength = TRAIL_SEGMENTS;
+ }
+
+ if ( remove ) { // removes every segment
+ kiTrail->numSegments = ( kiTrail->numSegments > 0 )
+ ? kiTrail->numSegments - 1
+ : 0;
+ } else {
+ if ( kiTrail->numSegments < kiTrailLength ) {
+ ++kiTrail->numSegments;
+ }
+ }
+
+ // shift points down the buffer
+ for ( i = kiTrail->numSegments - 1; i > 0; --i ) {
+ VectorCopy( kiTrail->segments[i - 1], kiTrail->segments[i] );
+ }
+
+ // add the current position at the start
+ VectorCopy( origin, kiTrail->segments[0] );
+
+ for ( i = 0; i < kiTrail->numSegments - OVERLAP_TIMES; ++i ) {
+ vec3_t start, end;
+
+ VectorCopy( kiTrail->segments[i], start );
+ VectorCopy( kiTrail->segments[i + OVERLAP_TIMES], end );
+
+#if POLYVERT_KI_TRAILS
+ // render the polys
+ {
+ polyVert_t verts[4];
+ vec3_t forward, right;
+ vec3_t viewAxis;
+
+ VectorSubtract( end, start, forward );
+ VectorNormalize( forward );
+
+ VectorSubtract( cg.refdef.vieworg, start, viewAxis );
+ CrossProduct( viewAxis, forward, right );
+ VectorNormalize( right );
+
+ VectorMA( start, kiTrailWidth, right, verts[0].xyz );
+ Vector2Set( verts[0].st, 0, 0 );
+ Byte4Set( verts[0].modulate, 255, 255, 255, 255 );
+
+ VectorMA( end, kiTrailWidth, right, verts[1].xyz );
+ Vector2Set( verts[1].st, 1, 0 );
+ Byte4Set( verts[1].modulate, 255, 255, 255, 255 );
+
+ VectorMA( end, -kiTrailWidth, right, verts[2].xyz );
+ Vector2Set( verts[2].st, 1, 1 );
+ Byte4Set( verts[2].modulate, 255, 255, 255, 255 );
+
+ VectorMA( start, -kiTrailWidth, right, verts[3].xyz );
+ Vector2Set( verts[3].st, 0, 1 );
+ Byte4Set( verts[3].modulate, 255, 255, 255, 255 );
+
+ trap_R_AddPolyToScene( hShader, 4, verts );
+ }
+#else
+ { // I see... so, BFP originally used RT_RAIL_CORE, they didn't care the size, it was already set
+ refEntity_t trail;
+ memset( &trail, 0, sizeof( trail ) );
+ trail.reType = RT_RAIL_CORE;
+ trail.customShader = hShader;
+ VectorCopy( start, trail.origin );
+ VectorCopy( end, trail.oldorigin );
+ trail.shaderRGBA[0] = trail.shaderRGBA[1] = trail.shaderRGBA[2] = trail.shaderRGBA[3] = 255;
+
+ trap_R_AddRefEntityToScene( &trail );
+ }
+#endif
+ }
+}
+
+/*
+=====================
+CG_BeamTrail
+
+Adds beam trail segments
+=====================
+*/
+void CG_BeamTrail( int entityNum, vec3_t origin, vec3_t muzzleOrigin, qhandle_t hShader ) {
+ int i;
+ int nBeamSegments = cg_beamTrail.integer;
+ trail_t *beamTrail = &cg_trails[entityNum][BEAM_TRAIL];
+ const float BEAM_STRAIGHTEN_RATE = 0.2f; // how quickly segments straighten (0.0 - 1.0)
+
+ if ( entityNum < 0 || entityNum >= MAX_GENTITIES ) {
+ return;
+ }
+
+ // for better visual bendy effect, the number of segments should be equal or more than 10
+ if ( nBeamSegments < 10 ) {
+ nBeamSegments = 2;
+ }
+
+ if ( nBeamSegments > TRAIL_SEGMENTS ) {
+ nBeamSegments = TRAIL_SEGMENTS;
+ }
+
+ beamTrail->numSegments = nBeamSegments;
+
+ VectorCopy( muzzleOrigin, beamTrail->segments[0] );
+ VectorCopy( origin, beamTrail->segments[nBeamSegments - 1] );
+
+ // start stretching segments
+ if ( nBeamSegments >= 10 ) {
+ vec3_t currentAngles, beamDir;
+ float beamLength, lengthFactor;
+
+ // beam length and direction
+ VectorSubtract( origin, muzzleOrigin, beamDir );
+ beamLength = VectorLength( beamDir );
+ VectorNormalize( beamDir );
+ vectoangles( beamDir, currentAngles );
+
+ // length factor for straightening based on beam length
+ // longer beam = more straightening (less curve)
+ // reference length of 5000 units as baseline
+ lengthFactor = 1.0f + ( beamLength / 5000.0f );
+ if ( lengthFactor > 2.5f ) {
+ lengthFactor = 2.5f;
+ }
+
+ // shift all segments down by one position (creating trail effect)
+ for ( i = nBeamSegments - 1; i > 0; --i ) {
+ VectorCopy( beamTrail->segments[i - 1], beamTrail->segments[i] );
+ }
+
+ // update all segments with distance-based straightening
+ for ( i = 1; i < nBeamSegments; ++i ) {
+ vec3_t targetPos, segmentToOrigin;
+ float distanceToOrigin, segmentFraction;
+ float straightenFactor, maxExpectedDistance, stretchRatio = 1.0f;
+
+ // ideal position on straight line from muzzle to origin
+ segmentFraction = (float)i / (float)( nBeamSegments - 1 );
+ VectorMA( muzzleOrigin, segmentFraction * beamLength, beamDir, targetPos );
+
+ // how far this segment is from the impact point
+ VectorSubtract( origin, beamTrail->segments[i], segmentToOrigin );
+ distanceToOrigin = VectorLength( segmentToOrigin );
+
+ // expected distance if beam was straight
+ maxExpectedDistance = beamLength * ( 1.0f - segmentFraction );
+
+ // stretch ratio (how much segments are stretched)
+ if ( maxExpectedDistance > 0.1f ) {
+ stretchRatio = distanceToOrigin / maxExpectedDistance;
+ }
+
+ if ( stretchRatio > 1.0f ) {
+ // segments are stretching, straighten them out more aggressively
+ // length factor: longer beams straighten more
+ straightenFactor = BEAM_STRAIGHTEN_RATE * stretchRatio * lengthFactor;
+ if ( straightenFactor > 1.0f ) {
+ straightenFactor = 1.0f;
+ }
+ } else {
+ // not aiming and not stretched - gentle straightening
+ // length factor: longer beams straighten faster
+ straightenFactor = BEAM_STRAIGHTEN_RATE * lengthFactor;
+ if ( straightenFactor > 1.0f ) {
+ straightenFactor = 1.0f;
+ }
+ }
+
+ // transition toward target position
+ VectorScale( beamTrail->segments[i], 1.0f - straightenFactor, beamTrail->segments[i] );
+ VectorMA( beamTrail->segments[i], straightenFactor, targetPos, beamTrail->segments[i] );
+ }
+ }
+
+ // draw every beam segment
+ {
+ refEntity_t beam;
+ memset( &beam, 0, sizeof( beam ) );
+
+ beam.customShader = hShader;
+ beam.shaderRGBA[0] = beam.shaderRGBA[1] = beam.shaderRGBA[2] = beam.shaderRGBA[3] = 0xff;
+ beam.reType = RT_LIGHTNING;
+
+ if ( nBeamSegments == 2 ) {
+ VectorCopy( beamTrail->segments[0], beam.origin );
+ VectorCopy( beamTrail->segments[1], beam.oldorigin );
+ trap_R_AddRefEntityToScene( &beam );
+ return;
+ }
+
+ // BFP - NOTE: Skip the rendering of the 2 last segments.
+ // Weird. That's why the bendy beam can't visualize that segment correctly.
+ // On original BFP also happens
+ for ( i = 0; i < nBeamSegments - 2; ++i ) {
+ VectorCopy( beamTrail->segments[i], beam.origin );
+ if ( i > 0 ) {
+ VectorCopy( beamTrail->segments[i - 1], beam.origin );
+ }
+ VectorCopy( beamTrail->segments[i + 1], beam.oldorigin );
+ trap_R_AddRefEntityToScene( &beam );
+ beam.reType = RT_SPRITE;
+ trap_R_AddRefEntityToScene( &beam );
+ beam.reType = RT_LIGHTNING;
+ }
+ }
+}
+
+
+/*
+=====================
+CG_CorkscrewTrail
+
+Adds corkscrew trail segments
+=====================
+*/
+void CG_CorkscrewTrail( int entityNum, vec3_t origin, vec3_t muzzleOrigin, qhandle_t beamShader, qhandle_t corkscrewShader ) {
+ int i;
+ float length;
+ vec3_t fullDir, forward, right, up;
+ vec3_t temp = {0, 0, 1};
+ const float ROTATIONS = 5.0f;
+ const float MAX_RADIUS = 12.0f;
+ const int TAPER_SEGMENTS = 7;
+
+ if ( entityNum < 0 || entityNum >= MAX_GENTITIES ) {
+ return;
+ }
+
+ VectorCopy( origin, spiralSegments[CORKSCREW_SEGMENTS - 1] );
+ VectorCopy( muzzleOrigin, spiralSegments[0] );
+
+ // starting corkscrew segment spiral positions
+ VectorSubtract( origin, muzzleOrigin, fullDir );
+ length = VectorNormalize( fullDir );
+ VectorCopy( fullDir, forward );
+
+ // generate robust perpendicular vectors
+ CrossProduct( forward, temp, right );
+ VectorNormalize( right );
+ CrossProduct( forward, right, up );
+ VectorNormalize( up );
+
+ // generate spiral points around the beam
+ for ( i = 0; i < CORKSCREW_SEGMENTS; ++i ) {
+ float t = (float)i / (float)( CORKSCREW_SEGMENTS - 1 );
+ vec3_t basePoint, offset, radial;
+ float radius = MAX_RADIUS;
+ float angle = ROTATIONS * 2 * M_PI * t;
+
+ // point along the beam
+ VectorMA( muzzleOrigin, t * length, forward, basePoint );
+
+ // calculate spiral offset (orbit around beam)
+ VectorScale( right, cos( angle ), radial );
+ VectorMA( radial, sin( angle ), up, radial );
+ VectorNormalize( radial );
+
+ // taper radius near muzzle for the first few segments
+ if ( i < TAPER_SEGMENTS ) {
+ // linear taper: radius starts small and increases
+ radius = MAX_RADIUS * ( i / (float)TAPER_SEGMENTS );
+ }
+ // taper radius near origin for the last few segments
+ else if ( i > CORKSCREW_SEGMENTS - ( TAPER_SEGMENTS + 14 ) - 1 ) {
+ // linear taper: radius decreases to zero at the end
+ int segmentsFromEnd = CORKSCREW_SEGMENTS - 1 - i;
+ radius = MAX_RADIUS * ( segmentsFromEnd / (float)( TAPER_SEGMENTS + 14 ) );
+ }
+ VectorScale( radial, radius, offset );
+
+ // final spiral point
+ VectorAdd( basePoint, offset, spiralSegments[i] );
+ }
+
+ {
+ // draw beam segment
+ refEntity_t beam;
+ memset( &beam, 0, sizeof( beam ) );
+ beam.reType = RT_RAIL_CORE;
+ beam.customShader = beamShader;
+ beam.shaderRGBA[0] = beam.shaderRGBA[1] = beam.shaderRGBA[2] = beam.shaderRGBA[3] = 0xff;
+ VectorCopy( muzzleOrigin, beam.origin );
+ VectorCopy( origin, beam.oldorigin );
+
+ trap_R_AddRefEntityToScene( &beam );
+
+ // draw every corkscrew segment
+ beam.customShader = corkscrewShader;
+
+ for ( i = 0; i < CORKSCREW_SEGMENTS - 1; ++i ) {
+ VectorCopy( spiralSegments[i], beam.origin );
+ if ( i > 0 ) {
+ VectorCopy( spiralSegments[i - 1], beam.origin );
+ }
+ VectorCopy( spiralSegments[i + 1], beam.oldorigin );
+ trap_R_AddRefEntityToScene( &beam );
+ }
+ }
+}
+
+
+/*
+=====================
+HSVtoRGB
+=====================
+*/
+static float ModFloat( float a, float b ) {
+ return ( b <= 0.0f ) ? 0 : ( a - (int)( a / b ) * b );
+}
+static void HSVtoRGB( float h, float s, float v, float *r, float *g, float *b ) {
+ int i;
+ float f, p, q, t;
+ h = ModFloat( h, 1.0f );
+ if ( s <= 0.0f ) {
+ *r = *g = *b = v;
+ return;
+ }
+ h *= 6.0f;
+ i = floor( h );
+ f = h - i;
+ p = v * ( 1.0f - s );
+ q = v * ( 1.0f - s * f );
+ t = v * ( 1.0f - s * ( 1.0f - f ) );
+ switch ( i ) {
+ case 0: *r = v; *g = t; *b = p; break;
+ case 1: *r = q; *g = v; *b = p; break;
+ case 2: *r = p; *g = v; *b = t; break;
+ case 3: *r = p; *g = q; *b = v; break;
+ case 4: *r = t; *g = p; *b = v; break;
+ default:*r = v; *g = p; *b = q; break;
+ }
+}
+
+
+/*
+=====================
+CG_MissileTrail
+
+Just adds segments, doesn't draw
+=====================
+*/
+void CG_MissileTrail( int entityNum, vec3_t origin, qhandle_t hShader, vec3_t color, qboolean rainbow ) {
+ int i;
+ trail_t *missileTrail = &cg_trails[entityNum][MISSILE_TRAIL];
+
+ if ( entityNum < 0 || entityNum >= MAX_GENTITIES ) {
+ return;
+ }
+
+ if ( missileTrail->numSegments == 0 ) {
+ missileTrail->rainbowStartTime = cg.time;
+ }
+
+ if ( missileTrail->numSegments < MISSILE_TRAIL_SEGMENTS ) {
+ ++missileTrail->numSegments;
+ }
+
+ for ( i = missileTrail->numSegments - 1; i > 0; --i ) {
+ VectorCopy( missileTrail->segments[i-1], missileTrail->segments[i] );
+ missileTrail->segmentTime[i] = missileTrail->segmentTime[i-1];
+ }
+
+ VectorCopy( origin, missileTrail->segments[0] );
+ missileTrail->segmentTime[0] = cg.time;
+ missileTrail->shader = hShader;
+ VectorCopy( color, missileTrail->color );
+ missileTrail->rainbow = rainbow;
+}
+
+
+/*
+=====================
+CG_DrawMissileTrails
+
+Called once per frame.
+Draw all active missile segments of all entities
+=====================
+*/
+void CG_DrawMissileTrails( void ) {
+ int entityNum, i;
+ const int SEGMENT_LIFESPAN_MSEC = 900;
+ const float START_WIDTH = 15.0f;
+ const float END_WIDTH = 15.0f;
+
+ for ( entityNum = 0; entityNum < MAX_GENTITIES; ++entityNum ) {
+ trail_t *trail = &cg_trails[entityNum][MISSILE_TRAIL];
+
+ for ( i = 0; i < trail->numSegments - 1; ++i ) {
+ vec3_t start, end;
+ float alpha, width;
+ byte alphaByte;
+ int age = cg.time - trail->segmentTime[i];
+ int ageNext = cg.time - trail->segmentTime[i + 1];
+
+ if ( age >= SEGMENT_LIFESPAN_MSEC ) {
+ continue;
+ }
+
+ // the old end of the quad has expired — don't draw,
+ // but there might be new segments later in the buffer,
+ // so we use continue instead of break
+ if ( ageNext >= SEGMENT_LIFESPAN_MSEC ) {
+ continue;
+ }
+
+ // temporal discontinuity: the two ends belong to different firings.
+ // a gap greater than ~150ms between consecutive segments means that the
+ // buffer was refilled by a new missile on top of the remains of the previous one
+ if ( trail->segmentTime[i] - trail->segmentTime[i + 1] > 150 ) {
+ continue;
+ }
+
+ VectorCopy( trail->segments[i], start );
+ VectorCopy( trail->segments[i + 1], end );
+
+ // calculate opacity and width according to age
+ alpha = 1.0f - (float)age / SEGMENT_LIFESPAN_MSEC;
+ if ( alpha < 0.0f ) alpha = 0.0f;
+ alphaByte = (byte)(alpha * 255.0f);
+ width = START_WIDTH * ( 1.0f - (float)age / SEGMENT_LIFESPAN_MSEC )
+ + END_WIDTH * ( (float)age / SEGMENT_LIFESPAN_MSEC );
+
+ // render the polys
+ {
+ vec3_t forward, right, viewAxis;
+ polyVert_t verts[4];
+ byte r, g, b;
+
+ r = (byte)(trail->color[0] * alphaByte);
+ g = (byte)(trail->color[1] * alphaByte);
+ b = (byte)(trail->color[2] * alphaByte);
+
+ // rainbow effect
+ if ( trail->rainbow ) {
+ float rf, gf, bf;
+ float elapsed = ( cg.time - trail->rainbowStartTime ) * 0.0006f;
+ float hue = (float)age / SEGMENT_LIFESPAN_MSEC;
+ hue = ModFloat( elapsed, 1.0f );
+ HSVtoRGB( hue, 1.0f, 1.0f, &rf, &gf, &bf );
+ r = (byte)(rf * alphaByte);
+ g = (byte)(gf * alphaByte);
+ b = (byte)(bf * alphaByte);
+ }
+
+ VectorSubtract( end, start, forward );
+ VectorNormalize( forward );
+ VectorSubtract( cg.refdef.vieworg, start, viewAxis );
+ CrossProduct( viewAxis, forward, right );
+ VectorNormalize( right );
+
+ VectorMA( start, width, right, verts[0].xyz );
+ Vector2Set( verts[0].st, 0, 0 );
+ Byte4Set( verts[0].modulate, r, g, b, alphaByte );
+
+ VectorMA( end, width, right, verts[1].xyz );
+ Vector2Set( verts[1].st, 1, 0 );
+ Byte4Set( verts[1].modulate, r, g, b, alphaByte );
+
+ VectorMA( end, -width, right, verts[2].xyz );
+ Vector2Set( verts[2].st, 1, 1 );
+ Byte4Set( verts[2].modulate, r, g, b, alphaByte );
+
+ VectorMA( start, -width, right, verts[3].xyz );
+ Vector2Set( verts[3].st, 0, 1 );
+ Byte4Set( verts[3].modulate, r, g, b, alphaByte );
+
+ trap_R_AddPolyToScene( trail->shader, 4, verts );
+ }
+ }
+ }
+}
diff --git a/source/cgame/cg_view.c b/source/cgame/cg_view.c
index 67cb3f5..e0e5b2c 100644
--- a/source/cgame/cg_view.c
+++ b/source/cgame/cg_view.c
@@ -225,18 +225,10 @@ static void CG_OffsetThirdPersonView( void ) {
trace_t trace;
static vec3_t mins = { -4, -4, -4 };
static vec3_t maxs = { 4, 4, 4 };
- // vec3_t focusPoint; // BFP - unused
- // float focusDist; // BFP - unused
float forwardScale, sideScale;
// BFP - Camera setup variables
vec3_t overrideOrg;
float camAngle, camHeight, camRange;
- // BFP - Fly tilt
- int cmdNum;
- usercmd_t cmd;
- // BFP - Last angled for fly tilt angle to move smoothly similar to BFP vanilla
- static float lastAngled = 0.0f, lastRightAngled = 0.0f, lastUpAngled = 0.0f;
- float rightAngled, upAngled;
// BFP - Camera setup
camAngle = cg_thirdPersonAngle.value;
@@ -247,35 +239,36 @@ static void CG_OffsetThirdPersonView( void ) {
camHeight = -60.0f;
camRange = 110.0f;
}
- VectorCopy( cg.refdef.vieworg, overrideOrg );
- // cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight; // BFP - BFP camera position doesn't use that to move to player's height
+ // BFP - Monster gamemode, force player monster third person camera views
+ if ( cg.predictedPlayerState.eFlags & EF_MONSTER ) {
+// BFP - Macro to enable/disable original player monster camera view position
+#define BFP_MONSTER_CAMERA_VIEWPOS 1
+#if BFP_MONSTER_CAMERA_VIEWPOS
+ // BFP original camera view position
+ camHeight = -385.0f;
+ camRange = 500.0f;
+#else
+ // far camera view position similar to BFP fixed third person camera
+ camHeight = -530.0f;
+ camRange = 900.0f;
+#endif
+ }
+ VectorCopy( cg.refdef.vieworg, overrideOrg );
VectorCopy( cg.refdefViewAngles, focusAngles );
// if dead, look at killer
if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
- focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
- cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
+ int totalYaw = cg.predictedPlayerState.damageYaw + cg.predictedPlayerState.damagePitch;
+ focusAngles[YAW] = totalYaw;
+ cg.refdefViewAngles[YAW] = totalYaw;
}
-// BFP - unused check
-#if 0
- if ( focusAngles[PITCH] > 45 ) {
- focusAngles[PITCH] = 45; // don't go too far overhead
- }
-#endif
-
AngleVectors( focusAngles, forward, NULL, NULL );
- // VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint ); // BFP - unused
-
VectorCopy( cg.refdef.vieworg, view );
- // view[2] += 8; // // BFP - unused Q3 default view height value
-
- // cg.refdefViewAngles[PITCH] *= 0.5; // BFP - unused
-
AngleVectors( cg.refdefViewAngles, forward, right, up );
// BFP - Camera height setup
@@ -284,22 +277,15 @@ static void CG_OffsetThirdPersonView( void ) {
forwardScale = cos( camAngle / 180 * M_PI );
sideScale = sin( camAngle / 180 * M_PI );
-// BFP - unused
-#if 0
- VectorMA( view, -camRange * forwardScale, forward, view );
- VectorMA( view, -camRange * sideScale, right, view );
-#endif
-
- // trace a ray from the origin to the viewpoint to make sure the view isn't
- // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
-
// BFP - cg_cameraMode cvar to detect if it's disabled doesn't exist
+ // BFP - NOTE: Originally, BFP uses MASK_SOLID for tracing and it might not be a good solution, so use MASK_PLAYERSOLID for all traces here instead in the future
// That traces the camera pivot
+ // trace a ray from the origin to the viewpoint to make sure the view isn't
+ // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
if ( trace.fraction != 1.0 ) {
- // BFP - Use the vector scale to trace something solid and add endpos
+ // BFP - Use the vector scale to trace something solid
VectorScale( trace.plane.normal, camRange, view );
- VectorAdd( trace.endpos, view, cg.refdef.vieworg );
view[2] += (1.0 - trace.fraction) * 32;
// try another trace to this position, because a tunnel may have the ceiling
@@ -309,58 +295,15 @@ static void CG_OffsetThirdPersonView( void ) {
VectorCopy( trace.endpos, view );
}
-// BFP - unused
-#if 0
- VectorCopy( view, cg.refdef.vieworg );
-
- // select pitch to look at focus point from vieword
- VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
- focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
- if ( focusDist < 1 ) {
- focusDist = 1; // should never happen
- }
-
- cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
- cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value;
-#endif
-
// BFP - Camera setup
focusAngles[YAW] -= camAngle;
- // BFP - Fly tilt
- // Get the pressed keys to move left or right
- cmdNum = trap_GetCurrentCmdNumber();
- trap_GetUserCmd( cmdNum, &cmd );
-
- focusAngles[ROLL] = LERP( lastAngled, 0.0f, (float)(cg.frametime / 1000.00f) * 20.0f );
- rightAngled = LERP( lastRightAngled, 0.0f, (float)(cg.frametime / 1000.00f) * 20.0f );
- upAngled = LERP( lastUpAngled, 0.0f, (float)(cg.frametime / 1000.00f) * 20.0f );
-
- if ( cg_flytilt.integer >= 1
- && ( cg.predictedPlayerState.pm_flags & PMF_FLYING )
- && ( cg.predictedPlayerState.eFlags & EF_AURA ) && &cmd ) {
- if ( cmd.rightmove < 0 ) { // Left
- focusAngles[ROLL] = LERP( lastAngled, -20.0f, (float)(cg.frametime / 1000.00f) * 15.0f );
- rightAngled = LERP( lastRightAngled, focusAngles[ROLL] - 0.55f, (float)(cg.frametime / 1000.00f) * 15.0f );
- upAngled = LERP( lastUpAngled, -acos( focusAngles[ROLL] / 180 * M_PI ) - 1.7f, (float)(cg.frametime / 1000.00f) * 15.0f );
- } else if ( cmd.rightmove > 0 ) { // Right
- focusAngles[ROLL] = LERP( lastAngled, 20.0f, (float)(cg.frametime / 1000.00f) * 15.0f );
- rightAngled = LERP( lastRightAngled, focusAngles[ROLL] - 0.55f, (float)(cg.frametime / 1000.00f) * 15.0f );
- upAngled = LERP( lastUpAngled, -acos( focusAngles[ROLL] / 180 * M_PI ) - 1.7f, (float)(cg.frametime / 1000.00f) * 15.0f );
- }
- }
- // Last roll where it was "lerped"
- lastAngled = focusAngles[ROLL];
- lastRightAngled = rightAngled;
- lastUpAngled = upAngled;
-
VectorCopy( focusAngles, cg.refdefViewAngles );
// VectorCopy( focusAngles, cg.predictedPlayerState.viewangles ); // For player model, doesn't make sense though :P
- // BFP - NOTE: Applying angles to height and slide (up and right vectors), while rolling with fly tilt, is an odd case, BFP has something that handles up and right vectors (· ·') *curiosity sweat*
- VectorMA( overrideOrg, -camRange * sideScale + rightAngled, right, cg.refdef.vieworg );
+ VectorMA( overrideOrg, -camRange * sideScale, right, cg.refdef.vieworg );
VectorMA( cg.refdef.vieworg, -camRange * forwardScale, forward, cg.refdef.vieworg );
- VectorMA( cg.refdef.vieworg, upAngled, up, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, 0, up, cg.refdef.vieworg );
// BFP - Trace the camera position when being near to something solid
CG_Trace( &trace, view, mins, maxs, cg.refdef.vieworg, cg.predictedPlayerState.clientNum, MASK_SOLID );
@@ -385,11 +328,8 @@ Returns false if out of view.
qboolean CG_WorldCoordToScreenCoordFloat( vec3_t worldCoord, float *x, float *y ) { // BFP - Crosshair functionality
float xcenter, ycenter;
vec3_t local, transformed;
- vec3_t vforward;
- vec3_t vright;
- vec3_t vup;
- float xzi;
- float yzi;
+ vec3_t vforward, vright, vup;
+ float xzi, yzi;
xcenter = 640.0f / 2.0f; // gives screen coords in virtual 640x480, to be adjusted when drawn
ycenter = 480.0f / 2.0f; // gives screen coords in virtual 640x480, to be adjusted when drawn
@@ -445,6 +385,7 @@ void CG_OffsetFirstPersonView( centity_t *cent, refEntity_t *parent, qhandle_t p
vec3_t predictedVelocity;
int timeDelta;
orientation_t tagOrient; // BFP - First person vis mode orientation setup
+#define FPVISMODE_Q3BOBBING_MOVE 0 // BFP - A macro to enable/disable bobbing move like BFP originally does. Disabled by default because looks a little rough
if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
return;
@@ -457,11 +398,49 @@ void CG_OffsetFirstPersonView( centity_t *cent, refEntity_t *parent, qhandle_t p
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
angles[ROLL] = 40;
angles[PITCH] = -15;
- angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
+ angles[YAW] = cg.snap->ps.damageYaw + cg.snap->ps.damagePitch;
origin[2] += cg.predictedPlayerState.viewheight;
return;
}
+ // BFP - First person vis mode
+ if ( cg_drawOwnModel.integer >= 1 && parentModel ) {
+ if ( trap_R_LerpTag( &tagOrient, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, "tag_eyes" ) ) {
+ VectorCopy( parent->origin, cg.refdef.vieworg );
+#if FPVISMODE_Q3BOBBING_MOVE
+ // don't bob/duck while on air, flying or charging
+ if ( ( cg.predictedPlayerState.eFlags & EF_FLIGHT )
+ || ( cg.predictedPlayerState.pm_flags & PMF_KI_CHARGE )
+ || cg.predictedPlayerState.groundEntityNum == ENTITYNUM_NONE ) {
+ return;
+ }
+ goto _q3fpscam;
+#else
+ return;
+#endif
+ }
+
+ // if tag_eyes doesn't exist, set to tag_head
+ if ( trap_R_LerpTag( &tagOrient, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, "tag_head" ) ) {
+ VectorCopy( parent->origin, cg.refdef.vieworg );
+#if FPVISMODE_Q3BOBBING_MOVE
+ // don't bob/duck while on air, flying or charging
+ if ( ( cg.predictedPlayerState.eFlags & EF_FLIGHT )
+ || ( cg.predictedPlayerState.pm_flags & PMF_KI_CHARGE )
+ || cg.predictedPlayerState.groundEntityNum == ENTITYNUM_NONE ) {
+ return;
+ }
+ goto _q3fpscam;
+#else
+ return;
+#endif
+ }
+ }
+
+#if FPVISMODE_Q3BOBBING_MOVE
+_q3fpscam:
+#endif
+
// add angles based on weapon kick
VectorAdd (angles, cg.kick_angles, angles);
@@ -518,7 +497,9 @@ void CG_OffsetFirstPersonView( centity_t *cent, refEntity_t *parent, qhandle_t p
//===================================
// add view height
- origin[2] += cg.predictedPlayerState.viewheight;
+ if ( cg_drawOwnModel.integer < 1 ) { // BFP - For Q3 first person view
+ origin[2] += cg.predictedPlayerState.viewheight;
+ }
// smooth out duck height changes
timeDelta = cg.time - cg.duckTime;
@@ -534,25 +515,6 @@ void CG_OffsetFirstPersonView( centity_t *cent, refEntity_t *parent, qhandle_t p
}
origin[2] += bob;
-
- // BFP - First person vis mode
- // pivot the eye based on a neck length
-#if 1
- if ( cg_drawOwnModel.integer >= 1 ) {
-#define NECK_LENGTH 8
- vec3_t forward, up;
-
- VectorClear( cg.refdefViewAngles );
-
- if ( CG_GetTagOrientationFromPlayerEntityParentModel( cent, parent, parentModel, "tag_head", &tagOrient ) ) {
- VectorCopy( tagOrient.origin, cg.refdef.vieworg );
- cg.refdef.vieworg[2] -= NECK_LENGTH;
- AngleVectors( cg.refdefViewAngles, forward, NULL, up );
- VectorMA( cg.refdef.vieworg, -1, forward, cg.refdef.vieworg );
- VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
- }
- }
-#endif
// add fall height
delta = cg.time - cg.landTime;
@@ -780,12 +742,24 @@ static int CG_CalcViewValues( void ) {
VectorCopy( ps->origin, cg.refdef.vieworg );
+
+ // BFP - Fly tilt
+ if ( cg_flytilt.integer <= 0 || ps->pm_type == PM_SPECTATOR ) {
+ ps->viewangles[ROLL] = 0; // that removes tilt
+ }
VectorCopy( ps->viewangles, cg.refdefViewAngles );
if (cg_cameraOrbit.integer) {
if (cg.time > cg.nextOrbitTime) {
cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer;
cg_thirdPersonAngle.value += cg_cameraOrbit.value;
+ // BFP - If third person angle cvar surpasses 360º degrees, disable forcely camera orbit and set cg_thirdPersonAngle to 0
+ if ( cg_thirdPersonAngle.value >= 360.0f ) {
+ trap_Cvar_Set( "cg_cameraOrbit", "0" );
+ trap_Cvar_Set( "cg_thirdPersonAngle", "0" );
+ cg_cameraOrbit.value = 0.0f;
+ cg_thirdPersonAngle.value = 0.0f;
+ }
}
}
// add error decay
@@ -806,14 +780,13 @@ static int CG_CalcViewValues( void ) {
if ( cg.renderingThirdPerson && ps->pm_type != PM_SPECTATOR ) {
// back away from character
CG_OffsetThirdPersonView();
- }
-// BFP - Original Q3 first person view function call, not used here, moved to cg_players.c in CG_Player function
-#if 0
- else {
+ } else {
// offset for local bobbing and kicks
- CG_OffsetFirstPersonView();
+ // BFP - That only handles if cg_drawOwnModel is disabled
+ if ( cg_thirdPerson.integer <= 0 && cg_drawOwnModel.integer <= 0 ) {
+ CG_OffsetFirstPersonView( NULL, NULL, 0 );
+ }
}
-#endif
// position eye reletive to origin
AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
@@ -934,7 +907,8 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
CG_PredictPlayerState();
// decide on third person view
- cg.renderingThirdPerson = cg_drawOwnModel.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); //cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
+ // BFP - Also cg_drawOwnModel handles
+ cg.renderingThirdPerson = cg_thirdPerson.integer || cg_drawOwnModel.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
// build cg.refdef
inwater = CG_CalcViewValues();
@@ -948,8 +922,12 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
if ( !cg.hyperspace ) {
CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct
CG_AddMarks();
- CG_AddParticles ();
+ // BFP - Enable/disable particles
+ if ( cg_particles.integer > 0 ) {
+ CG_AddParticles ();
+ }
CG_AddLocalEntities();
+ CG_DrawMissileTrails(); // BFP - Trails
}
CG_AddViewWeapon( &cg.predictedPlayerState );
diff --git a/source/cgame/cg_weapons.c b/source/cgame/cg_weapons.c
index 60986b9..506ad08 100644
--- a/source/cgame/cg_weapons.c
+++ b/source/cgame/cg_weapons.c
@@ -24,262 +24,188 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "cg_local.h"
/*
-==========================
-CG_MachineGunEjectBrass
-==========================
-*/
-static void CG_MachineGunEjectBrass( centity_t *cent ) {
- localEntity_t *le;
- refEntity_t *re;
- vec3_t velocity, xvelocity;
- vec3_t offset, xoffset;
- float waterScale = 1.0f;
- vec3_t v[3];
+===================
+CG_AddFlashMissile
- if ( cg_brassTime.integer <= 0 ) {
+Adds muzzle flash or missile shader/model
+===================
+*/
+void CG_AddFlashMissile( qboolean isMissile, centity_t *cent, int entityNum, vec3_t origin, refEntity_t *parent, char *tagName, char *flashMissileShader, char *flashMissileModel, float flashMissileRadius, float flashMissileScaleFactor ) { // BFP - Flash or missile from player weapon tag
+ refEntity_t flashMissile;
+ float missileRadiusChargeMult = 75, missileScaleFactorChargeMult = 1;
+ float scale = 0;
+ float minCharge = 2;
+ float totalCharge = cent->currentState.generic1 - minCharge;
+
+ // don't show the muzzle flash to the player itself on first person camera, even on first person vis mode
+ if ( cg_thirdPerson.integer <= 0 && entityNum == cg.snap->ps.clientNum ) {
return;
}
- le = CG_AllocLocalEntity();
- re = &le->refEntity;
-
- velocity[0] = 0;
- velocity[1] = -50 + 40 * crandom();
- velocity[2] = 100 + 50 * crandom();
-
- le->leType = LE_FRAGMENT;
- le->startTime = cg.time;
- le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random();
+ if ( totalCharge < 0 ) {
+ totalCharge = 0;
+ }
- le->pos.trType = TR_GRAVITY;
- le->pos.trTime = cg.time - (rand()&15);
+ if ( flashMissileScaleFactor <= 0 ) {
+ flashMissileScaleFactor = 1;
+ }
- AnglesToAxis( cent->lerpAngles, v );
+ memset( &flashMissile, 0, sizeof( flashMissile ) );
- offset[0] = 8;
- offset[1] = -4;
- offset[2] = 24;
+ // BFP - TODO: These function parameters can be applied for the following cfg variable calls:
+ // - flashModel [attack index] ["name of model"]: The model used for the weapon flash.
+ // If this is not set or set to 0 and flashShader is set, the flash shader will be treated as a sprite.
+
+ // - flashShader [attack index] ["name of shader"]: The shader used for the flash.
+ // - flashRadius [attack index] [int]: Only used for sprite flashes. `flashRadius` is the radius of the flash sprite.
- xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
- xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
- xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
- VectorAdd( cent->lerpOrigin, xoffset, re->origin );
+ // - flashScaleFactor [attack index] [float]: The scale factor used for the flash.
+ // Used to resize non-sprite flashes.
+
+ // - firingFlashRadius [attack index] [int]: The radius of the firing flash.
+ // This is used for beam attacks while the beam is firing.
+
+ // - firingFlashScaleFactor [attack index] [float]: The scale factor used for the firing flash.
+ // Used to resize non-sprite flashes.
- VectorCopy( re->origin, le->pos.trBase );
+ VectorCopy( origin, flashMissile.origin );
- if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
- waterScale = 0.10f;
- }
+ if ( flashMissileModel && flashMissileModel[0] ) {
+ flashMissile.reType = RT_MODEL;
+ flashMissile.hModel = trap_R_RegisterModel( flashMissileModel );
- xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
- xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
- xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
- VectorScale( xvelocity, waterScale, le->pos.trDelta );
+ if ( parent ) {
+ AxisCopy( parent->axis, flashMissile.axis );
+ } else {
+ AxisClear( flashMissile.axis );
+ }
- AxisCopy( axisDefault, re->axis );
- re->hModel = cgs.media.machinegunBrassModel;
+ if ( isMissile ) {
+ qboolean missileSpinHoriz = qfalse;
+ scale = flashMissileScaleFactor + missileScaleFactorChargeMult * totalCharge;
+
+ // spin as it moves
+ if ( !missileSpinHoriz ) { // BFP - For disk or missileSpinHoriz (qboolean) weapons, don't rotate like the rocket
+ // BFP - missileModelRotation
+ float missileModelRotation = 0.2;
+ RotateAroundDirection( flashMissile.axis, cg.time * missileModelRotation );
+ } else {
+ // BFP - Rotate Z-axis like a wheel
+ vec3_t temp;
+ RotateAroundDirection( flashMissile.axis, cent->currentState.time );
+
+ VectorCopy( flashMissile.axis[0], temp );
+ RotatePointAroundVector( flashMissile.axis[0], flashMissile.axis[2], temp, cg.autoAnglesFast[1] );
+ VectorCopy( flashMissile.axis[1], temp );
+ RotatePointAroundVector( flashMissile.axis[1], flashMissile.axis[2], temp, cg.autoAnglesFast[1] );
+ }
+ } else {
+ scale = flashMissileScaleFactor;
+ }
+ CG_ModelSize( &flashMissile, scale );
+ } else {
+ flashMissile.reType = RT_SPRITE;
- le->bounceFactor = 0.4 * waterScale;
+ if ( isMissile ) {
+ float missileRotation = 15;
+ scale = flashMissileRadius + missileRadiusChargeMult * totalCharge;
+ flashMissile.rotation = missileRotation;
+ } else {
+ scale = flashMissileRadius;
+ }
+ flashMissile.radius = scale;
+ }
- le->angles.trType = TR_LINEAR;
- le->angles.trTime = cg.time;
- le->angles.trBase[0] = rand()&31;
- le->angles.trBase[1] = rand()&31;
- le->angles.trBase[2] = rand()&31;
- le->angles.trDelta[0] = 2;
- le->angles.trDelta[1] = 1;
- le->angles.trDelta[2] = 0;
+ if ( flashMissileShader && flashMissileShader[0] ) {
+ flashMissile.customShader = trap_R_RegisterShader( flashMissileShader );
+ }
- le->leFlags = LEF_TUMBLE;
- le->leBounceSoundType = LEBS_BRASS;
- le->leMarkType = LEMT_NONE;
+ flashMissile.shaderRGBA[0] = 0xff;
+ flashMissile.shaderRGBA[1] = 0xff;
+ flashMissile.shaderRGBA[2] = 0xff;
+ flashMissile.shaderRGBA[3] = 0xff;
+ trap_R_AddRefEntityToScene( &flashMissile );
}
+
/*
==========================
-CG_ShotgunEjectBrass
+CG_RailTrail
==========================
*/
-static void CG_ShotgunEjectBrass( centity_t *cent ) {
- localEntity_t *le;
- refEntity_t *re;
- vec3_t velocity, xvelocity;
- vec3_t offset, xoffset;
- vec3_t v[3];
- int i;
-
- if ( cg_brassTime.integer <= 0 ) {
- return;
- }
-
- for ( i = 0; i < 2; i++ ) {
- float waterScale = 1.0f;
-
- le = CG_AllocLocalEntity();
- re = &le->refEntity;
-
- velocity[0] = 60 + 60 * crandom();
- if ( i == 0 ) {
- velocity[1] = 40 + 10 * crandom();
- } else {
- velocity[1] = -40 + 10 * crandom();
- }
- velocity[2] = 100 + 50 * crandom();
-
- le->leType = LE_FRAGMENT;
- le->startTime = cg.time;
- le->endTime = le->startTime + cg_brassTime.integer*3 + cg_brassTime.integer * random();
-
- le->pos.trType = TR_GRAVITY;
- le->pos.trTime = cg.time;
+void CG_RailTrail ( clientInfo_t *ci, vec3_t start, vec3_t end ) { // BFP - BFP uses an old version of rail trail on Quake 3 Arena 1st version release
+ localEntity_t *le;
+ refEntity_t *re;
- AnglesToAxis( cent->lerpAngles, v );
+ //
+ // rings
+ //
+ le = CG_AllocLocalEntity();
+ re = &le->refEntity;
- offset[0] = 8;
- offset[1] = 0;
- offset[2] = 24;
+ le->leType = LE_FADE_RGB;
+ le->startTime = cg.time;
+ le->endTime = cg.time + cg_railTrailTime.value;
+ le->lifeRate = 1.0 / ( le->endTime - le->startTime );
- xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
- xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
- xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
- VectorAdd( cent->lerpOrigin, xoffset, re->origin );
- VectorCopy( re->origin, le->pos.trBase );
- if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
- waterScale = 0.10f;
- }
+ re->shaderTime = cg.time / 1000.0f;
+ re->reType = RT_RAIL_RINGS;
+ re->customShader = cgs.media.railRingsShader;
- xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
- xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
- xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
- VectorScale( xvelocity, waterScale, le->pos.trDelta );
+ VectorCopy( start, re->origin );
+ VectorCopy( end, re->oldorigin );
- AxisCopy( axisDefault, re->axis );
- re->hModel = cgs.media.shotgunBrassModel;
- le->bounceFactor = 0.3f;
+ // nudge down a bit so it isn't exactly in center
+ re->origin[2] -= 8;
+ re->oldorigin[2] -= 8;
- le->angles.trType = TR_LINEAR;
- le->angles.trTime = cg.time;
- le->angles.trBase[0] = rand()&31;
- le->angles.trBase[1] = rand()&31;
- le->angles.trBase[2] = rand()&31;
- le->angles.trDelta[0] = 1;
- le->angles.trDelta[1] = 0.5;
- le->angles.trDelta[2] = 0;
+ // BFP - No color1
+#if 0
+ le->color[0] = ci->color1[0] * 0.75;
+ le->color[1] = ci->color1[1] * 0.75;
+ le->color[2] = ci->color1[2] * 0.75;
+#endif
+ le->color[0] = 0.75;
+ le->color[1] = 0;
+ le->color[2] = 0;
+ le->color[3] = 1.0f;
- le->leFlags = LEF_TUMBLE;
- le->leBounceSoundType = LEBS_BRASS;
- le->leMarkType = LEMT_NONE;
- }
-}
+ AxisClear( re->axis );
-/*
-==========================
-CG_RailTrail
-==========================
-*/
-void CG_RailTrail (clientInfo_t *ci, vec3_t start, vec3_t end) {
- vec3_t axis[36], move, move2, next_move, vec, temp;
- float len;
- int i, j, skip;
-
- localEntity_t *le;
- refEntity_t *re;
-
-#define RADIUS 4
-#define ROTATION 1
-#define SPACING 5
-
- start[2] -= 4;
- VectorCopy (start, move);
- VectorSubtract (end, start, vec);
- len = VectorNormalize (vec);
- PerpendicularVector(temp, vec);
- for (i = 0 ; i < 36; i++) {
- RotatePointAroundVector(axis[i], vec, temp, i * 10);//banshee 2.4 was 10
- }
-
+ //
+ // core
+ //
le = CG_AllocLocalEntity();
re = &le->refEntity;
-
+
le->leType = LE_FADE_RGB;
le->startTime = cg.time;
le->endTime = cg.time + cg_railTrailTime.value;
- le->lifeRate = 1.0 / (le->endTime - le->startTime);
-
+ le->lifeRate = 1.0 / ( le->endTime - le->startTime );
+
re->shaderTime = cg.time / 1000.0f;
re->reType = RT_RAIL_CORE;
re->customShader = cgs.media.railCoreShader;
-
- VectorCopy(start, re->origin);
- VectorCopy(end, re->oldorigin);
-
- re->shaderRGBA[0] = ci->color1[0] * 255;
- re->shaderRGBA[1] = ci->color1[1] * 255;
- re->shaderRGBA[2] = ci->color1[2] * 255;
- re->shaderRGBA[3] = 255;
+ VectorCopy( start, re->origin );
+ VectorCopy( end, re->oldorigin );
+
+ // nudge down a bit so it isn't exactly in center
+ re->origin[2] -= 8;
+ re->oldorigin[2] -= 8;
+
+ // BFP - No color1
+#if 0
le->color[0] = ci->color1[0] * 0.75;
le->color[1] = ci->color1[1] * 0.75;
le->color[2] = ci->color1[2] * 0.75;
+#endif
+ le->color[0] = 0.75;
+ le->color[1] = 0;
+ le->color[2] = 0;
le->color[3] = 1.0f;
AxisClear( re->axis );
-
- VectorMA(move, 20, vec, move);
- VectorCopy(move, next_move);
- VectorScale (vec, SPACING, vec);
-
- if (cg_oldRail.integer != 0) {
- // nudge down a bit so it isn't exactly in center
- re->origin[2] -= 8;
- re->oldorigin[2] -= 8;
- return;
- }
- skip = -1;
-
- j = 18;
- for (i = 0; i < len; i += SPACING) {
- if (i != skip) {
- skip = i + SPACING;
- le = CG_AllocLocalEntity();
- re = &le->refEntity;
- le->leFlags = LEF_PUFF_DONT_SCALE;
- le->leType = LE_MOVE_SCALE_FADE;
- le->startTime = cg.time;
- le->endTime = cg.time + (i>>1) + 600;
- le->lifeRate = 1.0 / (le->endTime - le->startTime);
-
- re->shaderTime = cg.time / 1000.0f;
- re->reType = RT_SPRITE;
- re->radius = 1.1f;
- re->customShader = cgs.media.railRingsShader;
-
- re->shaderRGBA[0] = ci->color2[0] * 255;
- re->shaderRGBA[1] = ci->color2[1] * 255;
- re->shaderRGBA[2] = ci->color2[2] * 255;
- re->shaderRGBA[3] = 255;
-
- le->color[0] = ci->color2[0] * 0.75;
- le->color[1] = ci->color2[1] * 0.75;
- le->color[2] = ci->color2[2] * 0.75;
- le->color[3] = 1.0f;
-
- le->pos.trType = TR_LINEAR;
- le->pos.trTime = cg.time;
-
- VectorCopy( move, move2);
- VectorMA(move2, RADIUS , axis[j], move2);
- VectorCopy(move2, le->pos.trBase);
-
- le->pos.trDelta[0] = axis[j][0]*6;
- le->pos.trDelta[1] = axis[j][1]*6;
- le->pos.trDelta[2] = axis[j][2]*6;
- }
-
- VectorAdd (move, vec, move);
-
- j = j + ROTATION < 36 ? j + ROTATION : (j + ROTATION) % 36;
- }
}
/*
@@ -288,18 +214,18 @@ CG_RocketTrail
==========================
*/
static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) {
- int step;
vec3_t origin, lastPos;
- int t;
- int startTime, contents;
- int lastContents;
+ int lastContents, contents;
entityState_t *es;
+
+ // BFP - NOTE: cg_oldRocket is for these who wanna to play Q3 rocket smoke trails
+
+ int step;
+ int t;
+ int startTime;
vec3_t up;
localEntity_t *smoke;
-
- if ( cg_noProjectileTrail.integer ) {
- return;
- }
+ vec3_t color = {1, 0.75, 0}; // BFP - Color for missile trail
up[0] = 0;
up[1] = 0;
@@ -307,10 +233,15 @@ static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) {
step = 50;
- es = &ent->currentState;
startTime = ent->trailTime;
t = step * ( (startTime + step) / step );
+ if ( cg_noProjectileTrail.integer ) {
+ return;
+ }
+
+ es = &ent->currentState;
+
BG_EvaluateTrajectory( &es->pos, cg.time, origin );
contents = CG_PointContents( origin, -1 );
@@ -323,35 +254,48 @@ static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) {
BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
lastContents = CG_PointContents( lastPos, -1 );
- ent->trailTime = cg.time;
+ // BFP - Missile trail
+ if ( cg_oldRocket.integer <= 0 ) {
+ CG_MissileTrail( ent->currentState.number, origin, cgs.media.railCoreShader, color, qfalse );
+ }
if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
- if ( contents & lastContents & CONTENTS_WATER ) {
- CG_BubbleTrail( lastPos, origin, 8 );
+ if ( ( contents & lastContents & CONTENTS_WATER )
+ && cg.frametime > 0.0f ) { // BFP - If paused, don't spawn bubble particles (cg_paused.integer < 1 is another solution, but not good enough for server responses)
+ // BFP - Apply particle bubble effect in that case
+ CG_ParticleBubble( ent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, origin, lastPos, 900, 10, 2 );
+ CG_ParticleBubble( ent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, origin, lastPos, 900, 10, 2 );
+ CG_ParticleBubble( ent, cgs.media.waterBubbleShader, cgs.media.lowPolySphereModel, origin, lastPos, 900, 10, 2 );
+ // CG_BubbleTrail( lastPos, origin, 8 );
}
return;
}
- for ( ; t <= ent->trailTime ; t += step ) {
- BG_EvaluateTrajectory( &es->pos, t, lastPos );
+ if ( cg_oldRocket.integer > 0 ) {
+ ent->trailTime = cg.time;
- smoke = CG_SmokePuff( lastPos, up,
- wi->trailRadius,
- 1, 1, 1, 0.33f,
- wi->wiTrailTime,
- t,
- 0,
- 0,
- cgs.media.smokePuffShader );
- // use the optimized local entity add
- smoke->leType = LE_SCALE_FADE;
+ for ( ; t <= ent->trailTime ; t += step ) {
+ BG_EvaluateTrajectory( &es->pos, t, lastPos );
+
+ smoke = CG_SmokePuff( lastPos, up,
+ wi->trailRadius,
+ 1, 1, 1, 0.33f,
+ wi->wiTrailTime,
+ t,
+ 0,
+ 0,
+ cgs.media.smokePuffShader );
+ // use the optimized local entity add
+ smoke->leType = LE_SCALE_FADE;
+ }
}
-
}
+// BFP - Unused CG_PlasmaTrail function
+#if 0
/*
==========================
-CG_NailTrail
+CG_PlasmaTrail
==========================
*/
static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) {
@@ -361,19 +305,20 @@ static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) {
vec3_t velocity, xvelocity, origin;
vec3_t offset, xoffset;
vec3_t v[3];
- int t, startTime, step;
+ // BFP - Unused in Q3? What?
+ // int t, startTime, step;
float waterScale = 1.0f;
- if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) {
+ if ( cg_noProjectileTrail.integer ) {
return;
}
- step = 50;
+ // step = 50;
es = ¢->currentState;
- startTime = cent->trailTime;
- t = step * ( (startTime + step) / step );
+ // startTime = cent->trailTime;
+ // t = step * ( (startTime + step) / step );
BG_EvaluateTrajectory( &es->pos, cg.time, origin );
@@ -444,6 +389,8 @@ static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) {
le->angles.trDelta[2] = 0;
}
+#endif
+
/*
==========================
CG_GrappleTrail
@@ -452,7 +399,6 @@ CG_GrappleTrail
void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) {
vec3_t origin;
entityState_t *es;
- vec3_t forward, up;
refEntity_t beam;
es = &ent->currentState;
@@ -461,11 +407,8 @@ void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) {
ent->trailTime = cg.time;
memset( &beam, 0, sizeof( beam ) );
- //FIXME adjust for muzzle position
- VectorCopy ( cg_entities[ ent->currentState.otherEntityNum ].lerpOrigin, beam.origin );
- beam.origin[2] += 26;
- AngleVectors( cg_entities[ ent->currentState.otherEntityNum ].lerpAngles, forward, NULL, up );
- VectorMA( beam.origin, -6, up, beam.origin );
+
+ VectorCopy( cg_entities[ es->otherEntityNum ].pe.muzzleOrigin, beam.origin );
VectorCopy( origin, beam.oldorigin );
if (Distance( beam.origin, beam.oldorigin ) < 64 )
@@ -482,6 +425,53 @@ void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) {
trap_R_AddRefEntityToScene( &beam );
}
+
+/*
+==========================
+CG_BFPBeamTrail
+==========================
+*/
+void CG_BFPBeamTrail( centity_t *ent, const weaponInfo_t *wi ) { // BFP - BFP Beam trail handling
+ vec3_t origin;
+ entityState_t *es;
+
+ es = &ent->currentState;
+
+ BG_EvaluateTrajectory( &es->pos, cg.time, origin );
+ // ent->trailTime = cg.time;
+
+ // BFP - NOTE: That's where we apply the flash properties read from client cfg
+ CG_AddFlashMissile( qfalse, ent, es->otherEntityNum, cg_entities[ es->otherEntityNum ].pe.muzzleOrigin, NULL, "", "ImpactBeamFlashShader", 0, 25, 1 );
+
+ CG_AddFlashMissile( qtrue, ent, es->number, origin, NULL, "", "ImpactBeamFlashShader", 0, 50, 1 );
+
+ CG_BeamTrail( es->number, origin, cg_entities[ es->otherEntityNum ].pe.muzzleOrigin, cgs.media.PowerWaveBeamShader );
+ // to test corkscrew
+ // CG_CorkscrewTrail( es->number, origin, cg_entities[ es->otherEntityNum ].pe.muzzleOrigin, cgs.media.SSBBeamShader, cgs.media.SSBSpiralShader );
+}
+
+/*
+==========================
+CG_BFPSpiralBeamTrail
+==========================
+*/
+void CG_BFPSpiralBeamTrail( centity_t *ent, const weaponInfo_t *wi ) { // BFP - BFP Spiral beam trail handling
+ vec3_t origin;
+ entityState_t *es;
+
+ es = &ent->currentState;
+
+ BG_EvaluateTrajectory( &es->pos, cg.time, origin );
+ // ent->trailTime = cg.time;
+
+ // BFP - NOTE: That's where we apply the flash properties read from client cfg
+ CG_AddFlashMissile( qfalse, ent, es->otherEntityNum, cg_entities[ es->otherEntityNum ].pe.muzzleOrigin, NULL, "", "ImpactBeamFlashShader", 0, 25, 1 );
+
+ CG_AddFlashMissile( qtrue, ent, es->number, origin, NULL, "", "ImpactBeamFlashShader", 0, 50, 1 );
+
+ CG_CorkscrewTrail( es->number, origin, cg_entities[ es->otherEntityNum ].pe.muzzleOrigin, cgs.media.SSBBeamShader, cgs.media.SSBSpiralShader );
+}
+
/*
==========================
CG_GrenadeTrail
@@ -502,9 +492,10 @@ The server says this item is used on this level
void CG_RegisterWeapon( int weaponNum ) {
weaponInfo_t *weaponInfo;
gitem_t *item, *ammo;
- char path[MAX_QPATH];
- vec3_t mins, maxs;
- int i;
+ // BFP - Unused variables
+ //char path[MAX_QPATH];
+ //vec3_t mins, maxs;
+ //int i;
weaponInfo = &cg_weapons[weaponNum];
@@ -528,8 +519,11 @@ void CG_RegisterWeapon( int weaponNum ) {
if ( !item->classname ) {
CG_Error( "Couldn't find weapon %i", weaponNum );
}
- CG_RegisterItemVisuals( item - bg_itemlist );
+ // BFP - Don't register item visuals again
+ // CG_RegisterItemVisuals( item - bg_itemlist );
+ // BFP - No weaponModel
+#if 0
// load cmodel before model so filecache works
weaponInfo->weaponModel = trap_R_RegisterModel( item->world_model[0] );
@@ -538,9 +532,10 @@ void CG_RegisterWeapon( int weaponNum ) {
for ( i = 0 ; i < 3 ; i++ ) {
weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] );
}
+#endif
weaponInfo->weaponIcon = trap_R_RegisterShader( item->icon );
- weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon );
+ //weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon ); // BFP - No ammoIcon
for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) {
if ( ammo->giType == IT_AMMO && ammo->giTag == weaponNum ) {
@@ -551,40 +546,61 @@ void CG_RegisterWeapon( int weaponNum ) {
weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] );
}
- strcpy( path, item->world_model[0] );
- COM_StripExtension( path, path );
- strcat( path, "_flash.md3" );
+ // BFP - No handsModel, weaponModel and barrelModel
+#if 0
+ COM_StripExtension( item->world_model[0], path, sizeof(path) );
+ Q_strcat( path, sizeof(path), "_flash.md3" );
weaponInfo->flashModel = trap_R_RegisterModel( path );
- strcpy( path, item->world_model[0] );
- COM_StripExtension( path, path );
- strcat( path, "_barrel.md3" );
+ COM_StripExtension( item->world_model[0], path, sizeof(path) );
+ Q_strcat( path, sizeof(path), "_barrel.md3" );
weaponInfo->barrelModel = trap_R_RegisterModel( path );
- strcpy( path, item->world_model[0] );
- COM_StripExtension( path, path );
- strcat( path, "_hand.md3" );
+ COM_StripExtension( item->world_model[0], path, sizeof(path) );
+ Q_strcat( path, sizeof(path), "_hand.md3" );
weaponInfo->handsModel = trap_R_RegisterModel( path );
if ( !weaponInfo->handsModel ) {
weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" );
}
+#endif
weaponInfo->loopFireSound = qfalse;
+ // BFP - missileTrailFunc
+ switch ( weaponInfo->missileTrailFuncType ) {
+ case MISSILE_TRAIL_FUNC_BEAM: // "beam"
+ weaponInfo->missileTrailFunc = CG_BFPBeamTrail;
+ break;
+ case MISSILE_TRAIL_FUNC_ROCKET: // "rocket"
+ weaponInfo->missileTrailFunc = CG_RocketTrail;
+ break;
+ case MISSILE_TRAIL_FUNC_SPIRALBEAM: // "spiralbeam"
+ weaponInfo->missileTrailFunc = CG_BFPSpiralBeamTrail;
+ default: // "none"
+ break;
+ }
+
switch ( weaponNum ) {
case WP_GAUNTLET:
MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
- weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse );
- weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav", qfalse );
+ // BFP - TODO: Maybe hitscan weapons use firingSound only, it has some firing sound at the beginning
+ weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
+ // weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav", qfalse );
+
+ // BFP - constantFireAttack
+ weaponInfo->constantFireAttack = qtrue;
break;
case WP_LIGHTNING:
MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse );
weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/lightning/lg_hum.wav", qfalse );
-
weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/lightning/lg_fire.wav", qfalse );
+
+ // BFP - constantFireAttack
+ weaponInfo->constantFireAttack = qtrue;
+
cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew");
cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" );
cgs.media.sfx_lghit1 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit.wav", qfalse );
@@ -595,14 +611,27 @@ void CG_RegisterWeapon( int weaponNum ) {
case WP_GRAPPLING_HOOK:
MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
- weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
- weaponInfo->missileTrailFunc = CG_GrappleTrail;
+ // BFP - No missile model
+ weaponInfo->missileModel = 0; // trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
+ weaponInfo->missileTrailFunc = CG_BFPBeamTrail; // BFP - Use BFP Beam instead CG_GrappleTrail
weaponInfo->missileDlight = 200;
weaponInfo->wiTrailTime = 2000;
weaponInfo->trailRadius = 64;
MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 );
+ cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew" );
weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse );
- weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse );
+
+ // BFP - chargeSound
+ weaponInfo->chargeSound = cgs.media.defaultKiChargingSound;
+
+ // BFP - missileSound
+ weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
+
+ // BFP - firingSound
+ weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
+
+ // BFP - flashSound
+ weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/bfp/beamflash1.wav", qfalse );
break;
case WP_MACHINEGUN:
@@ -611,14 +640,19 @@ void CG_RegisterWeapon( int weaponNum ) {
weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf2b.wav", qfalse );
weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf3b.wav", qfalse );
weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf4b.wav", qfalse );
- weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" );
break;
case WP_SHOTGUN:
- MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
- weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav", qfalse );
- weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass;
+ // BFP - No flash light, just force field test
+ //MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
+ //weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav", qfalse );
+
+ // BFP - noExplosion test
+ weaponInfo->noExplosion = qtrue;
+
+ // BFP - firingSound
+ weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
break;
case WP_ROCKET_LAUNCHER:
@@ -648,10 +682,26 @@ void CG_RegisterWeapon( int weaponNum ) {
case WP_PLASMAGUN:
// weaponInfo->missileModel = cgs.media.invulnerabilityPowerupModel;
- weaponInfo->missileTrailFunc = CG_PlasmaTrail;
+ // BFP - No plasma trail (cg_oldPlasma > 0)
+ // weaponInfo->missileTrailFunc = CG_PlasmaTrail;
weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/plasma/lasfly.wav", qfalse );
+
+ // plasmagun dlight
+ weaponInfo->missileDlight = 200;
+ MAKERGB( weaponInfo->missileDlightColor, 0.2f, 0.2f, 1.0f );
+
+ // BFP - missileRotation
+ weaponInfo->missileRotation = 15;
+
+ // BFP - chargeSound
+ weaponInfo->chargeSound = cgs.media.defaultKiChargingSound;
+
MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f );
weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/plasma/hyprbf1a.wav", qfalse );
+
+ // BFP - missileShader
+ weaponInfo->missileShader = cgs.media.plasmaBallShader;
+
cgs.media.plasmaExplosionShader = trap_R_RegisterShader( "plasmaExplosion" );
cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" );
break;
@@ -667,11 +717,27 @@ void CG_RegisterWeapon( int weaponNum ) {
case WP_BFG:
weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/bfg/bfg_hum.wav", qfalse );
- MAKERGB( weaponInfo->flashDlightColor, 1, 0.7f, 1 );
+
+ // BFP - TODO: From skin config use like:
+ //missileDlight 3 200
+ //missileDlightColor 3 0.75 0 1
+
+ // bfg dlight
+ weaponInfo->missileDlight = 200;
+ //MAKERGB( weaponInfo->missileDlightColor, 0.2f, 1.0f, 0.2f );
+ MAKERGB( weaponInfo->missileDlightColor, 0.75, 0, 1 );
+
+ MAKERGB( weaponInfo->flashDlightColor, 1.0f, 0.7f, 1.0f );
weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/bfg/bfg_fire.wav", qfalse );
cgs.media.bfgExplosionShader = trap_R_RegisterShader( "bfgExplosion" );
- weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/bfg.md3" );
- weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
+
+ // BFP - Charge sound
+ weaponInfo->chargeSound = cgs.media.diskKiChargingSound;
+
+ // BFP - Modified for disk weapon testing
+ weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/disk.md3" ); //trap_R_RegisterModel( "models/weaphits/bfg.md3" );
+ weaponInfo->missileShader = trap_R_RegisterShader( "purpleAttackShader" );
+ weaponInfo->missileSound = trap_S_RegisterSound( "sound/bfp/diskfly.wav", qfalse ); //trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse );
break;
default:
@@ -734,8 +800,8 @@ VIEW WEAPON
========================================================================================
*/
-// BFP - TODO: Change animations for BFP
-
+// BFP - No CG_MapTorsoToWeaponFrame and CG_CalculateWeaponPosition
+#if 0
/*
=================
CG_MapTorsoToWeaponFrame
@@ -744,7 +810,7 @@ CG_MapTorsoToWeaponFrame
*/
static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame ) {
- // BFP - TODO: These animations aren't definitive
+ // BFP - That was changed for the animations, but in that case, it isn't necessary
// change weapon
// BFP doesn't use this animation
@@ -820,6 +886,7 @@ static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) {
angles[YAW] += scale * fracsin * 0.01;
angles[PITCH] += scale * fracsin * 0.01;
}
+#endif
/*
@@ -838,6 +905,12 @@ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
refEntity_t beam;
vec3_t forward;
vec3_t muzzlePoint, endPoint;
+ // BFP - lightningBolt config property
+ qboolean lightningBolt = qtrue;
+
+ if ( !lightningBolt ) {
+ return;
+ }
if (cent->currentState.weapon != WP_LIGHTNING) {
return;
@@ -845,6 +918,8 @@ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
memset( &beam, 0, sizeof( beam ) );
+ // BFP - Unused true lightning logic effect, remove in the future
+#if 0
// CPMA "true" lightning
if ((cent->currentState.number == cg.predictedPlayerState.clientNum) && (cg_trueLightning.value != 0)) {
vec3_t angle;
@@ -871,7 +946,9 @@ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
AngleVectors(angle, forward, NULL, NULL );
VectorCopy(cent->lerpOrigin, muzzlePoint );
// VectorCopy(cg.refdef.vieworg, muzzlePoint );
- } else {
+ } else
+#endif
+ {
// !CPMA
AngleVectors( cent->lerpAngles, forward, NULL, NULL );
VectorCopy(cent->lerpOrigin, muzzlePoint );
@@ -921,69 +998,7 @@ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
trap_R_AddRefEntityToScene( &beam );
}
}
-/*
-
-static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
- trace_t trace;
- refEntity_t beam;
- vec3_t forward;
- vec3_t muzzlePoint, endPoint;
-
- if ( cent->currentState.weapon != WP_LIGHTNING ) {
- return;
- }
-
- memset( &beam, 0, sizeof( beam ) );
-
- // find muzzle point for this frame
- VectorCopy( cent->lerpOrigin, muzzlePoint );
- AngleVectors( cent->lerpAngles, forward, NULL, NULL );
-
- // FIXME: crouch
- muzzlePoint[2] += DEFAULT_VIEWHEIGHT;
-
- VectorMA( muzzlePoint, 14, forward, muzzlePoint );
-
- // project forward by the lightning range
- VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint );
-
- // see if it hit a wall
- CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint,
- cent->currentState.number, MASK_SHOT );
- // this is the endpoint
- VectorCopy( trace.endpos, beam.oldorigin );
-
- // use the provided origin, even though it may be slightly
- // different than the muzzle origin
- VectorCopy( origin, beam.origin );
-
- beam.reType = RT_LIGHTNING;
- beam.customShader = cgs.media.lightningShader;
- trap_R_AddRefEntityToScene( &beam );
-
- // add the impact flare if it hit something
- if ( trace.fraction < 1.0 ) {
- vec3_t angles;
- vec3_t dir;
-
- VectorSubtract( beam.oldorigin, beam.origin, dir );
- VectorNormalize( dir );
-
- memset( &beam, 0, sizeof( beam ) );
- beam.hModel = cgs.media.lightningExplosionModel;
-
- VectorMA( trace.endpos, -16, dir, beam.origin );
-
- // make a random orientation
- angles[0] = rand() % 360;
- angles[1] = rand() % 360;
- angles[2] = rand() % 360;
- AnglesToAxis( angles, beam.axis );
- trap_R_AddRefEntityToScene( &beam );
- }
-}
-*/
/*
===============
@@ -1008,65 +1023,6 @@ static void CG_SpawnRailTrail( centity_t *cent, vec3_t origin ) {
}
-/*
-======================
-CG_MachinegunSpinAngle
-======================
-*/
-#define SPIN_SPEED 0.9
-#define COAST_TIME 1000
-static float CG_MachinegunSpinAngle( centity_t *cent ) {
- int delta;
- float angle;
- float speed;
-
- delta = cg.time - cent->pe.barrelTime;
- if ( cent->pe.barrelSpinning ) {
- angle = cent->pe.barrelAngle + delta * SPIN_SPEED;
- } else {
- if ( delta > COAST_TIME ) {
- delta = COAST_TIME;
- }
-
- speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
- angle = cent->pe.barrelAngle + delta * speed;
- }
-
- if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) {
- cent->pe.barrelTime = cg.time;
- cent->pe.barrelAngle = AngleMod( angle );
- cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING);
- }
-
- return angle;
-}
-
-
-/*
-========================
-CG_AddWeaponWithPowerups
-========================
-*/
-static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups ) {
- // add powerup effects
- if ( powerups & ( 1 << PW_INVIS ) ) {
- gun->customShader = cgs.media.invisShader;
- trap_R_AddRefEntityToScene( gun );
- } else {
- trap_R_AddRefEntityToScene( gun );
-
- if ( powerups & ( 1 << PW_BATTLESUIT ) ) {
- gun->customShader = cgs.media.battleWeaponShader;
- trap_R_AddRefEntityToScene( gun );
- }
- if ( powerups & ( 1 << PW_QUAD ) ) {
- gun->customShader = cgs.media.quadWeaponShader;
- trap_R_AddRefEntityToScene( gun );
- }
- }
-}
-
-
/*
=============
CG_AddPlayerWeapon
@@ -1076,84 +1032,59 @@ The main player will have this called for BOTH cases, so effects like light and
sound should only be done on the world model case.
=============
*/
-void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ) {
- refEntity_t gun;
- refEntity_t barrel;
- refEntity_t flash;
- vec3_t angles;
+// BFP - CG_AddPlayerWeapon now has a new argument called tagName to attach the flash to a tag
+void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team, char *tagName ) {
weapon_t weaponNum;
weaponInfo_t *weapon;
centity_t *nonPredictedCent;
-// int col;
weaponNum = cent->currentState.weapon;
CG_RegisterWeapon( weaponNum );
weapon = &cg_weapons[weaponNum];
- // add the weapon
- memset( &gun, 0, sizeof( gun ) );
- VectorCopy( parent->lightingOrigin, gun.lightingOrigin );
- gun.shadowPlane = parent->shadowPlane;
- gun.renderfx = parent->renderfx;
-
- // set custom shading for railgun refire rate
- if ( ps ) {
- if ( cg.predictedPlayerState.weapon == WP_RAILGUN
- && cg.predictedPlayerState.weaponstate == WEAPON_FIRING ) {
- float f;
-
- f = (float)cg.predictedPlayerState.weaponTime / 1500;
- gun.shaderRGBA[1] = 0;
- gun.shaderRGBA[0] =
- gun.shaderRGBA[2] = 255 * ( 1.0 - f );
- } else {
- gun.shaderRGBA[0] = 255;
- gun.shaderRGBA[1] = 255;
- gun.shaderRGBA[2] = 255;
- gun.shaderRGBA[3] = 255;
+ if ( !ps ) {
+ // BFP - With constantFireAttack, don't play and start the sound looply
+ if ( !( cent->currentState.eFlags & EF_FIRING ) ) {
+ cent->pe.constantFireAtkPlayed = qfalse;
}
- }
- // gun.hModel = weapon->weaponModel; // BFP - Hide weapon model
- if (!gun.hModel) {
- return;
- }
+ // BFP - attackChargeVoice [attack index] [charge count] ["path of sound"]
+ weapon->attackChargeVoice = trap_S_RegisterSound( "sound/player/bfp1-kyah/attack4V1.wav", qtrue );
+ if ( cg_stfu.integer <= 0
+ && weapon->attackChargeVoice
+ && !cent->pe.lastChargeVoiceLevel
+ && cent->currentState.generic1 == 2 // BFP - TODO: [charge count] set here
+ && ( ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK0_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK1_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK2_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK3_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK4_PREPARE ) ) {
+ trap_S_StartSound( cent->lerpOrigin, ENTITYNUM_WORLD, CHAN_VOICE, weapon->attackChargeVoice );
+ cent->pe.lastChargeVoiceLevel = qtrue;
+ } else if ( !( ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK0_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK1_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK2_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK3_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK4_PREPARE ) ) { // stopped charging ki attack, resets to the next cycle
+ cent->pe.lastChargeVoiceLevel = qfalse;
+ }
- if ( !ps ) {
- // add weapon ready sound
- cent->pe.lightningFiring = qfalse;
- if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) {
+ // BFP - chargeSound
+ if ( weapon->chargeSound
+ && ( ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK0_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK1_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK2_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK3_PREPARE
+ || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) == TORSO_ATTACK4_PREPARE ) ) {
+ trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin,
+ vec3_origin, weapon->chargeSound );
+ } else if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) {
// lightning gun and guantlet make a different sound when fire is held down
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->firingSound );
- cent->pe.lightningFiring = qtrue;
- } else if ( weapon->readySound ) {
- trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound );
}
}
- CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon");
-
- CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups );
-
- // add the spinning barrel
- if ( weapon->barrelModel ) {
- memset( &barrel, 0, sizeof( barrel ) );
- VectorCopy( parent->lightingOrigin, barrel.lightingOrigin );
- barrel.shadowPlane = parent->shadowPlane;
- barrel.renderfx = parent->renderfx;
-
- barrel.hModel = weapon->barrelModel;
- angles[YAW] = 0;
- angles[PITCH] = 0;
- angles[ROLL] = CG_MachinegunSpinAngle( cent );
- AnglesToAxis( angles, barrel.axis );
-
- CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" );
-
- CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups );
- }
-
// make sure we aren't looking at cg.predictedPlayerEntity for LG
nonPredictedCent = &cg_entities[cent->currentState.clientNum];
@@ -1165,54 +1096,63 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
}
// add the flash
- if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK )
+ // BFP - constantFireAttack
+ if ( ( weapon->constantFireAttack || weaponNum == WP_GRAPPLING_HOOK )
&& ( nonPredictedCent->currentState.eFlags & EF_FIRING ) )
{
// continuous flash
} else {
// impulse flash
- if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash ) {
+ if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash
+ && weaponNum != WP_GRAPPLING_HOOK
+ && weaponNum != WP_PLASMAGUN
+ && weaponNum != WP_BFG ) {
return;
}
}
- memset( &flash, 0, sizeof( flash ) );
- VectorCopy( parent->lightingOrigin, flash.lightingOrigin );
- flash.shadowPlane = parent->shadowPlane;
- flash.renderfx = parent->renderfx;
-
- flash.hModel = weapon->flashModel;
- if (!flash.hModel) {
- return;
- }
- angles[YAW] = 0;
- angles[PITCH] = 0;
- angles[ROLL] = crandom() * 10;
- AnglesToAxis( angles, flash.axis );
-
- // colorize the railgun blast
- if ( weaponNum == WP_RAILGUN ) {
- clientInfo_t *ci;
-
- ci = &cgs.clientinfo[ cent->currentState.clientNum ];
- flash.shaderRGBA[0] = 255 * ci->color1[0];
- flash.shaderRGBA[1] = 255 * ci->color1[1];
- flash.shaderRGBA[2] = 255 * ci->color1[2];
+ // BFP - NOTE: Here's where the player gets the muzzle attached from some of the tags (apply that to client cfg side) (tag_left, tag_right, tag_eyes, ...)
+ {
+ refEntity_t tagEnt;
+ memset( &tagEnt, 0, sizeof( tagEnt ) );
+ CG_PositionEntityOnTag( &tagEnt, parent, parent->hModel, tagName );
+ VectorCopy( tagEnt.origin, nonPredictedCent->pe.muzzleOrigin );
+
+ // BFP - Displaying the muzzle flash to the other player correctly
+ if ( nonPredictedCent->currentState.eFlags & EF_READY_KI_ATTACK ) {
+ // BFP - NOTE: That's where we apply the flash properties read from client cfg
+ if ( weaponNum == WP_GRAPPLING_HOOK ) {
+ CG_AddFlashMissile( qfalse, nonPredictedCent, -1, nonPredictedCent->pe.muzzleOrigin, &tagEnt, tagName, "ImpactBeamFlashShader", 0, 15, 1 );
+ }
+ if ( weaponNum == WP_PLASMAGUN ) {
+ CG_AddFlashMissile( qfalse, nonPredictedCent, -1, nonPredictedCent->pe.muzzleOrigin, &tagEnt, tagName, "plasmaExplosion", 0, 15, 1 );
+ }
+ if ( weaponNum == WP_BFG ) {
+ CG_AddFlashMissile( qfalse, nonPredictedCent, -1, nonPredictedCent->pe.muzzleOrigin, &tagEnt, tagName, "purpleAttackShader", "models/weaphits/disk.md3", 0, 0.8 );
+ }
+ }
}
- CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash");
- trap_R_AddRefEntityToScene( &flash );
-
if ( ps || cg.renderingThirdPerson ||
cent->currentState.number != cg.predictedPlayerState.clientNum ) {
+
+ // BFP - NOTE: That avoids adding the muzzle light using the beam,
+ // it would be cool adding a light to the player while charging or firing their ki,
+ // but in a custom way
+ if ( weaponNum == WP_GRAPPLING_HOOK
+ || weaponNum == WP_PLASMAGUN
+ || weaponNum == WP_BFG ) {
+ return;
+ }
+
// add lightning bolt
- CG_LightningBolt( nonPredictedCent, flash.origin );
+ CG_LightningBolt( nonPredictedCent, parent->origin );
// add rail trail
- CG_SpawnRailTrail( cent, flash.origin );
+ CG_SpawnRailTrail( cent, parent->origin );
if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) {
- trap_R_AddLightToScene( flash.origin, 300 + (rand()&31), weapon->flashDlightColor[0],
+ trap_R_AddLightToScene( parent->origin, 300 + (rand()&31), weapon->flashDlightColor[0],
weapon->flashDlightColor[1], weapon->flashDlightColor[2] );
}
}
@@ -1226,12 +1166,13 @@ Add the weapon, and flash for the player's view
==============
*/
void CG_AddViewWeapon( playerState_t *ps ) {
- refEntity_t hand;
- centity_t *cent;
- clientInfo_t *ci;
- float fovOffset;
- vec3_t angles;
- weaponInfo_t *weapon;
+ // BFP - Unused variables
+ //refEntity_t hand;
+ //clientInfo_t *ci;
+ //vec3_t angles;
+ //weaponInfo_t *weapon;
+ //centity_t *cent;
+ //float fovOffset;
if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
return;
@@ -1266,15 +1207,20 @@ void CG_AddViewWeapon( playerState_t *ps ) {
return;
}
+ // BFP - No fovOffset
+#if 0
// drop gun lower at higher fov
if ( cg_fov.integer > 90 ) {
fovOffset = -0.2 * ( cg_fov.integer - 90 );
} else {
fovOffset = 0;
}
+#endif
- cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum];
+ //cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum];
CG_RegisterWeapon( ps->weapon );
+ // BFP - No draw hand and gun
+#if 0
weapon = &cg_weapons[ ps->weapon ];
memset (&hand, 0, sizeof(hand));
@@ -1305,7 +1251,8 @@ void CG_AddViewWeapon( playerState_t *ps ) {
hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT;
// add everything onto the hand
- CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity, ps->persistant[PERS_TEAM] );
+ CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity, ps->persistant[PERS_TEAM], "" );
+#endif
}
/*
@@ -1321,7 +1268,8 @@ WEAPON SELECTION
CG_DrawWeaponSelect
===================
*/
-void CG_DrawWeaponSelect( void ) {
+void CG_DrawWeaponSelect( void ) { // BFP - Modified Q3 selectable weapon HUD
+ // BFP - TODO: Change that selection to 5 selectable ki attacks
int i;
int bits;
int count;
@@ -1344,18 +1292,20 @@ void CG_DrawWeaponSelect( void ) {
cg.itemPickupTime = 0;
// count the number of weapons owned
+ i = 0;
bits = cg.snap->ps.stats[ STAT_WEAPONS ];
count = 0;
- for ( i = 1 ; i < 16 ; i++ ) {
+ while ( ++i < 16 ) {
if ( bits & ( 1 << i ) ) {
- count++;
+ ++count;
}
}
- x = 320 - count * 20;
- y = 380;
+ x = 10;
+ y = 75;
- for ( i = 1 ; i < 16 ; i++ ) {
+ i = 0;
+ while ( ++i < 16 ) {
if ( !( bits & ( 1 << i ) ) ) {
continue;
}
@@ -1363,28 +1313,30 @@ void CG_DrawWeaponSelect( void ) {
CG_RegisterWeapon( i );
// draw weapon icon
- CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon );
+ CG_DrawPic( x, y, 91, 46, cg_weapons[i].weaponIcon );
// draw selection marker
if ( i == cg.weaponSelect ) {
- CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader );
+ CG_DrawPic( x, y, 91, 46, cgs.media.selectShader );
}
// no ammo cross on top
if ( !cg.snap->ps.ammo[ i ] ) {
- CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader );
+ CG_DrawPic( x, y, 91, 46, cgs.media.noammoShader );
}
- x += 40;
+ y += 47;
}
+ // BFP - attackName
+
// draw the selected name
if ( cg_weapons[ cg.weaponSelect ].item ) {
name = cg_weapons[ cg.weaponSelect ].item->pickup_name;
if ( name ) {
w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
x = ( SCREEN_WIDTH - w ) / 2;
- CG_DrawBigStringColor(x, y - 22, name, color);
+ CG_DrawBigStringColor(x, 288, name, color);
}
}
@@ -1432,9 +1384,12 @@ void CG_NextWeapon_f( void ) {
if ( cg.weaponSelect == 16 ) {
cg.weaponSelect = 0;
}
+ // BFP - Don't lock the scroll to this weapon index
+#if 0
if ( cg.weaponSelect == WP_GAUNTLET ) {
continue; // never cycle to gauntlet
}
+#endif
if ( CG_WeaponSelectable( cg.weaponSelect ) ) {
break;
}
@@ -1468,9 +1423,12 @@ void CG_PrevWeapon_f( void ) {
if ( cg.weaponSelect == -1 ) {
cg.weaponSelect = 15;
}
+ // BFP - Don't lock the scroll to this weapon index
+#if 0
if ( cg.weaponSelect == WP_GAUNTLET ) {
continue; // never cycle to gauntlet
}
+#endif
if ( CG_WeaponSelectable( cg.weaponSelect ) ) {
break;
}
@@ -1566,11 +1524,54 @@ void CG_FireWeapon( centity_t *cent ) {
// append the flash to the weapon model
cent->muzzleFlashTime = cg.time;
- // lightning gun only does this this on initial press
- if ( ent->weapon == WP_LIGHTNING ) {
- if ( cent->pe.lightningFiring ) {
- return;
+ // BFP - constantFireAttack
+ if ( weap->constantFireAttack ) {
+ if ( cent->pe.constantFireAtkPlayed ) {
+ goto _skipFlashSound;
}
+ cent->pe.constantFireAtkPlayed = qtrue;
+ }
+
+ // BFP - attackFireVoice
+ weap->attackFireVoice = trap_S_RegisterSound( "sound/player/bfp1-kyah/attack5F.wav", qtrue );
+ if ( cg_stfu.integer <= 0
+ && weap->attackFireVoice && ( cent->currentState.eFlags & EF_FIRING )
+ && ent->weapon != WP_SHOTGUN ) { // BFP - TODO: Handle forcefield
+ trap_S_StartSound( cent->lerpOrigin, ent->number, CHAN_VOICE, weap->attackFireVoice );
+ }
+
+ // BFP - Just testing, WP_SHOTGUN treated as ki explosion example
+ if ( ent->weapon == WP_SHOTGUN ) {
+ cent->pe.chargeAutoFire = qtrue;
+ }
+ if ( ent->weapon == WP_SHOTGUN && !cent->pe.chargeAutoFire
+ && weap->noExplosion ) {
+ // BFP - Use that as blinding_flash weapon, no chargeAutoFire set
+ // this is when noExplosion is set as weapon config dictates
+
+ // BFP - Low poly sphere
+ // BFP - TODO: Apply explosionModel from bfp attack config, highPolySphereModel is just a test
+ qhandle_t sphereModel = ( cg_lowpolysphere.integer > 0 ) ? cgs.media.lowPolySphereModel : cgs.media.highPolySphereModel;
+ qhandle_t blindingShader = trap_R_RegisterShader( "BlindingFlashShader" );
+ localEntity_t *leSphere;
+ const float MAX_SCALE = 27, MAX_SCALEFACTOR = 6.0f; // limits to prevent too large scaling
+ // BFP - TODO: Apply explosionScaleFactor as indicated on default.cfg file from some character: explosionScaleFactor
+ // BFP - TODO: Apply explosionScaleFactorChargeMult as indicated on default.cfg file from some character: explosionScaleFactorChargeMult
+ float explosionScaleFactor = 3, explosionScaleFactorChargeMult = 0;
+ float scale = 1;
+
+ if ( explosionScaleFactor > MAX_SCALEFACTOR ) explosionScaleFactor = MAX_SCALEFACTOR;
+ if ( explosionScaleFactorChargeMult > MAX_SCALEFACTOR ) explosionScaleFactorChargeMult = MAX_SCALEFACTOR;
+ scale = explosionScaleFactor + explosionScaleFactorChargeMult;
+ if ( scale > MAX_SCALE ) scale = MAX_SCALE;
+
+ leSphere = CG_SpawnExplosionModel( cent->lerpOrigin, NULL, LE_EXPLOSION_SPHERE, sphereModel, blindingShader, 1000 );
+ VectorScale( leSphere->refEntity.axis[0], scale, leSphere->refEntity.axis[0] );
+ VectorScale( leSphere->refEntity.axis[1], scale, leSphere->refEntity.axis[1] );
+ VectorScale( leSphere->refEntity.axis[2], scale, leSphere->refEntity.axis[2] );
+
+ CG_ExplosionSound( cent->lerpOrigin );
+ return;
}
// play quad sound if needed
@@ -1592,13 +1593,15 @@ void CG_FireWeapon( centity_t *cent ) {
}
}
+// BFP - constantFireAttack handling
+_skipFlashSound:
+
// do brass ejection
if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) {
weap->ejectBrassFunc( cent );
}
}
-
/*
=================
CG_MissileHitWall
@@ -1608,33 +1611,17 @@ Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing
*/
void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ) {
qhandle_t mod;
- qhandle_t mark;
qhandle_t shader;
sfxHandle_t sfx;
float radius;
- float light;
- vec3_t lightColor;
- localEntity_t *le;
int r;
- qboolean alphaFade;
- qboolean isSprite;
- int duration;
- vec3_t sprOrg;
- vec3_t sprVel;
-
- mark = 0;
- radius = 32;
+
+ // BFP - NOTE: Crack mark shader replaces all other mark shaders, the radius is the same (64), there's no alpha fade
+
+ radius = 64;
sfx = 0;
mod = 0;
shader = 0;
- light = 0;
- lightColor[0] = 1;
- lightColor[1] = 1;
- lightColor[2] = 0;
-
- // set defaults
- isSprite = qfalse;
- duration = 600;
switch ( weapon ) {
default:
@@ -1648,71 +1635,40 @@ void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, im
} else {
sfx = cgs.media.sfx_lghit3;
}
- mark = cgs.media.holeMarkShader;
- radius = 12;
break;
case WP_GRENADE_LAUNCHER:
mod = cgs.media.dishFlashModel;
shader = cgs.media.grenadeExplosionShader;
sfx = cgs.media.sfx_rockexp;
- mark = cgs.media.burnMarkShader;
- radius = 64;
- light = 300;
- isSprite = qtrue;
break;
case WP_ROCKET_LAUNCHER:
mod = cgs.media.dishFlashModel;
shader = cgs.media.rocketExplosionShader;
sfx = cgs.media.sfx_rockexp;
- mark = cgs.media.burnMarkShader;
- radius = 64;
- light = 300;
- isSprite = qtrue;
- duration = 1000;
- lightColor[0] = 1;
- lightColor[1] = 0.75;
- lightColor[2] = 0.0;
- if (cg_oldRocket.integer == 0) {
- // explosion sprite animation
- VectorMA( origin, 24, dir, sprOrg );
- VectorScale( dir, 64, sprVel );
-
- CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1400, 20, 30 );
- }
break;
case WP_RAILGUN:
mod = cgs.media.ringFlashModel;
shader = cgs.media.railExplosionShader;
sfx = cgs.media.sfx_plasmaexp;
- mark = cgs.media.energyMarkShader;
- radius = 24;
break;
case WP_PLASMAGUN:
mod = cgs.media.ringFlashModel;
shader = cgs.media.plasmaExplosionShader;
sfx = cgs.media.sfx_plasmaexp;
- mark = cgs.media.energyMarkShader;
- radius = 16;
break;
case WP_BFG:
mod = cgs.media.dishFlashModel;
shader = cgs.media.bfgExplosionShader;
sfx = cgs.media.sfx_rockexp;
- mark = cgs.media.burnMarkShader;
- radius = 32;
- isSprite = qtrue;
break;
case WP_SHOTGUN:
mod = cgs.media.bulletFlashModel;
shader = cgs.media.bulletExplosionShader;
- mark = cgs.media.bulletMarkShader;
sfx = 0;
- radius = 4;
break;
case WP_MACHINEGUN:
mod = cgs.media.bulletFlashModel;
shader = cgs.media.bulletExplosionShader;
- mark = cgs.media.bulletMarkShader;
r = rand() & 3;
if ( r == 0 ) {
@@ -1723,42 +1679,57 @@ void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, im
sfx = cgs.media.sfx_ric3;
}
- radius = 8;
break;
}
- if ( sfx ) {
+ // BFP - Don't display for force field weapons
+ if ( weapon == WP_SHOTGUN ) {
+ return;
+ }
+
+ if ( sfx
+ && ( weapon == WP_MACHINEGUN || weapon == WP_SHOTGUN || weapon == WP_LIGHTNING ) ) { // BFP - TODO: Apply these weapon types into this sound
trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx );
+ } else { // BFP - Explosion sounds
+ CG_ExplosionSound( origin );
}
//
// create the explosion
//
- if ( mod ) {
- le = CG_MakeExplosion( origin, dir,
- mod, shader,
- duration, isSprite );
- le->light = light;
- VectorCopy( lightColor, le->lightColor );
- if ( weapon == WP_RAILGUN ) {
- // colorize with client color
- VectorCopy( cgs.clientinfo[clientNum].color1, le->color );
- }
+ if ( weapon == WP_MACHINEGUN || weapon == WP_SHOTGUN ) { // BFP - TODO: Apply muzzle effects only to these weapon types (from default.cfg file from some character)
+ // BFP - Don't use this explosion, use explosion model instead
+#if 0
+ CG_MakeExplosion( origin, dir,
+ mod, shader,
+ 600, qfalse );
+#endif
+ const float MAX_SCALE = 27, MAX_SCALEFACTOR = 6.0f; // limits to prevent too large scaling
+ // BFP - TODO: Apply explosionScaleFactor as indicated on default.cfg file from some character: explosionScaleFactor
+ // BFP - TODO: Apply explosionScaleFactorChargeMult as indicated on default.cfg file from some character: explosionScaleFactorChargeMult
+ float explosionScaleFactor = 3, explosionScaleFactorChargeMult = 0;
+ float scale = 1;
+ localEntity_t *leExp;
+
+ if ( explosionScaleFactor > MAX_SCALEFACTOR ) explosionScaleFactor = MAX_SCALEFACTOR;
+ if ( explosionScaleFactorChargeMult > MAX_SCALEFACTOR ) explosionScaleFactorChargeMult = MAX_SCALEFACTOR;
+ scale = explosionScaleFactor + explosionScaleFactorChargeMult;
+ if ( scale > MAX_SCALE ) scale = MAX_SCALE;
+
+ leExp = CG_SpawnExplosionModel( origin, dir, LE_EXPLOSION_SPHERE, mod, shader, 1200 );
+ VectorScale( leExp->refEntity.axis[0], scale, leExp->refEntity.axis[0] );
+ VectorScale( leExp->refEntity.axis[1], scale, leExp->refEntity.axis[1] );
+ VectorScale( leExp->refEntity.axis[2], scale, leExp->refEntity.axis[2] );
+ } else if ( weapon == WP_LIGHTNING ) { // BFP - TODO: Just a test for lightning gun (eyebeam should have this behavior)
+ CG_SmokeExplosion( origin, dir ); // BFP - Explosion smoke
+ } else { // BFP - Explosion effects
+ CG_ExplosionEffect( origin, dir );
}
//
// impact mark
//
- alphaFade = (mark == cgs.media.energyMarkShader); // plasma fades alpha, all others fade color
- if ( weapon == WP_RAILGUN ) {
- float *color;
-
- // colorize with client color
- color = cgs.clientinfo[clientNum].color2;
- CG_ImpactMark( mark, origin, dir, random()*360, color[0],color[1], color[2],1, alphaFade, radius, qfalse );
- } else {
- CG_ImpactMark( mark, origin, dir, random()*360, 1,1,1,1, alphaFade, radius, qfalse );
- }
+ CG_ImpactMark( cgs.media.crackMarkShader, origin, dir, random()*360, 1,1,1,1, 0, radius, qfalse );
}
@@ -1768,16 +1739,57 @@ CG_MissileHitPlayer
=================
*/
void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ) {
- CG_Bleed( origin, entityNum );
+ // BFP - Seems like a small redo from CG_MissileHitWall to adjust the needed effects
+ sfxHandle_t sfx = 0;
+
+ // BFP - NOTE: Originally on BFP, players don't bleed, that's a friendly mod :P
+ // CG_Bleed( origin, entityNum );
// some weapons will make an explosion with the blood, while
// others will just make the blood
switch ( weapon ) {
- case WP_GRENADE_LAUNCHER:
- case WP_ROCKET_LAUNCHER:
- CG_MissileHitWall( weapon, 0, origin, dir, IMPACTSOUND_FLESH );
+ case WP_LIGHTNING:
+ break;
+ case WP_MACHINEGUN:
+ case WP_SHOTGUN: // BFP - TODO: Apply muzzle effects only to these weapon types (from default.cfg file from some character)
+ if ( weapon == WP_MACHINEGUN ) {
+ int r = rand() & 3;
+ switch ( r ) {
+ case 0: sfx = cgs.media.sfx_ric1; break;
+ case 1: sfx = cgs.media.sfx_ric2; break;
+ default: sfx = cgs.media.sfx_ric3; break;
+ }
+ }
+ trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx );
+ // BFP - Don't use this explosion, use explosion model instead
+#if 0
+ CG_MakeExplosion( origin, dir,
+ cgs.media.bulletFlashModel, cgs.media.bulletExplosionShader,
+ 600, qfalse );
+#endif
+ {
+ const float MAX_SCALE = 27, MAX_SCALEFACTOR = 6.0f; // limits to prevent too large scaling
+ // BFP - TODO: Apply explosionScaleFactor as indicated on default.cfg file from some character: explosionScaleFactor
+ // BFP - TODO: Apply explosionScaleFactorChargeMult as indicated on default.cfg file from some character: explosionScaleFactorChargeMult
+ float explosionScaleFactor = 3, explosionScaleFactorChargeMult = 0;
+ float scale = 1;
+ localEntity_t *leExp;
+
+ if ( explosionScaleFactor > MAX_SCALEFACTOR ) explosionScaleFactor = MAX_SCALEFACTOR;
+ if ( explosionScaleFactorChargeMult > MAX_SCALEFACTOR ) explosionScaleFactorChargeMult = MAX_SCALEFACTOR;
+ scale = explosionScaleFactor + explosionScaleFactorChargeMult;
+ if ( scale > MAX_SCALE ) scale = MAX_SCALE;
+
+ leExp = CG_SpawnExplosionModel( origin, dir, LE_EXPLOSION_SPHERE, cgs.media.bulletFlashModel, cgs.media.bulletExplosionShader, 1200 );
+ VectorScale( leExp->refEntity.axis[0], scale, leExp->refEntity.axis[0] );
+ VectorScale( leExp->refEntity.axis[1], scale, leExp->refEntity.axis[1] );
+ VectorScale( leExp->refEntity.axis[2], scale, leExp->refEntity.axis[2] );
+ }
break;
default:
+ CG_ExplosionSound( origin ); // BFP - Explosion sounds
+ CG_SparksExplosion( origin, dir ); // BFP - Spark particles explosion
+ CG_ExplosionEffect( origin, dir ); // BFP - Explosion effects
break;
}
}
@@ -1799,10 +1811,13 @@ CG_ShotgunPellet
*/
static void CG_ShotgunPellet( vec3_t start, vec3_t end, int skipNum ) {
trace_t tr;
- int sourceContentType, destContentType;
+ // BFP - Unused variables
+ // int sourceContentType, destContentType;
CG_Trace( &tr, start, NULL, NULL, end, skipNum, MASK_SHOT );
+ // BFP - Don't spawn bubble trail effect
+#if 0
sourceContentType = trap_CM_PointContents( start, 0 );
destContentType = trap_CM_PointContents( tr.endpos, 0 );
@@ -1822,6 +1837,7 @@ static void CG_ShotgunPellet( vec3_t start, vec3_t end, int skipNum ) {
trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER );
CG_BubbleTrail( tr.endpos, trace.endpos, 32 );
}
+#endif
if ( tr.surfaceFlags & SURF_NOIMPACT ) {
return;
@@ -1943,36 +1959,20 @@ void CG_Tracer( vec3_t source, vec3_t dest ) {
VectorNormalize( right );
VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz );
- verts[0].st[0] = 0;
- verts[0].st[1] = 1;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
+ Vector2Set( verts[0].st, 0, 1 );
+ Byte4Set( verts[0].modulate, 255, 255, 255, 255 );
VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz );
- verts[1].st[0] = 1;
- verts[1].st[1] = 0;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
+ Vector2Set( verts[1].st, 1, 0 );
+ Byte4Set( verts[1].modulate, 255, 255, 255, 255 );
VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz );
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
+ Vector2Set( verts[2].st, 1, 1 );
+ Byte4Set( verts[2].modulate, 255, 255, 255, 255 );
VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz );
- verts[3].st[0] = 0;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
+ Vector2Set( verts[3].st, 0, 0 );
+ Byte4Set( verts[3].modulate, 255, 255, 255, 255 );
trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts );
@@ -1985,7 +1985,8 @@ void CG_Tracer( vec3_t source, vec3_t dest ) {
}
-
+// BFP - Unused CG_CalcMuzzlePoint function
+#if 0
/*
======================
CG_CalcMuzzlePoint
@@ -2024,6 +2025,7 @@ static qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) {
return qtrue;
}
+#endif
/*
======================
@@ -2033,6 +2035,8 @@ Renders bullet effects.
======================
*/
void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ) {
+ // BFP - No water particles in BFP by firing a kind of ki-type bullet
+#if 0
trace_t trace;
int sourceContentType, destContentType;
vec3_t start;
@@ -2065,12 +2069,8 @@ void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh,
}
}
}
+#endif
// impact splash and mark
- if ( flesh ) {
- CG_Bleed( end, fleshEntityNum );
- } else {
- CG_MissileHitWall( WP_MACHINEGUN, 0, end, normal, IMPACTSOUND_DEFAULT );
- }
-
+ CG_MissileHitWall( WP_MACHINEGUN, 0, end, normal, IMPACTSOUND_DEFAULT );
}
diff --git a/source/cgame/cgame.bat b/source/cgame/cgame.bat
index 18a79ba..ce03fcb 100644
--- a/source/cgame/cgame.bat
+++ b/source/cgame/cgame.bat
@@ -40,6 +40,8 @@ set cc=..\..\..\tools\bin\lcc.exe -DQ3_VM -S -Wf-target=bytecode -Wf-g -I%src%\c
@if errorlevel 1 goto quit
%cc% %src%/cgame/cg_marks.c
@if errorlevel 1 goto quit
+%cc% %src%/cgame/cg_particles.c
+@if errorlevel 1 goto quit
%cc% %src%/cgame/cg_players.c
@if errorlevel 1 goto quit
%cc% %src%/cgame/cg_playerstate.c
@@ -52,6 +54,8 @@ set cc=..\..\..\tools\bin\lcc.exe -DQ3_VM -S -Wf-target=bytecode -Wf-g -I%src%\c
@if errorlevel 1 goto quit
%cc% %src%/cgame/cg_snapshot.c
@if errorlevel 1 goto quit
+%cc% %src%/cgame/cg_trails.c
+@if errorlevel 1 goto quit
%cc% %src%/cgame/cg_view.c
@if errorlevel 1 goto quit
%cc% %src%/cgame/cg_weapons.c
diff --git a/source/cgame/cgame.def b/source/cgame/cgame.def
new file mode 100644
index 0000000..2ee748e
--- /dev/null
+++ b/source/cgame/cgame.def
@@ -0,0 +1,3 @@
+EXPORTS
+ vmMain
+ dllEntry
diff --git a/source/cgame/cgame.q3asm b/source/cgame/cgame.q3asm
index c44b804..957c050 100644
--- a/source/cgame/cgame.q3asm
+++ b/source/cgame/cgame.q3asm
@@ -10,12 +10,14 @@ cg_event
cg_info
cg_localents
cg_marks
+cg_particles
cg_players
cg_playerstate
cg_predict
cg_scoreboard
cg_servercmds
cg_snapshot
+cg_trails
cg_view
cg_weapons
bg_slidemove
diff --git a/source/cgame/tr_types.h b/source/cgame/tr_types.h
index b3ed8da..232e8db 100644
--- a/source/cgame/tr_types.h
+++ b/source/cgame/tr_types.h
@@ -148,22 +148,26 @@ typedef enum {
TC_S3TC
} textureCompression_t;
-
-//
-// glDriverType_t is not used by the engine and quake3 game anymore.
-// Single value (GLDRV_ICD) is left only for compatibility with other mods.
typedef enum {
- GLDRV_ICD // driver is integrated with window system
+ GLDRV_ICD, // driver is integrated with window system
+ // WARNING: there are tests that check for
+ // > GLDRV_ICD for minidriverness, so this
+ // should always be the lowest value in this
+ // enum set
+ GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver
+ GLDRV_VOODOO // driver is a 3Dfx standalone driver
} glDriverType_t;
-//
-//
-// glHardwareType_t is not used by the engine and quake3 game anymore.
-// Single value (GLHW_GENERIC) is left only for compatibility with other mods.
typedef enum {
- GLHW_GENERIC // where everthing works the way it should
+ GLHW_GENERIC, // where everthing works the way it should
+ GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is
+ // the hardware type then there can NOT exist a secondary
+ // display adapter
+ GLHW_RIVA128, // where you can't interpolate alpha
+ GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures
+ GLHW_PERMEDIA2 // where you don't have src*dst
} glHardwareType_t;
-//
+
typedef struct {
char renderer_string[MAX_STRING_CHARS];
diff --git a/source/game/ai_bfp.c b/source/game/ai_bfp.c
new file mode 100644
index 0000000..3324098
--- /dev/null
+++ b/source/game/ai_bfp.c
@@ -0,0 +1,900 @@
+/*
+===========================================================================
+
+BFP BOT AI
+
+===========================================================================
+*/
+
+
+#include "g_local.h"
+#include "botlib.h"
+#include "be_aas.h"
+#include "be_ea.h"
+#include "be_ai_char.h"
+#include "be_ai_chat.h"
+#include "be_ai_gen.h"
+#include "be_ai_goal.h"
+#include "be_ai_move.h"
+#include "be_ai_weap.h"
+//
+#include "ai_main.h"
+#include "ai_dmq3.h"
+#include "ai_chat.h"
+#include "ai_cmd.h"
+#include "ai_dmnet.h"
+#include "ai_team.h"
+#include "ai_bfp.h"
+//
+#include "chars.h"
+#include "inv.h"
+#include "syn.h"
+#include "match.h"
+
+// thresholds for bot behavior
+#define BFP_BOT_KI_LOW_PCT 0.057f
+#define BFP_BOT_KI_SAFE_PCT 0.705f
+#define BFP_BOT_KI_BOOST_MIN_PCT 0.05f
+#define BFP_BOT_FLIGHT_RANGE 700.0f
+#define BFP_BOT_MELEE_RANGE_PAD 10.0f
+#define BFP_BOT_WEAPON_SWITCH_MINTIME 1.0f
+#define BFP_BOT_WEAPON_SWITCH_MAXTIME 3.0f
+#define BFP_BOT_KI_CHARGE_DANGER_RANGE 100.0f
+#define BFP_BOT_MELEE_REPRESS_DELAY 0.25f
+#define BFP_BOT_MELEE_STANCE_HOLD 2.0f
+#define BFP_BOT_MELEE_STANCE_RANGE_BONUS 40.0f
+#define BFP_BOT_CHASE_TRIGGER_RANGE 1520.0f
+#define BFP_BOT_CHASE_DURATION 5.25f
+#define BFP_BOT_ZANZOKEN_PRESS_MS 120
+#define BFP_BOT_ZANZOKEN_RELEASE_MS 40
+#define BFP_BOT_ZANZOKEN_DANGER_RANGE 300.0f
+#define BFP_BOT_ZANZOKEN_HITSTUN_CHANCE 0.55f
+#define BFP_BOT_ZANZOKEN_HITSTUN_RETRY_MS 400 // how often, in ms, the bot re-rolls its zanzoken-escape chance while hitstun is active - lets it try more than once during a ~3s stun instead of a single roll at the start
+#define BFP_BOT_FIELD_OF_VIEW 180.0f // very visible, even behind
+#define BFP_BOT_SIXTHSENSE_RANGE 16000.0f
+
+static const float bfpFlightChanceBySkill[6] = {
+ 0.26f, // skill 0
+ 0.34f, // skill 1
+ 0.42f, // skill 2
+ 0.50f, // skill 3
+ 0.61f, // skill 4
+ 0.74f // skill 5
+};
+
+// BFP - TODO: Just a test, replace for bfp_weapon.cfg
+static const int bfpAttackSlots[] = {
+ WP_MACHINEGUN,
+ WP_SHOTGUN,
+ WP_GRENADE_LAUNCHER,
+ WP_ROCKET_LAUNCHER,
+ WP_GRAPPLING_HOOK
+};
+#define BFP_NUM_ATTACK_SLOTS ( sizeof(bfpAttackSlots) / sizeof(bfpAttackSlots[0]) )
+
+typedef struct {
+ int weaponNum;
+ int attackType;
+ qboolean chargeAttack;
+ qboolean chargeAutoFire;
+} bfpWeaponProfile_t;
+
+static const bfpWeaponProfile_t bfpWeaponProfiles[] = {
+ { WP_MACHINEGUN, ATK_HITSCAN, qfalse, qfalse },
+ { WP_SHOTGUN, ATK_FORCEFIELD, qtrue, qtrue },
+ { WP_GRENADE_LAUNCHER, ATK_MISSILE, qfalse, qfalse },
+ { WP_ROCKET_LAUNCHER, ATK_MISSILE, qfalse, qfalse },
+ { WP_GRAPPLING_HOOK, ATK_BEAM, qtrue, qfalse }
+};
+
+/*
+==================
+BotBFPGetWeaponProfile
+==================
+*/
+static const bfpWeaponProfile_t *BotBFPGetWeaponProfile( int weaponNum ) {
+ int i;
+ for ( i = 0; i < (int)( sizeof(bfpWeaponProfiles) / sizeof(bfpWeaponProfiles[0]) ); i++ ) {
+ if ( bfpWeaponProfiles[i].weaponNum == weaponNum ) {
+ return &bfpWeaponProfiles[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+==================
+BotBFPResetState
+==================
+*/
+void BotBFPResetState( bot_state_t *bs ) {
+ bs->bfpButtons = 0;
+ bs->bfpKiRecharging = qfalse;
+ bs->bfpKiRechargeInterrupted_time = 0;
+ bs->bfpLastHealth = -1;
+ bs->bfpLastMelee_time = 0;
+ bs->bfpStrafeFlip_time = 0;
+ bs->bfpFlightDecision = qfalse;
+ bs->bfpFlightReroll_time = 0;
+ bs->bfpZanzokenPhase = 0;
+ bs->bfpZanzokenPhaseEnd_time = 0;
+ bs->bfpZanzokenDir = 0;
+ bs->bfpZanzokenHitstunRetry_time = 0;
+ bs->bfpRightmoveOverride = 0;
+ bs->bfpRightmoveOverrideActive = qfalse;
+ bs->bfpForceAttackOff = qfalse;
+ bs->bfpKiCompensate_time = 0;
+ bs->bfpSixthSenseStep_time = 0;
+ bs->bfpEvadeTime = 0;
+ VectorClear( bs->bfpEvadeDir );
+
+ // ensure a valid weapon is selected even before any combat logic runs
+ if ( bs->weaponnum <= WP_NONE ) {
+ bs->weaponnum = WP_GAUNTLET;
+ }
+}
+
+/*
+==================
+BotBFPApplyButtons
+==================
+*/
+void BotBFPApplyButtons( bot_state_t *bs, usercmd_t *ucmd ) {
+ ucmd->buttons |= bs->bfpButtons;
+ if ( bs->bfpForceAttackOff ) {
+ ucmd->buttons &= ~BUTTON_ATTACK;
+ }
+ if ( bs->bfpRightmoveOverrideActive ) {
+ ucmd->rightmove = bs->bfpRightmoveOverride;
+ }
+}
+
+/*
+==================
+BotBFPWantsToMelee
+==================
+*/
+static qboolean BotBFPWantsToMelee( bot_state_t *bs, aas_entityinfo_t *entinfo ) {
+ float rangeMultiplier, distance;
+ vec3_t dir;
+
+ if ( bs->cur_ps.stats[STAT_HITSTUN_TIME] > 0 ) {
+ return qfalse;
+ }
+
+ VectorSubtract( entinfo->origin, bs->origin, dir );
+ distance = VectorLength( dir );
+
+ rangeMultiplier = g_meleeRange.integer + 45 - BFP_BOT_MELEE_RANGE_PAD;
+ if ( bs->cur_ps.eFlags & EF_MONSTER ) {
+ rangeMultiplier *= 2.5f;
+ }
+
+ if ( bs->bfpLastMelee_time > FloatTime() - BFP_BOT_MELEE_STANCE_HOLD ) {
+ rangeMultiplier += BFP_BOT_MELEE_STANCE_RANGE_BONUS;
+ }
+
+ if ( distance > rangeMultiplier ) {
+ return qfalse;
+ }
+
+ if ( !BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, BFP_BOT_FIELD_OF_VIEW, bs->enemy ) ) {
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+/*
+==================
+BotBFPCheckMelee
+==================
+*/
+static void BotBFPCheckMelee( bot_state_t *bs, aas_entityinfo_t *entinfo ) {
+ if ( !BotBFPWantsToMelee( bs, entinfo ) ) {
+ return;
+ }
+ if ( bs->bfpLastMelee_time > FloatTime() - BFP_BOT_MELEE_REPRESS_DELAY ) {
+ return;
+ }
+ bs->bfpButtons |= BUTTON_MELEE;
+ bs->bfpLastMelee_time = FloatTime();
+}
+
+/*
+==================
+BotBFPCompensateKiCharge
+==================
+*/
+static void BotBFPCompensateKiCharge( bot_state_t *bs ) {
+ gentity_t *botent;
+ playerState_t *ps;
+ int maxKi;
+ const float HUMAN_CMD_INTERVAL = 0.017f; // 17 ms
+ float now, elapsed, kiPerSecond, kiChargeTotal;
+
+ botent = &g_entities[bs->client];
+ if ( !botent->inuse || !botent->client ) {
+ return;
+ }
+ ps = &botent->client->ps;
+ now = FloatTime();
+
+ if ( !( ( ps->pm_flags & PMF_KI_CHARGE ) && ( ps->eFlags & EF_AURA ) ) ) {
+ bs->bfpKiCompensate_time = now;
+ return;
+ }
+
+ if ( ps->stats[STAT_HITSTUN_TIME] > 0 ) {
+ bs->bfpKiCompensate_time = now;
+ return;
+ }
+
+ maxKi = ps->stats[STAT_MAX_KI];
+ if ( ps->stats[STAT_KI] >= maxKi ) {
+ bs->bfpKiCompensate_time = now;
+ return;
+ }
+
+ if ( bs->bfpKiCompensate_time <= 0 ) {
+ bs->bfpKiCompensate_time = now;
+ return;
+ }
+
+ elapsed = now - bs->bfpKiCompensate_time;
+ if ( elapsed <= 0 ) {
+ return;
+ }
+ if ( elapsed > 2.0f ) {
+ elapsed = 2.0f;
+ }
+
+ kiChargeTotal = ( g_kiCharge.value * 0.01f ) + g_kiChargePct.value * ( maxKi * 0.0001f );
+ kiPerSecond = kiChargeTotal / HUMAN_CMD_INTERVAL;
+ ps->stats[STAT_KI] += (int)( kiPerSecond * elapsed );
+ if ( ps->stats[STAT_KI] > maxKi ) {
+ ps->stats[STAT_KI] = maxKi;
+ }
+
+ bs->bfpKiCompensate_time = now;
+}
+
+/*
+==================
+BotBFPCheckKiRecharge
+==================
+*/
+static void BotBFPCheckKiRecharge( bot_state_t *bs, aas_entityinfo_t *entinfo ) {
+ int maxKi, curKi, health;
+ vec3_t dir;
+ float enemyDist;
+ qboolean enemyDanger, justHit;
+
+ maxKi = bs->cur_ps.stats[STAT_MAX_KI];
+ curKi = bs->cur_ps.stats[STAT_KI];
+ health = bs->inventory[INVENTORY_HEALTH];
+
+ if ( maxKi <= 0 ) {
+ return;
+ }
+
+ enemyDanger = qfalse;
+ if ( entinfo != NULL ) {
+ VectorSubtract( entinfo->origin, bs->origin, dir );
+ enemyDist = VectorLength( dir );
+ enemyDanger = ( enemyDist < BFP_BOT_KI_CHARGE_DANGER_RANGE );
+ }
+
+ if ( bs->bfpLastHealth < 0 ) {
+ bs->bfpLastHealth = health;
+ }
+ justHit = ( health < bs->bfpLastHealth );
+ bs->bfpLastHealth = health;
+
+ if ( ( justHit || enemyDanger ) && bs->bfpKiRecharging ) {
+ bs->bfpKiRecharging = qfalse;
+ bs->bfpKiRechargeInterrupted_time = FloatTime();
+ return;
+ }
+
+ if ( bs->bfpKiRechargeInterrupted_time > FloatTime() - 1.5 ) {
+ return;
+ }
+
+ if ( enemyDanger ) {
+ return;
+ }
+
+ if ( !bs->bfpKiRecharging ) {
+ if ( curKi < maxKi * BFP_BOT_KI_LOW_PCT ) {
+ bs->bfpKiRecharging = qtrue;
+ }
+ } else {
+ if ( curKi >= maxKi * BFP_BOT_KI_SAFE_PCT ) {
+ bs->bfpKiRecharging = qfalse;
+ }
+ }
+
+ if ( bs->bfpKiRecharging ) {
+ bs->bfpButtons |= BUTTON_KI_CHARGE;
+ }
+}
+
+/*
+==================
+BotBFPEnemyAirborne
+==================
+*/
+static qboolean BotBFPEnemyAirborne( aas_entityinfo_t *entinfo ) {
+ bsp_trace_t trace;
+ vec3_t end;
+
+ VectorCopy( entinfo->origin, end );
+ end[2] -= 48;
+ BotAI_Trace( &trace, entinfo->origin, entinfo->mins, entinfo->maxs, end, -1, MASK_SOLID );
+ return ( trace.fraction >= 1.0 );
+}
+
+/*
+==================
+BotBFPCheckFlight
+==================
+*/
+static void BotBFPCheckFlight( bot_state_t *bs, aas_entityinfo_t *entinfo, bot_goal_t *goal ) {
+ int skillIndex;
+ float flightChance, dist, stopChance;
+ vec3_t dir;
+ qboolean wantsFlight, alreadyFlying;
+
+ if ( bs->cur_ps.stats[STAT_KI] <= 0 ) {
+ bs->bfpFlightDecision = qfalse;
+ return;
+ }
+
+ if ( bs->cur_ps.pm_flags & PMF_KI_CHARGE ) {
+ return;
+ }
+
+ skillIndex = (int)bs->settings.skill;
+ if ( skillIndex < 0 ) {
+ skillIndex = 0;
+ }
+ if ( skillIndex > 5 ) {
+ skillIndex = 5;
+ }
+ flightChance = bfpFlightChanceBySkill[skillIndex];
+
+ alreadyFlying = ( bs->cur_ps.eFlags & EF_FLIGHT );
+
+ VectorSubtract( entinfo->origin, bs->origin, dir );
+ dist = VectorLength( dir );
+
+ wantsFlight = qfalse;
+ if ( entinfo != NULL ) { // combat
+ VectorSubtract( entinfo->origin, bs->origin, dir );
+ dist = VectorLength( dir );
+ if ( BotBFPEnemyAirborne( entinfo ) ) {
+ wantsFlight = qtrue;
+ } else if ( dist > BFP_BOT_FLIGHT_RANGE ) {
+ wantsFlight = qtrue;
+ }
+ } else if ( goal != NULL ) { // CTF
+ VectorSubtract( goal->origin, bs->origin, dir );
+ dist = VectorLength( dir );
+ if ( dist > BFP_BOT_FLIGHT_RANGE ) {
+ wantsFlight = qtrue;
+ }
+ } else {
+ return;
+ }
+
+ if ( bs->bfpFlightReroll_time < FloatTime() ) {
+ float windowMin = 1.0f + ( skillIndex / 5.0f ) * 1.0f;
+ float windowMax = 2.0f + ( skillIndex / 5.0f ) * 2.0f;
+ float window = windowMin + random() * ( windowMax - windowMin );
+
+ if ( !alreadyFlying ) {
+ bs->bfpFlightDecision = ( wantsFlight && random() < flightChance );
+ } else {
+ int maxKi = bs->cur_ps.stats[STAT_MAX_KI];
+ int curKi = bs->cur_ps.stats[STAT_KI];
+
+ stopChance = 0.6f - ( (float)skillIndex / 5.0f ) * 0.55f;
+ if ( stopChance < 0.05f ) {
+ stopChance = 0.05f;
+ }
+ if ( stopChance > 0.6f ) {
+ stopChance = 0.6f;
+ }
+ if ( maxKi > 0 && curKi < maxKi * 0.2f ) {
+ stopChance += 0.2f;
+ if ( stopChance > 0.8f ) {
+ stopChance = 0.8f;
+ }
+ }
+
+ if ( wantsFlight ) {
+ bs->bfpFlightDecision = ( random() > stopChance * 0.2f );
+ } else {
+ bs->bfpFlightDecision = ( random() > stopChance );
+ }
+ }
+ bs->bfpFlightReroll_time = FloatTime() + window;
+ }
+
+ if ( bs->bfpFlightDecision != bs->bfpLastFlightDecision ) {
+ bs->bfpButtons |= BUTTON_ENABLEFLIGHT;
+ bs->bfpLastFlightDecision = bs->bfpFlightDecision;
+ }
+}
+
+/*
+==================
+BotBFPCheckKiBoost
+==================
+*/
+static void BotBFPCheckKiBoost( bot_state_t *bs ) {
+ int maxKi = bs->cur_ps.stats[STAT_MAX_KI];
+ int curKi = bs->cur_ps.stats[STAT_KI];
+ float skill = bs->settings.skill;
+
+ if ( maxKi <= 0 || curKi <= 0 ) {
+ return;
+ }
+
+ if ( bs->bfpKiRecharging ) {
+ return;
+ }
+
+ if ( curKi < maxKi * BFP_BOT_KI_BOOST_MIN_PCT ) {
+ return;
+ }
+
+ if ( bs->cur_ps.stats[STAT_HITSTUN_TIME] > 0 ) {
+ return;
+ }
+
+ if ( bs->cur_ps.weaponstate == WEAPON_BEAMSTRUGGLE ) {
+ if ( random() < ( 0.3f + 0.06f * skill ) ) {
+ bs->bfpButtons |= BUTTON_KI_USE;
+ }
+ return;
+ }
+
+ if ( random() < ( 0.7f + 0.06f * skill ) ) {
+ bs->bfpButtons |= BUTTON_KI_USE;
+ }
+}
+
+/*
+==================
+BotBFPCheckWeaponSlot
+==================
+*/
+static void BotBFPCheckWeaponSlot( bot_state_t *bs ) {
+ int weaponBits;
+ int available[BFP_NUM_ATTACK_SLOTS];
+ int numAvailable;
+ int i, w;
+ int pick;
+
+ if ( bs->cur_ps.weaponstate == WEAPON_RAISING
+ || bs->cur_ps.weaponstate == WEAPON_DROPPING ) {
+ return;
+ }
+
+ if ( bs->weaponnum <= WP_NONE ) {
+ bs->weaponnum = WP_GAUNTLET;
+ trap_EA_SelectWeapon( bs->client, bs->weaponnum );
+ }
+
+ if ( bs->cur_ps.weaponstate == WEAPON_FIRING
+ || bs->cur_ps.weaponstate == WEAPON_ACTIVE
+ || bs->cur_ps.weaponstate == WEAPON_BEAMSTRUGGLE ) {
+ return;
+ }
+
+ weaponBits = bs->cur_ps.stats[STAT_WEAPONS];
+
+ numAvailable = 0;
+ for ( i = 0; i < (int)BFP_NUM_ATTACK_SLOTS; i++ ) {
+ w = bfpAttackSlots[i];
+ if ( weaponBits & ( 1 << w ) ) {
+ available[numAvailable++] = w;
+ }
+ }
+
+ if ( numAvailable <= 1 ) {
+ if ( numAvailable == 1 && bs->weaponnum != available[0] ) {
+ bs->weaponnum = available[0];
+ trap_EA_SelectWeapon( bs->client, bs->weaponnum );
+ }
+ return;
+ }
+
+ if ( bs->weaponchange_time > FloatTime() - BFP_BOT_WEAPON_SWITCH_MINTIME ) {
+ return;
+ }
+ if ( bs->weaponchange_time > FloatTime() - BFP_BOT_WEAPON_SWITCH_MAXTIME
+ && random() > 0.65f ) {
+ return;
+ }
+
+ pick = available[0];
+ for ( i = 0; i < numAvailable; i++ ) {
+ if ( available[i] == bs->weaponnum ) {
+ pick = available[ ( i + 1 ) % numAvailable ];
+ break;
+ }
+ }
+
+ if ( pick != bs->weaponnum ) {
+ bs->weaponnum = pick;
+ bs->weaponchange_time = FloatTime();
+ trap_EA_SelectWeapon( bs->client, bs->weaponnum );
+ }
+}
+
+/*
+==================
+BotBFPCheckChargedAttack
+==================
+*/
+static void BotBFPCheckChargedAttack( bot_state_t *bs, aas_entityinfo_t *entinfo ) {
+ const bfpWeaponProfile_t *profile;
+ qboolean lineOfFireClear;
+ bsp_trace_t trace;
+
+ bs->bfpForceAttackOff = qfalse;
+
+ profile = BotBFPGetWeaponProfile( bs->weaponnum );
+ if ( !profile ) {
+ return;
+ }
+
+ if ( profile->chargeAutoFire ) {
+ if ( bs->cur_ps.weaponstate == WEAPON_FIRING || bs->cur_ps.weaponstate == WEAPON_ACTIVE ) {
+ if ( bs->bfpChargeAutoFireStartTime == 0 ) {
+ bs->bfpChargeAutoFireStartTime = FloatTime();
+ }
+ if ( FloatTime() - bs->bfpChargeAutoFireStartTime < 4.0f ) {
+ bs->bfpButtons |= BUTTON_ATTACK;
+ } else {
+ bs->bfpButtons &= ~BUTTON_ATTACK;
+ bs->bfpForceAttackOff = qtrue;
+ bs->bfpChargeAutoFireStartTime = 0;
+ }
+ } else {
+ bs->bfpChargeAutoFireStartTime = 0;
+ }
+ }
+
+ if ( bs->cur_ps.weaponstate == WEAPON_FIRING && profile->chargeAttack ) {
+ if ( bs->bfpButtons & BUTTON_KI_CHARGE ) {
+ return;
+ }
+
+ bs->bfpButtons |= BUTTON_ATTACK;
+
+ if ( bs->cur_ps.eFlags & EF_READY_KI_ATTACK ) {
+ BotAI_Trace( &trace, bs->eye, NULL, NULL, bs->aimtarget, bs->client,
+ CONTENTS_SOLID | CONTENTS_PLAYERCLIP );
+ lineOfFireClear = ( trace.fraction >= 1.0f || trace.ent == bs->enemy );
+
+ if ( lineOfFireClear ) {
+ bs->bfpForceAttackOff = qtrue;
+ }
+ }
+ return;
+ }
+
+ if ( profile->attackType == ATK_SBEAM ) {
+ if ( bs->cur_ps.weaponstate == WEAPON_READY || bs->cur_ps.weaponstate == WEAPON_ACTIVE ) {
+ bs->bfpButtons |= BUTTON_ATTACK;
+ bs->bfpForceAttackOff = qfalse;
+ }
+ if ( bs->cur_ps.weaponstate == WEAPON_READY && bs->bfpLastWeaponState == WEAPON_ACTIVE ) {
+ bs->bfpForceAttackOff = qfalse;
+ bs->bfpButtons |= BUTTON_ATTACK;
+ }
+ bs->bfpLastWeaponState = bs->cur_ps.weaponstate;
+ }
+
+ if ( bs->cur_ps.weaponstate == WEAPON_ACTIVE && profile->attackType == ATK_BEAM ) {
+ bs->bfpForceAttackOff = qtrue;
+ return;
+ }
+
+ if ( bs->cur_ps.weaponstate == WEAPON_BEAMSTRUGGLE && profile->attackType == ATK_BEAM ) {
+ bs->bfpForceAttackOff = qtrue;
+ return;
+ }
+}
+
+/*
+==================
+BotBFPCheckChase
+==================
+*/
+static void BotBFPCheckChase( bot_state_t *bs, aas_entityinfo_t *entinfo, qboolean forceMelee ) {
+ vec3_t dir, forward, up = { 0, 0, 1 }, sideward;
+ float dist;
+ qboolean visible;
+ float meleeStrafeRange = g_meleeDiveRange.value + 130;
+
+ // evasion
+ if ( bs->bfpEvadeTime > FloatTime() ) {
+ if ( VectorLength( bs->bfpEvadeDir ) > 0.1f ) {
+ trap_BotMoveInDirection( bs->ms, bs->bfpEvadeDir, 400, MOVE_WALK );
+ }
+ return;
+ }
+
+ VectorSubtract( entinfo->origin, bs->origin, dir );
+ dist = VectorLength( dir );
+
+ visible = BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, BFP_BOT_FIELD_OF_VIEW, bs->enemy );
+
+ if ( visible && dist <= meleeStrafeRange ) {
+ bs->bfpButtons |= BUTTON_MELEE;
+ }
+
+ if ( forceMelee ) {
+ if ( visible ) {
+ bs->attackchase_time = FloatTime() + 9999.0f;
+ dir[2] = 0;
+ if ( VectorNormalize( dir ) > 0.1f ) {
+ trap_BotMoveInDirection( bs->ms, dir, 400, MOVE_WALK );
+ }
+ }
+ }
+
+ if ( !forceMelee && visible && dist > BFP_BOT_CHASE_TRIGGER_RANGE ) {
+ bs->attackchase_time = FloatTime() + BFP_BOT_CHASE_DURATION;
+ return;
+ }
+
+ if ( visible && dist < meleeStrafeRange && bs->attackchase_time <= FloatTime() ) {
+ VectorCopy( dir, forward );
+ VectorNormalize( forward );
+ CrossProduct( forward, up, sideward );
+ if ( bs->flags & BFL_STRAFERIGHT ) {
+ VectorNegate( sideward, sideward );
+ }
+ trap_BotMoveInDirection( bs->ms, sideward, 400, MOVE_WALK );
+ if ( bs->bfpStrafeFlip_time < FloatTime() ) {
+ bs->flags ^= BFL_STRAFERIGHT;
+ bs->bfpStrafeFlip_time = FloatTime() + 0.5f + random() * 0.4f;
+ }
+ }
+}
+
+/*
+==================
+BotBFPCheckZanzoken
+==================
+*/
+static void BotBFPCheckZanzoken( bot_state_t *bs, aas_entityinfo_t *entinfo ) {
+ float dist;
+ vec3_t dir;
+ qboolean wantsZanzoken;
+
+ if ( bs->bfpZanzokenPhase != 0 ) {
+ bs->bfpRightmoveOverrideActive = qtrue;
+ switch ( bs->bfpZanzokenPhase ) {
+ case 1:
+ bs->bfpRightmoveOverride = ( bs->bfpZanzokenDir > 0 ) ? 127 : -127;
+ if ( FloatTime() >= bs->bfpZanzokenPhaseEnd_time ) {
+ bs->bfpZanzokenPhase = 2;
+ bs->bfpZanzokenPhaseEnd_time = FloatTime() + BFP_BOT_ZANZOKEN_RELEASE_MS * 0.001;
+ }
+ break;
+ case 2:
+ bs->bfpRightmoveOverride = 0;
+ if ( FloatTime() >= bs->bfpZanzokenPhaseEnd_time ) {
+ bs->bfpZanzokenPhase = 3;
+ bs->bfpZanzokenPhaseEnd_time = FloatTime() + BFP_BOT_ZANZOKEN_PRESS_MS * 0.001;
+ }
+ break;
+ case 3:
+ bs->bfpRightmoveOverride = ( bs->bfpZanzokenDir > 0 ) ? 127 : -127;
+ if ( FloatTime() >= bs->bfpZanzokenPhaseEnd_time ) {
+ bs->bfpZanzokenPhase = 0;
+ bs->bfpRightmoveOverrideActive = qfalse;
+ }
+ break;
+ }
+ return;
+ }
+
+ wantsZanzoken = qfalse;
+
+ if ( bs->cur_ps.stats[STAT_HITSTUN_TIME] > 0
+ && bs->cur_ps.stats[STAT_HITSTUN_TIME] <= 3000 ) {
+ if ( bs->bfpZanzokenHitstunRetry_time <= FloatTime() ) {
+ bs->bfpZanzokenHitstunRetry_time = FloatTime() + BFP_BOT_ZANZOKEN_HITSTUN_RETRY_MS * 0.001;
+ if ( random() < BFP_BOT_ZANZOKEN_HITSTUN_CHANCE ) {
+ wantsZanzoken = qtrue;
+ }
+ }
+ }
+
+ if ( bs->cur_ps.stats[STAT_HITSTUN_TIME] <= 0 ) {
+ bs->bfpZanzokenHitstunRetry_time = 0;
+ }
+
+ if ( !wantsZanzoken ) {
+ if ( bs->cur_ps.weaponstate == WEAPON_ACTIVE
+ || bs->cur_ps.weaponstate == WEAPON_BEAMSTRUGGLE
+ || bs->cur_ps.weaponstate == WEAPON_STUN ) {
+ return;
+ }
+ if ( bs->cur_ps.pm_flags & ( PMF_KI_CHARGE | PMF_ULTIMATE_TIER ) ) {
+ return;
+ }
+ if ( bs->cur_ps.stats[STAT_KI] <= ( bs->cur_ps.stats[STAT_MAX_KI] * 0.05f ) ) {
+ return;
+ }
+
+ if ( entinfo != NULL ) {
+ VectorSubtract( entinfo->origin, bs->origin, dir );
+ dist = VectorLength( dir );
+ if ( dist < BFP_BOT_ZANZOKEN_DANGER_RANGE
+ && BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, BFP_BOT_FIELD_OF_VIEW, bs->enemy ) ) {
+ if ( random() < 0.09f ) {
+ wantsZanzoken = qtrue;
+ }
+ }
+ }
+ }
+
+ if ( wantsZanzoken ) {
+ bs->bfpZanzokenDir = ( random() < 0.5f ) ? -1 : 1;
+ bs->bfpZanzokenPhase = 1;
+ bs->bfpZanzokenPhaseEnd_time = FloatTime() + BFP_BOT_ZANZOKEN_PRESS_MS * 0.001;
+ bs->bfpRightmoveOverrideActive = qtrue;
+ bs->bfpRightmoveOverride = ( bs->bfpZanzokenDir > 0 ) ? 127 : -127;
+ }
+}
+
+/*
+==================
+BotBFPBeginFrame
+==================
+*/
+void BotBFPBeginFrame( bot_state_t *bs ) {
+ bs->bfpButtons = 0;
+ bs->bfpForceAttackOff = qfalse;
+}
+
+/*
+==================
+BotBFPCheckSixthSense
+==================
+*/
+static void BotBFPCheckSixthSense( bot_state_t *bs ) {
+ int i;
+ vec3_t dir, angles;
+ float squaredist;
+ aas_entityinfo_t entinfo;
+
+ for ( i = 0; i < level.maxclients; i++ ) {
+ if ( i == bs->entitynum ) {
+ continue;
+ }
+ if ( BotSameTeam( bs, i ) ) {
+ continue;
+ }
+
+ BotEntityInfo( i, &entinfo );
+ if ( !entinfo.valid ) {
+ continue;
+ }
+ if ( EntityIsDead( &entinfo ) ) {
+ continue;
+ }
+
+ VectorSubtract( entinfo.origin, bs->origin, dir );
+ squaredist = VectorLengthSquared( dir );
+ if ( squaredist > Square( BFP_BOT_SIXTHSENSE_RANGE ) ) {
+ continue;
+ }
+
+ vectoangles( dir, angles );
+ if ( InFieldOfVision( bs->viewangles, BFP_BOT_FIELD_OF_VIEW, angles ) ) {
+ continue;
+ }
+
+ VectorCopy( angles, bs->ideal_viewangles );
+ bs->flags |= BFL_IDEALVIEWSET;
+
+ if ( bs->bfpSixthSenseStep_time < FloatTime() ) {
+ vec3_t away;
+ VectorNormalize( dir );
+ VectorNegate( dir, away );
+ away[2] = 0;
+ if ( VectorNormalize( away ) > 0.1f ) {
+ trap_BotMoveInDirection( bs->ms, away, 300, MOVE_WALK );
+ }
+ bs->bfpSixthSenseStep_time = FloatTime() + 0.4f;
+ }
+
+ return;
+ }
+}
+
+/*
+==================
+BotBFPUniversalCheck
+==================
+*/
+void BotBFPUniversalCheck( bot_state_t *bs ) {
+ if ( bs->enemy < 0 ) {
+ if ( gametype == GT_CTF ) {
+ bot_goal_t goal;
+ if ( trap_BotGetTopGoal( bs->gs, &goal ) ) {
+ BotBFPCheckFlight( bs, NULL, &goal );
+ BotBFPCheckKiBoost( bs );
+ }
+ }
+ BotBFPCheckZanzoken( bs, NULL );
+ BotBFPCheckKiRecharge( bs, NULL );
+ BotBFPCompensateKiCharge( bs );
+ BotBFPCheckSixthSense( bs );
+ }
+}
+
+/*
+==================
+BotBFPCombatAI
+==================
+*/
+void BotBFPCombatAI( bot_state_t *bs ) {
+ aas_entityinfo_t entinfo;
+ int currentHealth = bs->inventory[INVENTORY_HEALTH];
+
+ if ( bs->enemy < 0 ) {
+ bs->bfpKiRecharging = qfalse;
+ bs->bfpFlightDecision = qfalse;
+ return;
+ }
+
+ BotEntityInfo( bs->enemy, &entinfo );
+
+ BotBFPCheckMelee( bs, &entinfo );
+ BotBFPCheckKiRecharge( bs, &entinfo );
+ BotBFPCompensateKiCharge( bs );
+ BotBFPCheckFlight( bs, &entinfo, NULL );
+ BotBFPCheckKiBoost( bs );
+
+ // use evasion after receiving damage
+ if ( bs->bfpLastHealth < 0 ) {
+ bs->bfpLastHealth = currentHealth;
+ }
+ if ( currentHealth < bs->bfpLastHealth && bs->bfpEvadeTime < FloatTime() ) {
+ vec3_t right, up = { 0, 0, 1 };
+ vec3_t dirToEnemy;
+ VectorSubtract( entinfo.origin, bs->origin, dirToEnemy );
+ VectorNormalize( dirToEnemy );
+ CrossProduct( dirToEnemy, up, right );
+ VectorNormalize( right );
+ if ( random() < 0.5f ) {
+ VectorScale( right, ( random() < 0.5f ) ? 1 : -1, bs->bfpEvadeDir );
+ } else {
+ VectorScale( dirToEnemy, -1, bs->bfpEvadeDir );
+ }
+ bs->bfpEvadeDir[2] += ( random() - 0.5f ) * 0.2f;
+ VectorNormalize( bs->bfpEvadeDir );
+ bs->bfpEvadeTime = FloatTime() + 0.5f + random() * 0.3f;
+ }
+ bs->bfpLastHealth = currentHealth;
+
+ // don't use weapons on melee only
+ if ( g_meleeOnly.integer <= 0 ) {
+ BotBFPCheckWeaponSlot( bs );
+ BotBFPCheckChargedAttack( bs, &entinfo );
+ }
+
+ BotBFPCheckChase( bs, &entinfo, ( g_meleeOnly.integer > 0 ) );
+ BotBFPCheckZanzoken( bs, &entinfo );
+}
diff --git a/source/game/ai_bfp.h b/source/game/ai_bfp.h
new file mode 100644
index 0000000..40d5634
--- /dev/null
+++ b/source/game/ai_bfp.h
@@ -0,0 +1,13 @@
+/*
+===========================================================================
+
+BFP BOT AI
+
+===========================================================================
+*/
+
+void BotBFPCombatAI( bot_state_t *bs );
+void BotBFPBeginFrame( bot_state_t *bs );
+void BotBFPApplyButtons( bot_state_t *bs, usercmd_t *ucmd );
+void BotBFPUniversalCheck( bot_state_t *bs );
+void BotBFPResetState( bot_state_t *bs );
diff --git a/source/game/ai_chat.c b/source/game/ai_chat.c
index 9618af4..8c26f18 100644
--- a/source/game/ai_chat.c
+++ b/source/game/ai_chat.c
@@ -63,16 +63,12 @@ BotNumActivePlayers
int BotNumActivePlayers(void) {
int i, num;
char buf[MAX_INFO_STRING];
- static int maxclients;
-
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
num = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
@@ -89,17 +85,13 @@ BotIsFirstInRankings
int BotIsFirstInRankings(bot_state_t *bs) {
int i, score;
char buf[MAX_INFO_STRING];
- static int maxclients;
playerState_t ps;
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
-
score = bs->cur_ps.persistant[PERS_SCORE];
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
@@ -117,17 +109,13 @@ BotIsLastInRankings
int BotIsLastInRankings(bot_state_t *bs) {
int i, score;
char buf[MAX_INFO_STRING];
- static int maxclients;
playerState_t ps;
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
-
score = bs->cur_ps.persistant[PERS_SCORE];
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
@@ -146,18 +134,14 @@ char *BotFirstClientInRankings(void) {
int i, bestscore, bestclient;
char buf[MAX_INFO_STRING];
static char name[32];
- static int maxclients;
playerState_t ps;
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
-
bestscore = -999999;
bestclient = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
@@ -167,10 +151,11 @@ char *BotFirstClientInRankings(void) {
bestclient = i;
}
}
- EasyClientName(bestclient, name, 32);
+ EasyClientName( bestclient, name, sizeof( name ) );
return name;
}
+
/*
==================
BotLastClientInRankings
@@ -180,18 +165,14 @@ char *BotLastClientInRankings(void) {
int i, worstscore, bestclient;
char buf[MAX_INFO_STRING];
static char name[32];
- static int maxclients;
playerState_t ps;
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
-
worstscore = 999999;
bestclient = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
@@ -201,10 +182,11 @@ char *BotLastClientInRankings(void) {
bestclient = i;
}
}
- EasyClientName(bestclient, name, 32);
+ EasyClientName( bestclient, name, sizeof( name ) );
return name;
}
+
/*
==================
BotRandomOpponentName
@@ -214,20 +196,16 @@ char *BotRandomOpponentName(bot_state_t *bs) {
int i, count;
char buf[MAX_INFO_STRING];
int opponents[MAX_CLIENTS], numopponents;
- static int maxclients;
static char name[32];
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
-
numopponents = 0;
opponents[0] = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (i == bs->client) continue;
//
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//skip team mates
@@ -248,21 +226,14 @@ char *BotRandomOpponentName(bot_state_t *bs) {
return name;
}
+
/*
==================
BotMapTitle
==================
*/
-
-char *BotMapTitle(void) {
- char info[1024];
- static char mapname[128];
-
- trap_GetServerinfo(info, sizeof(info));
-
- strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1);
- mapname[sizeof(mapname)-1] = '\0';
-
+static const char *BotMapTitle( void )
+{
return mapname;
}
@@ -272,7 +243,6 @@ char *BotMapTitle(void) {
BotWeaponNameForMeansOfDeath
==================
*/
-
char *BotWeaponNameForMeansOfDeath(int mod) {
switch(mod) {
case MOD_SHOTGUN: return "Shotgun";
@@ -289,6 +259,7 @@ char *BotWeaponNameForMeansOfDeath(int mod) {
case MOD_BFG:
case MOD_BFG_SPLASH: return "BFG10K";
case MOD_GRAPPLE: return "Grapple";
+ case MOD_KI_ATTACK: return "Ki Blast";
default: return "[unknown weapon]";
}
}
@@ -359,6 +330,7 @@ int BotValidChatPosition(bot_state_t *bs) {
if (BotIsDead(bs)) return qtrue;
//never start chatting with a powerup
if (bs->inventory[INVENTORY_QUAD] ||
+ bs->inventory[INVENTORY_ENVIRONMENTSUIT] ||
bs->inventory[INVENTORY_HASTE] ||
bs->inventory[INVENTORY_INVISIBILITY] ||
bs->inventory[INVENTORY_REGEN] ||
@@ -469,7 +441,9 @@ int BotChat_StartLevel(bot_state_t *bs) {
return qfalse;
}
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1);
if (!bot_fastchat.integer) {
if (random() > rnd) return qfalse;
@@ -504,7 +478,9 @@ int BotChat_EndLevel(bot_state_t *bs) {
return qtrue;
}
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1);
if (!bot_fastchat.integer) {
if (random() > rnd) return qfalse;
@@ -556,7 +532,9 @@ int BotChat_Death(bot_state_t *bs) {
if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1);
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
//if fast chatting is off
if (!bot_fastchat.integer) {
if (random() > rnd) return qfalse;
@@ -594,16 +572,26 @@ int BotChat_Death(bot_state_t *bs) {
bs->botdeathtype == MOD_SUICIDE ||
bs->botdeathtype == MOD_TARGET_LASER ||
bs->botdeathtype == MOD_TRIGGER_HURT ||
+ // BFP - Ki attack method of death
+ bs->botdeathtype == MOD_KI_ATTACK ||
bs->botdeathtype == MOD_UNKNOWN)
BotAI_BotInitialChat(bs, "death_suicide", BotRandomOpponentName(bs), NULL);
else if (bs->botdeathtype == MOD_TELEFRAG)
BotAI_BotInitialChat(bs, "death_telefrag", name, NULL);
else {
+ // BFP - That part is replaced to "death_suicide" because in the BFP player_t.c botfiles mention the following notes:
+ // note to anyone who may read this: there shouldn't be any gauntlets
+ // note to anyone who may read this: there shouldn't be any rails
+ // note to anyone who may read this: there shouldn't be any BFGs
if ((bs->botdeathtype == MOD_GAUNTLET ||
bs->botdeathtype == MOD_RAILGUN ||
bs->botdeathtype == MOD_BFG ||
- bs->botdeathtype == MOD_BFG_SPLASH) && random() < 0.5) {
+ bs->botdeathtype == MOD_BFG_SPLASH ||
+ // BFP - Ki attack method of death
+ bs->botdeathtype == MOD_KI_ATTACK) && random() < 0.5) {
+ BotAI_BotInitialChat(bs, "death_suicide", name, NULL);
+#if 0
if (bs->botdeathtype == MOD_GAUNTLET)
BotAI_BotInitialChat(bs, "death_gauntlet",
name, // 0
@@ -619,6 +607,7 @@ int BotChat_Death(bot_state_t *bs) {
name, // 0
BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
NULL);
+#endif
}
//choose between insult and praise
else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) {
@@ -653,7 +642,9 @@ int BotChat_Kill(bot_state_t *bs) {
if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1);
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
//if fast chat is off
if (!bot_fastchat.integer) {
if (random() > rnd) return qfalse;
@@ -713,11 +704,13 @@ int BotChat_EnemySuicide(bot_state_t *bs) {
if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
if (BotNumActivePlayers() <= 1) return qfalse;
//
- rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1);
+ rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENEMYSUICIDE, 0, 1);
//don't chat in teamplay
if (TeamPlayIsOn()) return qfalse;
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
//if fast chat is off
if (!bot_fastchat.integer) {
if (random() > rnd) return qfalse;
@@ -757,7 +750,9 @@ int BotChat_HitTalking(bot_state_t *bs) {
//don't chat in teamplay
if (TeamPlayIsOn()) return qfalse;
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
//if fast chat is off
if (!bot_fastchat.integer) {
if (random() > rnd * 0.5) return qfalse;
@@ -765,7 +760,7 @@ int BotChat_HitTalking(bot_state_t *bs) {
if (!BotValidChatPosition(bs)) return qfalse;
//
ClientName(g_entities[bs->client].client->lasthurt_client, name, sizeof(name));
- weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_client);
+ weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_mod);
//
BotAI_BotInitialChat(bs, "hit_talking", name, weap, NULL);
bs->lastchat_time = FloatTime();
@@ -797,7 +792,9 @@ int BotChat_HitNoDeath(bot_state_t *bs) {
//don't chat in teamplay
if (TeamPlayIsOn()) return qfalse;
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
//if fast chat is off
if (!bot_fastchat.integer) {
if (random() > rnd * 0.5) return qfalse;
@@ -835,7 +832,9 @@ int BotChat_HitNoKill(bot_state_t *bs) {
//don't chat in teamplay
if (TeamPlayIsOn()) return qfalse;
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
//if fast chat is off
if (!bot_fastchat.integer) {
if (random() > rnd * 0.5) return qfalse;
@@ -869,7 +868,9 @@ int BotChat_Random(bot_state_t *bs) {
if (BotIsObserver(bs)) return qfalse;
if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
// don't chat in tournament mode
- if (gametype == GT_TOURNAMENT) return qfalse;
+ if ( gametype == GT_TOURNAMENT
+ || gametype == GT_SURVIVAL ) // BFP - Survival
+ return qfalse;
//don't chat when doing something important :)
if (bs->ltgtype == LTG_TEAMHELP ||
bs->ltgtype == LTG_TEAMACCOMPANY ||
@@ -929,9 +930,9 @@ BotChatTime
==================
*/
float BotChatTime(bot_state_t *bs) {
- int cpm;
+ //int cpm;
- cpm = trap_Characteristic_BInteger(bs->character, CHARACTERISTIC_CHAT_CPM, 1, 4000);
+ //cpm = trap_Characteristic_BInteger(bs->character, CHARACTERISTIC_CHAT_CPM, 1, 4000);
return 2.0; //(float) trap_BotChatLength(bs->cs) * 30 / cpm;
}
diff --git a/source/game/ai_cmd.c b/source/game/ai_cmd.c
index 37cd9d8..ba8a9ac 100644
--- a/source/game/ai_cmd.c
+++ b/source/game/ai_cmd.c
@@ -217,54 +217,57 @@ float BotGetTime(bot_match_t *match) {
return 0;
}
+
/*
==================
FindClientByName
==================
*/
-int FindClientByName(char *name) {
+int FindClientByName( const char *name ) {
+ char buf[ MAX_INFO_STRING ];
int i;
- char buf[MAX_INFO_STRING];
- static int maxclients;
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
- ClientName(i, buf, sizeof(buf));
- if (!Q_stricmp(buf, name)) return i;
+ for ( i = 0; i < level.maxclients; i++ ) {
+ ClientName( i, buf, sizeof( buf ) );
+ if ( !Q_stricmp( buf, name ) )
+ return i; // exact match
}
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
- ClientName(i, buf, sizeof(buf));
- if (stristr(buf, name)) return i;
+
+ for ( i = 0; i < level.maxclients ; i++ ) {
+ ClientName(i, buf, sizeof( buf ) );
+ if ( stristr( buf, name ) )
+ return i; // partial match
}
+
return -1;
}
+
/*
==================
FindEnemyByName
==================
*/
-int FindEnemyByName(bot_state_t *bs, char *name) {
- int i;
+int FindEnemyByName( bot_state_t *bs, const char *name ) {
char buf[MAX_INFO_STRING];
- static int maxclients;
+ int i;
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (BotSameTeam(bs, i)) continue;
ClientName(i, buf, sizeof(buf));
if (!Q_stricmp(buf, name)) return i;
}
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+
+ for (i = 0; i < level.maxclients; i++) {
if (BotSameTeam(bs, i)) continue;
ClientName(i, buf, sizeof(buf));
if (stristr(buf, name)) return i;
}
+
return -1;
}
+
/*
==================
NumPlayersOnSameTeam
@@ -273,15 +276,11 @@ NumPlayersOnSameTeam
int NumPlayersOnSameTeam(bot_state_t *bs) {
int i, num;
char buf[MAX_INFO_STRING];
- static int maxclients;
-
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
num = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING);
- if (strlen(buf)) {
+ if (buf[0]) {
if (BotSameTeam(bs, i+1)) num++;
}
}
@@ -1038,8 +1037,7 @@ void BotMatch_JoinSubteam(bot_state_t *bs, bot_match_t *match) {
//get the sub team name
trap_BotMatchVariable(match, TEAMNAME, teammate, sizeof(teammate));
//set the sub team name
- strncpy(bs->subteam, teammate, 32);
- bs->subteam[31] = '\0';
+ Q_strncpyz( bs->subteam, teammate, sizeof( bs->subteam ) );
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
BotAI_BotInitialChat(bs, "joinedteam", teammate, NULL);
@@ -1060,7 +1058,7 @@ void BotMatch_LeaveSubteam(bot_state_t *bs, bot_match_t *match) {
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
- if (strlen(bs->subteam))
+ if (bs->subteam[0] != '\0')
{
BotAI_BotInitialChat(bs, "leftteam", bs->subteam, NULL);
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
@@ -1080,7 +1078,7 @@ void BotMatch_WhichTeam(bot_state_t *bs, bot_match_t *match) {
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
- if (strlen(bs->subteam)) {
+ if (bs->subteam[0] != '\0') {
BotAI_BotInitialChat(bs, "inteam", bs->subteam, NULL);
}
else {
@@ -1109,7 +1107,7 @@ void BotMatch_CheckPoint(bot_state_t *bs, bot_match_t *match) {
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
//BotGPSToPosition(buf, position);
- sscanf(buf, "%f %f %f", &position[0], &position[1], &position[2]);
+ Q_sscanf(buf, "%f %f %f", &position[0], &position[1], &position[2]);
position[2] += 0.5;
areanum = BotPointAreaNum(position);
if (!areanum) {
@@ -1210,8 +1208,8 @@ void BotMatch_Suicide(bot_state_t *bs, bot_match_t *match) {
trap_EA_Command(bs->client, "kill");
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
- client = ClientFromName(netname);
//
+ client = ClientFromName(netname);
BotVoiceChat(bs, client, VOICECHAT_TAUNT);
// trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
}
@@ -1230,8 +1228,7 @@ void BotMatch_StartTeamLeaderShip(bot_state_t *bs, bot_match_t *match) {
if (match->subtype & ST_I) {
//get the team mate that will be the team leader
trap_BotMatchVariable(match, NETNAME, teammate, sizeof(teammate));
- strncpy(bs->teamleader, teammate, sizeof(bs->teamleader));
- bs->teamleader[sizeof(bs->teamleader)] = '\0';
+ Q_strncpyz( bs->teamleader, teammate, sizeof( bs->teamleader ) );
}
//chats for someone else
else {
diff --git a/source/game/ai_dmnet.c b/source/game/ai_dmnet.c
index f89b902..423894a 100644
--- a/source/game/ai_dmnet.c
+++ b/source/game/ai_dmnet.c
@@ -47,6 +47,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "ai_cmd.h"
#include "ai_dmnet.h"
#include "ai_team.h"
+#include "ai_bfp.h"
//data file headers
#include "chars.h" //characteristics
#include "inv.h" //indexes into the inventory
@@ -85,7 +86,7 @@ void BotDumpNodeSwitches(bot_state_t *bs) {
ClientName(bs->client, netname, sizeof(netname));
BotAI_Print(PRT_MESSAGE, "%s at %1.1f switched more than %d AI nodes\n", netname, FloatTime(), MAX_NODESWITCHES);
for (i = 0; i < numnodeswitches; i++) {
- BotAI_Print(PRT_MESSAGE, nodeswitch[i]);
+ BotAI_Print( PRT_MESSAGE, "%s", nodeswitch[i] );
}
BotAI_Print(PRT_FATAL, "");
}
@@ -102,7 +103,7 @@ void BotRecordNodeSwitch(bot_state_t *bs, char *node, char *str, char *s) {
Com_sprintf(nodeswitch[numnodeswitches], 144, "%s at %2.1f entered %s: %s from %s\n", netname, FloatTime(), node, str, s);
#ifdef DEBUG
if (0) {
- BotAI_Print(PRT_MESSAGE, nodeswitch[numnodeswitches]);
+ BotAI_Print( PRT_MESSAGE, "%s", nodeswitch[numnodeswitches] );
}
#endif //DEBUG
numnodeswitches++;
@@ -193,7 +194,7 @@ int BotNearbyGoal(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) {
//check if the bot should go for air
if (BotGoForAir(bs, tfl, ltg, range)) return qtrue;
- //if the bot is carrying the enemy flag
+ // if the bot is carrying a flag or cubes
if (BotCTFCarryingFlag(bs)) {
//if the bot is just a few secs away from the base
if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin,
@@ -213,7 +214,7 @@ int BotNearbyGoal(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) {
trap_BotGoalName(goal.number, buf, sizeof(buf));
BotAI_Print(PRT_MESSAGE, "%1.1f: new nearby goal %s\n", FloatTime(), buf);
}
- */
+ */
return ret;
}
@@ -300,7 +301,7 @@ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) {
trap_BotGetTopGoal(bs->gs, goal);
trap_BotGoalName(goal->number, buf, sizeof(buf));
BotAI_Print(PRT_MESSAGE, "%1.1f: new long term goal %s\n", FloatTime(), buf);
- */
+ */
bs->ltg_time = FloatTime() + 20;
}
else {//the bot gets sorta stuck with all the avoid timings, shouldn't happen though
@@ -389,7 +390,6 @@ int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal)
BotAI_BotInitialChat(bs, "accompany_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL);
trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES);
- // trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
bs->teammessage_time = 0;
}
//if accompanying the companion for 3 minutes
@@ -569,11 +569,10 @@ int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal)
bs->teammessage_time = 0;
}
//
- if (bs->lastkilledplayer == bs->teamgoal.entitynum) {
+ if (bs->killedenemy_time > bs->teamgoal_time - TEAM_KILL_SOMEONE && bs->lastkilledplayer == bs->teamgoal.entitynum) {
EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf));
BotAI_BotInitialChat(bs, "kill_done", buf, NULL);
trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
- bs->lastkilledplayer = -1;
bs->ltgtype = 0;
}
//
@@ -591,7 +590,6 @@ int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal)
BotAI_BotInitialChat(bs, "getitem_start", buf, NULL);
trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL);
BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES);
- // trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
bs->teammessage_time = 0;
}
//set the bot goal
@@ -684,9 +682,7 @@ int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal)
bs->ltgtype = 0;
}
//
- if (bs->camp_range > 0) {
- //FIXME: move around a bit
- }
+ //FIXME: move around a bit
//
trap_BotResetAvoidReach(bs->ms);
return qfalse;
@@ -1103,6 +1099,8 @@ int BotSelectActivateWeapon(bot_state_t *bs) {
return WEAPONINDEX_PLASMAGUN;
else if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 0)
return WEAPONINDEX_LIGHTNING;
+ else if (bs->inventory[INVENTORY_GRENADELAUNCHER] > 0 && bs->inventory[INVENTORY_GRENADES] > 0)
+ return WEAPONINDEX_GRENADE_LAUNCHER;
else if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 0)
return WEAPONINDEX_RAILGUN;
else if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0)
@@ -1114,14 +1112,17 @@ int BotSelectActivateWeapon(bot_state_t *bs) {
}
}
+
+// BFP - No Team Arena proximity mines bot detection logic
/*
==================
BotClearPath
- try to deactivate obstacles like proximity mines on the bot's path
+try to deactivate obstacles like proximity mines on the bot's path
==================
*/
void BotClearPath(bot_state_t *bs, bot_moveresult_t *moveresult) {
+#if 0
int i, bestmine;
float dist, bestdist;
vec3_t target, dir;
@@ -1220,6 +1221,7 @@ void BotClearPath(bot_state_t *bs, bot_moveresult_t *moveresult) {
}
}
}
+#endif
}
/*
@@ -1325,7 +1327,7 @@ int AINode_Seek_ActivateEntity(bot_state_t *bs) {
}
else {
// if the bot has no goal
- if (!goal) {
+ if (!goal) { // FIXME: always false?
bs->activatestack->time = 0;
}
// if the bot does not have a shoot goal
@@ -1751,11 +1753,12 @@ void AIEnter_Battle_Fight(bot_state_t *bs, char *s) {
BotRecordNodeSwitch(bs, "battle fight", "", s);
trap_BotResetLastAvoidReach(bs->ms);
bs->ainode = AINode_Battle_Fight;
+ bs->flags &= ~BFL_FIGHTSUICIDAL;
}
/*
==================
-AIEnter_Battle_Fight
+AIEnter_Battle_SuicidalFight
==================
*/
void AIEnter_Battle_SuicidalFight(bot_state_t *bs, char *s) {
@@ -1798,7 +1801,9 @@ int AINode_Battle_Fight(bot_state_t *bs) {
#endif
}
//if no enemy
- if (bs->enemy < 0) {
+ if (bs->enemy < 0 || ( g_gametype.integer >= GT_TEAM && bs->enemy < level.maxclients &&
+ level.clients[bs->client].sess.sessionTeam == level.clients[bs->enemy].sess.sessionTeam ) ) {
+ bs->enemy = -1;
AIEnter_Seek_LTG(bs, "battle fight: no enemy");
return qfalse;
}
@@ -1811,6 +1816,10 @@ int AINode_Battle_Fight(bot_state_t *bs) {
if (bs->enemysuicide) {
BotChat_EnemySuicide(bs);
}
+ // BFP - Gesture after killing opponent
+ if ( bs->lastkilledplayer == bs->enemy ) {
+ trap_EA_Gesture( bs->client );
+ }
if (bs->lastkilledplayer == bs->enemy && BotChat_Kill(bs)) {
bs->stand_time = FloatTime() + BotChatTime(bs);
AIEnter_Stand(bs, "battle fight: enemy dead");
@@ -1885,8 +1894,7 @@ int AINode_Battle_Fight(bot_state_t *bs) {
if (BotCanAndWantsToRocketJump(bs)) {
bs->tfl |= TFL_ROCKETJUMP;
}
- //choose the best weapon to fight with
- BotChooseWeapon(bs);
+ // BFP - before Q3 line here: BotChooseWeapon(bs);
//do attack movements
moveresult = BotAttackMove(bs, bs->tfl);
//if the movement failed
@@ -1902,6 +1910,8 @@ int AINode_Battle_Fight(bot_state_t *bs) {
BotAimAtEnemy(bs);
//attack the enemy if possible
BotCheckAttack(bs);
+ // BFP - BFP bot combat
+ BotBFPCombatAI( bs );
//if the bot wants to retreat
if (!(bs->flags & BFL_FIGHTSUICIDAL)) {
if (BotWantsToRetreat(bs)) {
@@ -2192,8 +2202,7 @@ int AINode_Battle_Retreat(bot_state_t *bs) {
}
//
BotAIBlocked(bs, &moveresult, qfalse);
- //choose the best weapon to fight with
- BotChooseWeapon(bs);
+ // BFP - before Q3 line here: BotChooseWeapon(bs);
//if the view is fixed for the movement
if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) {
VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles);
@@ -2201,7 +2210,7 @@ int AINode_Battle_Retreat(bot_state_t *bs) {
else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET)
&& !(bs->flags & BFL_IDEALVIEWSET) ) {
attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1);
- //if the bot is skilled anough
+ //if the bot is skilled enough
if (attack_skill > 0.3) {
BotAimAtEnemy(bs);
}
@@ -2220,6 +2229,8 @@ int AINode_Battle_Retreat(bot_state_t *bs) {
if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon;
//attack the enemy if possible
BotCheckAttack(bs);
+ // BFP - BFP bot combat
+ BotBFPCombatAI( bs );
//
return qtrue;
}
@@ -2331,8 +2342,7 @@ int AINode_Battle_NBG(bot_state_t *bs) {
BotAIBlocked(bs, &moveresult, qfalse);
//update the attack inventory values
BotUpdateBattleInventory(bs, bs->enemy);
- //choose the best weapon to fight with
- BotChooseWeapon(bs);
+ // BFP - before Q3 line here: BotChooseWeapon(bs);
//if the view is fixed for the movement
if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) {
VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles);
@@ -2340,7 +2350,7 @@ int AINode_Battle_NBG(bot_state_t *bs) {
else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET)
&& !(bs->flags & BFL_IDEALVIEWSET)) {
attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1);
- //if the bot is skilled anough and the enemy is visible
+ //if the bot is skilled enough and the enemy is visible
if (attack_skill > 0.3) {
//&& BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)
BotAimAtEnemy(bs);
@@ -2360,6 +2370,8 @@ int AINode_Battle_NBG(bot_state_t *bs) {
if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon;
//attack the enemy if possible
BotCheckAttack(bs);
+ // BFP - BFP bot combat
+ BotBFPCombatAI( bs );
//
return qtrue;
}
diff --git a/source/game/ai_dmq3.c b/source/game/ai_dmq3.c
index 6e1eada..c5f048a 100644
--- a/source/game/ai_dmq3.c
+++ b/source/game/ai_dmq3.c
@@ -72,7 +72,7 @@ bot_waypoint_t *botai_freewaypoints;
//NOTE: not using a cvars which can be updated because the game should be reloaded anyway
int gametype; //game type
-int maxclients; //maximum number of clients
+//int maxclients; //maximum number of clients
vmCvar_t bot_grapple;
vmCvar_t bot_rocketjump;
@@ -107,15 +107,16 @@ int blue_numaltroutegoals;
BotSetUserInfo
==================
*/
-void BotSetUserInfo(bot_state_t *bs, char *key, char *value) {
+static void BotSetUserInfo( bot_state_t *bs, const char *key, const char *value ) {
char userinfo[MAX_INFO_STRING];
- trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo));
- Info_SetValueForKey(userinfo, key, value);
- trap_SetUserinfo(bs->client, userinfo);
+ trap_GetUserinfo( bs->client, userinfo, sizeof( userinfo ) );
+ Info_SetValueForKey( userinfo, key, value );
+ trap_SetUserinfo( bs->client, userinfo );
ClientUserinfoChanged( bs->client );
}
+
/*
==================
BotCTFCarryingFlag
@@ -229,9 +230,12 @@ qboolean EntityIsInvisible(aas_entityinfo_t *entinfo) {
if (EntityCarriesFlag(entinfo)) {
return qfalse;
}
+ // BFP - No invis powerup
+#if 0
if (entinfo->powerups & (1 << PW_INVIS)) {
return qtrue;
}
+#endif
return qfalse;
}
@@ -591,7 +595,7 @@ void BotCTFSeekGoals(bot_state_t *bs) {
if (bs->ctfroam_time > FloatTime())
return;
//if the bot has anough aggression to decide what to do
- if (BotAggression(bs) < 50)
+ if (BotAggression(bs) < 0.5)
return;
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
@@ -708,91 +712,96 @@ int BotPointAreaNum(vec3_t origin) {
return 0;
}
+
/*
==================
ClientName
==================
*/
-char *ClientName(int client, char *name, int size) {
- char buf[MAX_INFO_STRING];
+char *ClientName( int client, char *name, int size ) {
+ char buf[ MAX_INFO_STRING ];
- if (client < 0 || client >= MAX_CLIENTS) {
- BotAI_Print(PRT_ERROR, "ClientName: client out of range\n");
- return "[client out of range]";
+ if ( (unsigned) client >= MAX_CLIENTS ) {
+ BotAI_Print( PRT_ERROR, "ClientName: client out of range\n" );
+ Q_strncpyz( name, "[client out of range]", size );
+ return name;
}
- trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf));
- strncpy(name, Info_ValueForKey(buf, "n"), size-1);
- name[size-1] = '\0';
+
+ trap_GetConfigstring( CS_PLAYERS + client, buf, sizeof( buf ) );
+ Q_strncpyz( name, Info_ValueForKey( buf, "n" ), size );
Q_CleanStr( name );
+
return name;
}
+
/*
==================
ClientSkin
==================
*/
-char *ClientSkin(int client, char *skin, int size) {
- char buf[MAX_INFO_STRING];
+char *ClientSkin( int client, char *skin, int size ) {
+ char buf[ MAX_INFO_STRING ];
- if (client < 0 || client >= MAX_CLIENTS) {
+ if ( (unsigned) client >= MAX_CLIENTS ) {
BotAI_Print(PRT_ERROR, "ClientSkin: client out of range\n");
return "[client out of range]";
}
- trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf));
- strncpy(skin, Info_ValueForKey(buf, "model"), size-1);
- skin[size-1] = '\0';
+
+ trap_GetConfigstring( CS_PLAYERS + client, buf, sizeof( buf ) );
+ Q_strncpyz( skin, Info_ValueForKey( buf, "model" ), size );
+
return skin;
}
+
/*
==================
ClientFromName
==================
*/
-int ClientFromName(char *name) {
+int ClientFromName( const char *name ) {
int i;
- char buf[MAX_INFO_STRING];
- static int maxclients;
+ char buf[ MAX_INFO_STRING ];
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
- trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
+ for ( i = 0; i < level.maxclients; i++ ) {
+ trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
Q_CleanStr( buf );
- if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i;
+ if ( !Q_stricmp( Info_ValueForKey( buf, "n" ), name ) )
+ return i;
}
return -1;
}
+
/*
==================
ClientOnSameTeamFromName
==================
*/
-int ClientOnSameTeamFromName(bot_state_t *bs, char *name) {
- int i;
+int ClientOnSameTeamFromName( bot_state_t *bs, const char *name ) {
char buf[MAX_INFO_STRING];
- static int maxclients;
+ int i;
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
- if (!BotSameTeam(bs, i))
+ for ( i = 0; i < level.maxclients; i++ ) {
+ if ( !BotSameTeam( bs, i ) )
continue;
- trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
+ trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
Q_CleanStr( buf );
- if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i;
+ if ( !Q_stricmp( Info_ValueForKey( buf, "n" ), name ) )
+ return i;
}
+
return -1;
}
+
/*
==================
stristr
==================
*/
-char *stristr(char *str, char *charset) {
+const char *stristr(const char *str, const char *charset) {
int i;
while(*str) {
@@ -805,6 +814,7 @@ char *stristr(char *str, char *charset) {
return NULL;
}
+
/*
==================
EasyClientName
@@ -815,23 +825,23 @@ char *EasyClientName(int client, char *buf, int size) {
char *str1, *str2, *ptr, c;
char name[128];
- strcpy(name, ClientName(client, name, sizeof(name)));
+ ClientName( client, name, sizeof( name ) );
for (i = 0; name[i]; i++) name[i] &= 127;
//remove all spaces
- for (ptr = strstr(name, " "); ptr; ptr = strstr(name, " ")) {
- memmove(ptr, ptr+1, (int)strlen(ptr+1)+1);
+ for (ptr = strchr(name, ' '); ptr; ptr = strchr(name, ' ')) {
+ memmove(ptr, ptr+1, strlen(ptr+1)+1);
}
//check for [x] and ]x[ clan names
- str1 = strstr(name, "[");
- str2 = strstr(name, "]");
+ str1 = strchr(name, '[');
+ str2 = strchr(name, ']');
if (str1 && str2) {
- if (str2 > str1) memmove(str1, str2+1, (int)strlen(str2+1)+1);
- else memmove(str2, str1+1, (int)strlen(str1+1)+1);
+ if (str2 > str1) memmove(str1, str2+1, strlen(str2+1)+1);
+ else memmove(str2, str1+1, strlen(str1+1)+1);
}
//remove Mr prefix
if ((name[0] == 'm' || name[0] == 'M') &&
(name[1] == 'r' || name[1] == 'R')) {
- memmove(name, name+2, (int)strlen(name+2)+1);
+ memmove(name, name+2, strlen(name+2)+1);
}
//only allow lower case alphabet characters
ptr = name;
@@ -846,11 +856,12 @@ char *EasyClientName(int client, char *buf, int size) {
ptr++;
}
else {
- memmove(ptr, ptr+1, (int)strlen(ptr + 1)+1);
+ memmove(ptr, ptr+1, strlen(ptr + 1)+1);
}
}
- strncpy(buf, name, size-1);
- buf[size-1] = '\0';
+
+ Q_strncpyz( buf, name, size );
+
return buf;
}
@@ -916,9 +927,12 @@ void BotSetupForMovement(bot_state_t *bs) {
initmove.or_moveflags |= MFL_TELEPORTED;
}
//set the waterjump flag
+ // BFP - No handling PMF_TIME_WATERJUMP
+#if 0
if ((bs->cur_ps.pm_flags & PMF_TIME_WATERJUMP) && (bs->cur_ps.pm_time > 0)) {
initmove.or_moveflags |= MFL_WATERJUMP;
}
+#endif
//set presence type
if (bs->cur_ps.pm_flags & PMF_DUCKED) initmove.presencetype = PRESENCE_CROUCH;
else initmove.presencetype = PRESENCE_NORMAL;
@@ -950,35 +964,42 @@ void BotUpdateInventory(bot_state_t *bs) {
//armor
bs->inventory[INVENTORY_ARMOR] = bs->cur_ps.stats[STAT_ARMOR];
//weapons
+ // BFP - TODO: Just a test, bfp_weapon.cfg needs to be implemented and replace all stuff here
bs->inventory[INVENTORY_GAUNTLET] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GAUNTLET)) != 0;
- bs->inventory[INVENTORY_SHOTGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_SHOTGUN)) != 0;
bs->inventory[INVENTORY_MACHINEGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_MACHINEGUN)) != 0;
+ bs->inventory[INVENTORY_SHOTGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_SHOTGUN)) != 0;
bs->inventory[INVENTORY_GRENADELAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRENADE_LAUNCHER)) != 0;
- bs->inventory[INVENTORY_ROCKETLAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_ROCKET_LAUNCHER)) != 0;
- bs->inventory[INVENTORY_LIGHTNING] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_LIGHTNING)) != 0;
- bs->inventory[INVENTORY_RAILGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_RAILGUN)) != 0;
- bs->inventory[INVENTORY_PLASMAGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_PLASMAGUN)) != 0;
- bs->inventory[INVENTORY_BFG10K] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_BFG)) != 0;
bs->inventory[INVENTORY_GRAPPLINGHOOK] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRAPPLING_HOOK)) != 0;
+
+ bs->inventory[INVENTORY_ROCKETLAUNCHER] =
+ bs->inventory[INVENTORY_LIGHTNING] =
+ bs->inventory[INVENTORY_RAILGUN] =
+ bs->inventory[INVENTORY_PLASMAGUN] =
+ bs->inventory[INVENTORY_BFG10K] = 0;
//ammo
- bs->inventory[INVENTORY_SHELLS] = bs->cur_ps.ammo[WP_SHOTGUN];
- bs->inventory[INVENTORY_BULLETS] = bs->cur_ps.ammo[WP_MACHINEGUN];
- bs->inventory[INVENTORY_GRENADES] = bs->cur_ps.ammo[WP_GRENADE_LAUNCHER];
- bs->inventory[INVENTORY_CELLS] = bs->cur_ps.ammo[WP_PLASMAGUN];
- bs->inventory[INVENTORY_LIGHTNINGAMMO] = bs->cur_ps.ammo[WP_LIGHTNING];
- bs->inventory[INVENTORY_ROCKETS] = bs->cur_ps.ammo[WP_ROCKET_LAUNCHER];
- bs->inventory[INVENTORY_SLUGS] = bs->cur_ps.ammo[WP_RAILGUN];
- bs->inventory[INVENTORY_BFGAMMO] = bs->cur_ps.ammo[WP_BFG];
+ bs->inventory[INVENTORY_SHELLS] =
+ bs->inventory[INVENTORY_BULLETS] =
+ bs->inventory[INVENTORY_GRENADES] =
+ bs->inventory[INVENTORY_CELLS] =
+ bs->inventory[INVENTORY_LIGHTNINGAMMO] =
+ bs->inventory[INVENTORY_ROCKETS] =
+ bs->inventory[INVENTORY_SLUGS] =
+ bs->inventory[INVENTORY_BFGAMMO] = 1;
//powerups
bs->inventory[INVENTORY_HEALTH] = bs->cur_ps.stats[STAT_HEALTH];
bs->inventory[INVENTORY_TELEPORTER] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_TELEPORTER;
bs->inventory[INVENTORY_MEDKIT] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_MEDKIT;
bs->inventory[INVENTORY_QUAD] = bs->cur_ps.powerups[PW_QUAD] != 0;
- bs->inventory[INVENTORY_ENVIRONMENTSUIT] = bs->cur_ps.powerups[PW_BATTLESUIT] != 0;
- bs->inventory[INVENTORY_HASTE] = bs->cur_ps.powerups[PW_HASTE] != 0;
- bs->inventory[INVENTORY_INVISIBILITY] = bs->cur_ps.powerups[PW_INVIS] != 0;
- bs->inventory[INVENTORY_REGEN] = bs->cur_ps.powerups[PW_REGEN] != 0;
- bs->inventory[INVENTORY_FLIGHT] = bs->cur_ps.powerups[PW_FLIGHT] != 0;
+ // BFP - No battlesuit powerup
+ // bs->inventory[INVENTORY_ENVIRONMENTSUIT] = bs->cur_ps.powerups[PW_BATTLESUIT] != 0;
+ // BFP - No haste powerup
+ // bs->inventory[INVENTORY_HASTE] = bs->cur_ps.powerups[PW_HASTE] != 0;
+ // BFP - No invis powerup
+ // bs->inventory[INVENTORY_INVISIBILITY] = bs->cur_ps.powerups[PW_INVIS] != 0;
+ // BFP - No regen powerup
+ // bs->inventory[INVENTORY_REGEN] = bs->cur_ps.powerups[PW_REGEN] != 0;
+ // BFP - No flight powerup
+ // bs->inventory[INVENTORY_FLIGHT] = bs->cur_ps.powerups[PW_FLIGHT] != 0;
bs->inventory[INVENTORY_REDFLAG] = bs->cur_ps.powerups[PW_REDFLAG] != 0;
bs->inventory[INVENTORY_BLUEFLAG] = bs->cur_ps.powerups[PW_BLUEFLAG] != 0;
@@ -1166,46 +1187,8 @@ BotAggression
==================
*/
float BotAggression(bot_state_t *bs) {
- //if the bot has quad
- if (bs->inventory[INVENTORY_QUAD]) {
- //if the bot is not holding the gauntlet or the enemy is really nearby
- if (bs->weaponnum != WP_GAUNTLET ||
- bs->inventory[ENEMY_HORIZONTAL_DIST] < 80) {
- return 70;
- }
- }
- //if the enemy is located way higher than the bot
- if (bs->inventory[ENEMY_HEIGHT] > 200) return 0;
- //if the bot is very low on health
- if (bs->inventory[INVENTORY_HEALTH] < 60) return 0;
- //if the bot is low on health
- if (bs->inventory[INVENTORY_HEALTH] < 80) {
- //if the bot has insufficient armor
- if (bs->inventory[INVENTORY_ARMOR] < 40) return 0;
- }
- //if the bot can use the bfg
- if (bs->inventory[INVENTORY_BFG10K] > 0 &&
- bs->inventory[INVENTORY_BFGAMMO] > 7) return 100;
- //if the bot can use the railgun
- if (bs->inventory[INVENTORY_RAILGUN] > 0 &&
- bs->inventory[INVENTORY_SLUGS] > 5) return 95;
- //if the bot can use the lightning gun
- if (bs->inventory[INVENTORY_LIGHTNING] > 0 &&
- bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50) return 90;
- //if the bot can use the rocketlauncher
- if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 &&
- bs->inventory[INVENTORY_ROCKETS] > 5) return 90;
- //if the bot can use the plasmagun
- if (bs->inventory[INVENTORY_PLASMAGUN] > 0 &&
- bs->inventory[INVENTORY_CELLS] > 40) return 85;
- //if the bot can use the grenade launcher
- if (bs->inventory[INVENTORY_GRENADELAUNCHER] > 0 &&
- bs->inventory[INVENTORY_GRENADES] > 10) return 80;
- //if the bot can use the shotgun
- if (bs->inventory[INVENTORY_SHOTGUN] > 0 &&
- bs->inventory[INVENTORY_SHELLS] > 10) return 50;
- //otherwise the bot is not feeling too good
- return 0;
+ // BFP - Read CHARACTERISTIC_AGGRESSION from botfiles
+ return trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AGGRESSION, 0, 1 );
}
/*
@@ -1253,7 +1236,7 @@ int BotWantsToRetreat(bot_state_t *bs) {
if (bs->ltgtype == LTG_GETFLAG)
return qtrue;
//
- if (BotAggression(bs) < 50)
+ if (BotAggression(bs) < 0.5)
return qtrue;
return qfalse;
}
@@ -1279,7 +1262,7 @@ int BotWantsToChase(bot_state_t *bs) {
if (bs->ltgtype == LTG_GETFLAG)
return qfalse;
//
- if (BotAggression(bs) > 50)
+ if (BotAggression(bs) > 0.5)
return qtrue;
return qfalse;
}
@@ -1346,15 +1329,6 @@ int BotHasPersistantPowerupAndWeapon(bot_state_t *bs) {
//if the bot can use the rocketlauncher
if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 &&
bs->inventory[INVENTORY_ROCKETS] > 5) return qtrue;
- //
- if (bs->inventory[INVENTORY_NAILGUN] > 0 &&
- bs->inventory[INVENTORY_NAILS] > 5) return qtrue;
- //
- if (bs->inventory[INVENTORY_PROXLAUNCHER] > 0 &&
- bs->inventory[INVENTORY_MINES] > 5) return qtrue;
- //
- if (bs->inventory[INVENTORY_CHAINGUN] > 0 &&
- bs->inventory[INVENTORY_BELT] > 40) return qtrue;
//if the bot can use the plasmagun
if (bs->inventory[INVENTORY_PLASMAGUN] > 0 &&
bs->inventory[INVENTORY_CELLS] > 20) return qtrue;
@@ -1419,9 +1393,9 @@ int BotWantsToCamp(bot_state_t *bs) {
return qfalse;
}
//if the bot isn't healthy anough
- if (BotAggression(bs) < 50) return qfalse;
+ if (BotAggression(bs) < 0.5) return qfalse;
//the bot should have at least have the rocket launcher, the railgun or the bfg10k with some ammo
- if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 || bs->inventory[INVENTORY_ROCKETS < 10]) &&
+ if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 || bs->inventory[INVENTORY_ROCKETS] < 10) &&
(bs->inventory[INVENTORY_RAILGUN] <= 0 || bs->inventory[INVENTORY_SLUGS] < 10) &&
(bs->inventory[INVENTORY_BFG10K] <= 0 || bs->inventory[INVENTORY_BFGAMMO] < 10)) {
return qfalse;
@@ -1677,21 +1651,20 @@ BotSameTeam
==================
*/
int BotSameTeam(bot_state_t *bs, int entnum) {
- char info1[1024], info2[1024];
- if (bs->client < 0 || bs->client >= MAX_CLIENTS) {
+ extern gclient_t g_clients[ MAX_CLIENTS ];
+
+ if ( (unsigned) bs->client >= MAX_CLIENTS ) {
//BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n");
return qfalse;
}
- if (entnum < 0 || entnum >= MAX_CLIENTS) {
+ if ( (unsigned) entnum >= MAX_CLIENTS ) {
//BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n");
return qfalse;
}
if ( gametype >= GT_TEAM ) {
- trap_GetConfigstring(CS_PLAYERS+bs->client, info1, sizeof(info1));
- trap_GetConfigstring(CS_PLAYERS+entnum, info2, sizeof(info2));
- //
- if (atoi(Info_ValueForKey(info1, "t")) == atoi(Info_ValueForKey(info2, "t"))) return qtrue;
+ if ( g_clients[bs->client].sess.sessionTeam == g_clients[entnum].sess.sessionTeam )
+ return qtrue;
}
return qfalse;
}
@@ -1742,6 +1715,8 @@ float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int
//calculate middle of bounding box
BotEntityInfo(ent, &entinfo);
+ if (!entinfo.valid)
+ return 0;
VectorAdd(entinfo.mins, entinfo.maxs, middle);
VectorScale(middle, 0.5, middle);
VectorAdd(entinfo.origin, middle, middle);
@@ -1839,7 +1814,7 @@ BotFindEnemy
*/
int BotFindEnemy(bot_state_t *bs, int curenemy) {
int i, healthdecrease;
- float f, alertness, easyfragger, vis;
+ float alertness, easyfragger, vis;
float squaredist, cursquaredist;
aas_entityinfo_t entinfo, curenemyinfo;
vec3_t dir, angles;
@@ -1861,11 +1836,13 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) {
cursquaredist = 0;
}
//
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (i == bs->client) continue;
//if it's the current enemy
if (i == curenemy) continue;
+ //if the enemy has targeting disabled
+ if (g_entities[i].flags & FL_NOTARGET) continue;
//
BotEntityInfo(i, &entinfo);
//
@@ -1893,16 +1870,12 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) {
if (curenemy >= 0 && squaredist > cursquaredist) continue;
} //end if
//if the bot has no
- if (squaredist > Square(900.0 + alertness * 4000.0)) continue;
+ if (squaredist > Square(1200.0 + alertness * 9000.0)) continue;
//if on the same team
if (BotSameTeam(bs, i)) continue;
- //if the bot's health decreased or the enemy is shooting
- if (curenemy < 0 && (healthdecrease || EntityIsShooting(&entinfo)))
- f = 360;
- else
- f = 90 + 90 - (90 - (squaredist > Square(810) ? Square(810) : squaredist) / (810 * 9));
+ // BFP - No fov adjustment, just 360, very visible, even behind
//check if the enemy is visible
- vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, f, i);
+ vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i);
if (vis <= 0) continue;
//if the enemy is quite far away, not shooting and the bot is not damaged
if (curenemy < 0 && squaredist > Square(100) && !healthdecrease && !EntityIsShooting(&entinfo))
@@ -1940,7 +1913,7 @@ int BotTeamFlagCarrierVisible(bot_state_t *bs) {
float vis;
aas_entityinfo_t entinfo;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (i == bs->client)
continue;
//
@@ -1973,7 +1946,7 @@ int BotTeamFlagCarrier(bot_state_t *bs) {
int i;
aas_entityinfo_t entinfo;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (i == bs->client)
continue;
//
@@ -2003,7 +1976,7 @@ int BotEnemyFlagCarrierVisible(bot_state_t *bs) {
float vis;
aas_entityinfo_t entinfo;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (i == bs->client)
continue;
//
@@ -2042,7 +2015,7 @@ void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies
*teammates = 0;
if (enemies)
*enemies = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (i == bs->client)
continue;
//
@@ -2088,6 +2061,14 @@ void BotAimAtEnemy(bot_state_t *bs) {
bot_goal_t goal;
bsp_trace_t trace;
vec3_t target;
+ gentity_t *botent = &g_entities[bs->entitynum];
+
+ // BFP - If bot is blinded, severely reduce aim accuracy
+ if ( botent->blindedTime && level.time - botent->blindedTime < 6000 ) {
+ // make the bot aim wildly
+ bs->ideal_viewangles[YAW] += 60 * crandom();
+ return;
+ }
//if the bot has no enemy
if (bs->enemy < 0) {
@@ -2266,10 +2247,10 @@ void BotAimAtEnemy(bot_state_t *bs) {
//if hitpoint is not vertically too far from the ground target
if (fabs(trace.endpos[2] - groundtarget[2]) < 50) {
VectorSubtract(trace.endpos, groundtarget, dir);
- //if the hitpoint is near anough the ground target
+ //if the hitpoint is near enough the ground target
if (VectorLengthSquared(dir) < Square(60)) {
VectorSubtract(trace.endpos, start, dir);
- //if the hitpoint is far anough from the bot
+ //if the hitpoint is far enough from the bot
if (VectorLengthSquared(dir) > Square(100)) {
//check if the bot is visible from the ground target
trace.endpos[2] += 1;
@@ -2291,7 +2272,7 @@ void BotAimAtEnemy(bot_state_t *bs) {
//
VectorCopy(bs->lastenemyorigin, bestorigin);
bestorigin[2] += 8;
- //if the bot is skilled anough
+ //if the bot is skilled enough
if (aim_skill > 0.5) {
//do prediction shots around corners
if (wi.number == WP_BFG ||
@@ -2374,7 +2355,21 @@ void BotCheckAttack(bot_state_t *bs) {
weaponinfo_t wi;
bsp_trace_t trace;
aas_entityinfo_t entinfo;
+ weapon_t weapon;
vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};
+ gentity_t *botent;
+
+ botent = &g_entities[bs->entitynum];
+
+ // BFP - If bot is blinded, don't attack
+ if ( botent->blindedTime && level.time - botent->blindedTime < 6000 ) {
+ return; // Bot can't see to attack
+ }
+
+ // BFP - Avoid the bot tries to attack with this status, otherwise keeps stunned while pressing attack key
+ if ( bs->cur_ps.stats[STAT_HITSTUN_TIME] > 0 ) {
+ return;
+ }
attackentity = bs->enemy;
//
@@ -2437,7 +2432,7 @@ void BotCheckAttack(bot_state_t *bs) {
VectorMA(start, -12, forward, start);
BotAI_Trace(&trace, start, mins, maxs, end, bs->entitynum, MASK_SHOT);
//if the entity is a client
- if (trace.ent > 0 && trace.ent <= MAX_CLIENTS) {
+ if (trace.ent >= 0 && trace.ent < MAX_CLIENTS) {
if (trace.ent != attackentity) {
//if a teammate is hit
if (BotSameTeam(bs, trace.ent))
@@ -2457,6 +2452,12 @@ void BotCheckAttack(bot_state_t *bs) {
//FIXME: check if a teammate gets radial damage
}
}
+
+ weapon = bs->cur_ps.weapon;
+ if ( weapon >= WP_MACHINEGUN && weapon <= WP_BFG && !bs->cur_ps.ammo[ weapon ] ) {
+ return;
+ }
+
//if fire has to be release to activate weapon
if (wi.flags & WFL_FIRERELEASED) {
if (bs->flags & BFL_ATTACKED) {
@@ -2475,18 +2476,11 @@ BotMapScripts
==================
*/
void BotMapScripts(bot_state_t *bs) {
- char info[1024];
- char mapname[128];
int i, shootbutton;
float aim_accuracy;
aas_entityinfo_t entinfo;
vec3_t dir;
- trap_GetServerinfo(info, sizeof(info));
-
- strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1);
- mapname[sizeof(mapname)-1] = '\0';
-
if (!Q_stricmp(mapname, "q3tourney6")) {
vec3_t mins = {700, 204, 672}, maxs = {964, 468, 680};
vec3_t buttonorg = {304, 352, 920};
@@ -2502,7 +2496,7 @@ void BotMapScripts(bot_state_t *bs) {
}
shootbutton = qfalse;
//if an enemy is below this bounding box then shoot the button
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (i == bs->client) continue;
//
@@ -2769,7 +2763,8 @@ BotFuncDoorGoal
int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) {
int modelindex, entitynum;
char model[MAX_INFO_STRING];
- vec3_t mins, maxs, origin, angles;
+ vec3_t mins, maxs, origin;
+ //vec3_t angles;
//shoot at the shootable door
trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model));
@@ -2778,7 +2773,7 @@ int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *act
modelindex = atoi(model+1);
if (!modelindex)
return qfalse;
- VectorClear(angles);
+ //VectorClear(angles);
entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs);
//door origin
VectorAdd(mins, maxs, origin);
@@ -2804,7 +2799,8 @@ BotTriggerMultipleGoal
int BotTriggerMultipleActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) {
int i, areas[10], numareas, modelindex, entitynum;
char model[128];
- vec3_t start, end, mins, maxs, angles;
+ vec3_t start, end, mins, maxs;
+ //vec3_t angles;
vec3_t origin, goalorigin;
activategoal->shoot = qfalse;
@@ -2816,7 +2812,7 @@ int BotTriggerMultipleActivateGoal(bot_state_t *bs, int bspent, bot_activategoal
modelindex = atoi(model+1);
if (!modelindex)
return qfalse;
- VectorClear(angles);
+ //VectorClear(angles);
entitynum = BotModelMinsMaxs(modelindex, 0, CONTENTS_TRIGGER, mins, maxs);
//trigger origin
VectorAdd(mins, maxs, origin);
@@ -2950,8 +2946,8 @@ int BotIsGoingToActivateEntity(bot_state_t *bs, int entitynum) {
==================
BotGetActivateGoal
- returns the number of the bsp entity to activate
- goal->entitynum will be set to the game entity to activate
+returns the number of the bsp entity to activate
+goal->entitynum will be set to the game entity to activate
==================
*/
//#define OBSTACLEDEBUG
@@ -2964,7 +2960,8 @@ int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activ
char targetname[10][128];
aas_entityinfo_t entinfo;
aas_areainfo_t areainfo;
- vec3_t origin, angles, absmins, absmaxs;
+ vec3_t origin, absmins, absmaxs;
+ //vec3_t angles;
memset(activategoal, 0, sizeof(bot_activategoal_t));
BotEntityInfo(entitynum, &entinfo);
@@ -2978,7 +2975,7 @@ int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activ
return 0;
}
trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname));
- if (!classname) {
+ if (!*classname) {
BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with model %s has no classname\n", model);
return 0;
}
@@ -3008,7 +3005,7 @@ int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activ
if (*model) {
modelindex = atoi(model+1);
if (modelindex) {
- VectorClear(angles);
+ //VectorClear(angles);
BotModelMinsMaxs(modelindex, ET_MOVER, 0, absmins, absmaxs);
//
numareas = trap_AAS_BBoxAreas(absmins, absmaxs, areas, MAX_ACTIVATEAREAS*2);
@@ -3080,9 +3077,9 @@ int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activ
continue;
// if the bot tries to activate this button already
if ( bs->activatestack && bs->activatestack->inuse &&
- bs->activatestack->goal.entitynum == activategoal->goal.entitynum &&
- bs->activatestack->time > FloatTime() &&
- bs->activatestack->start_time < FloatTime() - 2)
+ bs->activatestack->goal.entitynum == activategoal->goal.entitynum &&
+ bs->activatestack->time > FloatTime() &&
+ bs->activatestack->start_time < FloatTime() - 2)
continue;
// if the bot is in a reachability area
if ( trap_AAS_AreaReachability(bs->areanum) ) {
@@ -3105,9 +3102,9 @@ int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activ
continue;
// if the bot tries to activate this trigger already
if ( bs->activatestack && bs->activatestack->inuse &&
- bs->activatestack->goal.entitynum == activategoal->goal.entitynum &&
- bs->activatestack->time > FloatTime() &&
- bs->activatestack->start_time < FloatTime() - 2)
+ bs->activatestack->goal.entitynum == activategoal->goal.entitynum &&
+ bs->activatestack->time > FloatTime() &&
+ bs->activatestack->start_time < FloatTime() - 2)
continue;
// if the bot is in a reachability area
if ( trap_AAS_AreaReachability(bs->areanum) ) {
@@ -3231,7 +3228,7 @@ open, which buttons to activate etc.
*/
void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) {
int movetype, bspent;
- vec3_t hordir, start, end, mins, maxs, sideward, angles, up = {0, 0, 1};
+ vec3_t hordir, start, /*end, mins, maxs,*/ sideward, angles, up = {0, 0, 1};
aas_entityinfo_t entinfo;
bot_activategoal_t activategoal;
@@ -3252,7 +3249,7 @@ void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) {
#ifdef OBSTACLEDEBUG
ClientName(bs->client, netname, sizeof(netname));
BotAI_Print(PRT_MESSAGE, "%s: I'm blocked by model %d\n", netname, entinfo.modelindex);
-#endif // OBSTACLEDEBUG
+#endif
// if blocked by a bsp model and the bot wants to activate it
if (activate && entinfo.modelindex > 0 && entinfo.modelindex <= max_bspmodelindex) {
// find the bsp entity which should be activated in order to get the blocking entity out of the way
@@ -3295,9 +3292,9 @@ void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) {
// the bot might be able to crouch through
VectorCopy(bs->origin, start);
start[2] += 18;
- VectorMA(start, 5, hordir, end);
- VectorSet(mins, -16, -16, -24);
- VectorSet(maxs, 16, 16, 4);
+ //VectorMA(start, 5, hordir, end);
+ //VectorSet(mins, -16, -16, -24);
+ //VectorSet(maxs, 16, 16, 4);
//
//bsptrace = AAS_Trace(start, mins, maxs, end, bs->entitynum, MASK_PLAYERSOLID);
//if (bsptrace.fraction >= 1) movetype = MOVE_CROUCH;
@@ -3306,7 +3303,7 @@ void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) {
//
if (bs->flags & BFL_AVOIDRIGHT) VectorNegate(sideward, sideward);
// try to crouch straight forward?
- if (movetype != MOVE_CROUCH || !trap_BotMoveInDirection(bs->ms, hordir, 400, movetype)) {
+ if (!trap_BotMoveInDirection(bs->ms, hordir, 400, movetype)) {
// perform the movement
if (!trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) {
// flip the avoid direction flag
@@ -3575,7 +3572,7 @@ void BotCheckEvents(bot_state_t *bs, entityState_t *state) {
}
case EV_GLOBAL_SOUND:
{
- if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) {
+ if (state->eventParm < 0 || state->eventParm >= MAX_SOUNDS) {
BotAI_Print(PRT_ERROR, "EV_GLOBAL_SOUND: eventParm (%d) out of range\n", state->eventParm);
break;
}
@@ -3603,11 +3600,19 @@ void BotCheckEvents(bot_state_t *bs, entityState_t *state) {
if (gametype == GT_CTF) {
switch(state->eventParm) {
case GTS_RED_CAPTURE:
+ // BFP - Gesture after capturing the flag
+ if ( BotTeam( bs ) == TEAM_RED && bs->flagcarrier == bs->client ) {
+ trap_EA_Gesture( bs->client );
+ }
bs->blueflagstatus = 0;
bs->redflagstatus = 0;
bs->flagstatuschanged = qtrue;
break; //see BotMatch_CTF
case GTS_BLUE_CAPTURE:
+ // BFP - Gesture after capturing the flag
+ if ( BotTeam( bs ) == TEAM_BLUE && bs->flagcarrier == bs->client ) {
+ trap_EA_Gesture( bs->client );
+ }
bs->blueflagstatus = 0;
bs->redflagstatus = 0;
bs->flagstatuschanged = qtrue;
@@ -3646,7 +3651,7 @@ void BotCheckEvents(bot_state_t *bs, entityState_t *state) {
{
//if this sound is played on the bot
if (state->number == bs->client) {
- if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) {
+ if (state->eventParm < 0 || state->eventParm >= MAX_SOUNDS) {
BotAI_Print(PRT_ERROR, "EV_GENERAL_SOUND: eventParm (%d) out of range\n", state->eventParm);
break;
}
@@ -3704,6 +3709,7 @@ void BotCheckEvents(bot_state_t *bs, entityState_t *state) {
case EV_USE_ITEM12:
case EV_USE_ITEM13:
case EV_USE_ITEM14:
+ case EV_USE_ITEM15:
break;
}
}
@@ -3850,7 +3856,8 @@ void BotDeathmatchAI(bot_state_t *bs, float thinktime) {
Info_SetValueForKey(userinfo, "sex", gender);
trap_SetUserinfo(bs->client, userinfo);
//set the team
- if ( !bs->map_restart && g_gametype.integer != GT_TOURNAMENT ) {
+ if ( !bs->map_restart && g_gametype.integer != GT_TOURNAMENT
+ && g_gametype.integer != GT_SURVIVAL ) { // BFP - Survival
Com_sprintf(buf, sizeof(buf), "team %s", bs->settings.team);
trap_EA_Command(bs->client, buf);
}
@@ -4019,8 +4026,7 @@ void BotSetupDeathmatchAI(void) {
int ent, modelnum;
char model[128];
- gametype = trap_Cvar_VariableIntegerValue("g_gametype");
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
+ gametype = trap_Cvar_VariableIntegerValue( "g_gametype" );
trap_Cvar_Register(&bot_rocketjump, "bot_rocketjump", "1", 0);
trap_Cvar_Register(&bot_grapple, "bot_grapple", "0", 0);
diff --git a/source/game/ai_dmq3.h b/source/game/ai_dmq3.h
index cf01067..f68e06d 100644
--- a/source/game/ai_dmq3.h
+++ b/source/game/ai_dmq3.h
@@ -62,13 +62,11 @@ qboolean EntityIsDead(aas_entityinfo_t *entinfo);
qboolean EntityIsInvisible(aas_entityinfo_t *entinfo);
//returns true if the entity is shooting
qboolean EntityIsShooting(aas_entityinfo_t *entinfo);
-// set a user info key/value pair
-void BotSetUserInfo(bot_state_t *bs, char *key, char *value);
// set the team status (offense, defense etc.)
void BotSetTeamStatus(bot_state_t *bs);
//returns the name of the client
char *ClientName(int client, char *name, int size);
-//returns an simplyfied client name
+//returns a simplified client name
char *EasyClientName(int client, char *name, int size);
//returns the skin used by the client
char *ClientSkin(int client, char *skin, int size);
@@ -151,10 +149,10 @@ bot_waypoint_t *BotCreateWayPoint(char *name, vec3_t origin, int areanum);
//find a waypoint with the given name
bot_waypoint_t *BotFindWayPoint(bot_waypoint_t *waypoints, char *name);
//strstr but case insensitive
-char *stristr(char *str, char *charset);
+const char *stristr(const char *str, const char *charset);
//returns the number of the client with the given name
-int ClientFromName(char *name);
-int ClientOnSameTeamFromName(bot_state_t *bs, char *name);
+int ClientFromName( const char *name );
+int ClientOnSameTeamFromName( bot_state_t *bs, const char *name );
//
int BotPointAreaNum(vec3_t origin);
//
@@ -169,7 +167,7 @@ void BotMapScripts(bot_state_t *bs);
#define CTF_SKIN_BLUETEAM "blue"
extern int gametype; //game type
-extern int maxclients; //maximum number of clients
+extern char mapname[ MAX_QPATH ];
extern vmCvar_t bot_grapple;
extern vmCvar_t bot_rocketjump;
diff --git a/source/game/ai_main.c b/source/game/ai_main.c
index e5f6a47..bd2b0f0 100644
--- a/source/game/ai_main.c
+++ b/source/game/ai_main.c
@@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "ai_cmd.h"
#include "ai_dmnet.h"
#include "ai_vcmd.h"
+#include "ai_bfp.h"
//
#include "chars.h"
@@ -57,7 +58,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MAX_PATH 144
-
//bot states
bot_state_t *botstates[MAX_CLIENTS];
//number of bots
@@ -83,6 +83,7 @@ vmCvar_t bot_interbreedbots;
vmCvar_t bot_interbreedcycle;
vmCvar_t bot_interbreedwrite;
+char mapname[MAX_QPATH];
void ExitLevel( void );
@@ -92,12 +93,12 @@ void ExitLevel( void );
BotAI_Print
==================
*/
-void QDECL BotAI_Print(int type, char *fmt, ...) {
+void QDECL BotAI_Print( int type, const char *fmt, ... ) {
char str[2048];
va_list ap;
va_start(ap, fmt);
- vsprintf(str, fmt, ap);
+ Q_vsprintf(str, fmt, ap);
va_end(ap);
switch(type) {
@@ -147,11 +148,12 @@ void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs,
VectorCopy(trace.plane.normal, bsptrace->plane.normal);
bsptrace->plane.signbits = trace.plane.signbits;
bsptrace->plane.type = trace.plane.type;
- bsptrace->surface.value = trace.surfaceFlags;
+ bsptrace->surface.value = 0;
+ bsptrace->surface.flags = trace.surfaceFlags;
bsptrace->ent = trace.entityNum;
bsptrace->exp_dist = 0;
bsptrace->sidenum = 0;
- bsptrace->contents = 0;
+ bsptrace->contents = trace.contents;
}
/*
@@ -174,23 +176,32 @@ int BotAI_GetClientState( int clientNum, playerState_t *state ) {
return qtrue;
}
+
/*
==================
BotAI_GetEntityState
==================
*/
-int BotAI_GetEntityState( int entityNum, entityState_t *state ) {
- gentity_t *ent;
+qboolean BotAI_GetEntityState( int entityNum, entityState_t *state ) {
+ const gentity_t *ent;
+
+ ent = g_entities + entityNum;
+
+ if ( !ent->inuse || !ent->r.linked ) {
+ memset( state, 0, sizeof( entityState_t ) );
+ return qfalse;
+ }
+
+ if ( ent->r.svFlags & SVF_NOCLIENT ) {
+ memset( state, 0, sizeof( entityState_t ) );
+ return qfalse;
+ }
- ent = &g_entities[entityNum];
- memset( state, 0, sizeof(entityState_t) );
- if (!ent->inuse) return qfalse;
- if (!ent->r.linked) return qfalse;
- if (ent->r.svFlags & SVF_NOCLIENT) return qfalse;
memcpy( state, &ent->s, sizeof(entityState_t) );
return qtrue;
}
+
/*
==================
BotAI_GetSnapshotEntity
@@ -210,6 +221,7 @@ int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state )
return sequence + 1;
}
+
/*
==================
BotAI_BotInitialChat
@@ -253,7 +265,7 @@ void BotTestAAS(vec3_t origin) {
if (bot_testsolid.integer) {
if (!trap_AAS_Initialized()) return;
areanum = BotPointAreaNum(origin);
- if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area");
+ if (areanum) BotAI_Print(PRT_MESSAGE, "\rempty area");
else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area");
}
else if (bot_testclusters.integer) {
@@ -375,26 +387,26 @@ void BotTeamplayReport(void) {
char buf[MAX_INFO_STRING];
BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n");
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
//
if ( !botstates[i] || !botstates[i]->inuse ) continue;
//
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) {
BotReportStatus(botstates[i]);
}
}
BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n");
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
//
if ( !botstates[i] || !botstates[i]->inuse ) continue;
//
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) {
BotReportStatus(botstates[i]);
@@ -500,11 +512,11 @@ void BotSetInfoConfigString(bot_state_t *bs) {
break;
}
}
- cs = va("l\\%s\\c\\%s\\a\\%s",
+ cs = va("l\\%s\\c\\%s\\a\\%s",
leader,
carrying,
action);
- trap_SetConfigstring (CS_BOTINFO + bs->client, cs);
+ trap_SetConfigstring (CS_BOTINFO + bs->client, cs);
}
/*
@@ -516,14 +528,14 @@ void BotUpdateInfoConfigStrings(void) {
int i;
char buf[MAX_INFO_STRING];
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
//
if ( !botstates[i] || !botstates[i]->inuse )
continue;
//
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n")))
+ if (!buf[0] || !*Info_ValueForKey(buf, "n"))
continue;
BotSetInfoConfigString(botstates[i]);
}
@@ -607,7 +619,7 @@ void BotInterbreedEndMatch(void) {
bot_interbreedmatchcount = 0;
//
trap_Cvar_Update(&bot_interbreedwrite);
- if (strlen(bot_interbreedwrite.string)) {
+ if (bot_interbreedwrite.string[0] != '\0') {
BotWriteInterbreeded(bot_interbreedwrite.string);
trap_Cvar_Set("bot_interbreedwrite", "");
}
@@ -631,8 +643,15 @@ void BotInterbreeding(void) {
ExitLevel();
return;
}
+
+ if ( gametype != GT_SURVIVAL ) { // BFP - Survival
+ trap_Cvar_Set("g_gametype", va("%d", GT_SURVIVAL));
+ ExitLevel();
+ return;
+ }
+
//shutdown all the bots
- for (i = 0; i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
if (botstates[i] && botstates[i]->inuse) {
BotAIShutdownClient(botstates[i]->client, qfalse);
}
@@ -751,7 +770,7 @@ void BotChangeViewAngles(bot_state_t *bs, float thinktime) {
//
if (bot_challenge.integer) {
//smooth slowdown view model
- diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]));
+ diff = fabs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]));
anglespeed = diff * factor;
if (anglespeed > maxchange) anglespeed = maxchange;
bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i],
@@ -792,6 +811,7 @@ void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3]
vec3_t angles, forward, right;
short temp;
int j;
+ float f, r, u, m;
//clear the whole structure
memset(ucmd, 0, sizeof(usercmd_t));
@@ -846,22 +866,39 @@ void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3]
AngleVectors(angles, forward, right, NULL);
//bot input speed is in the range [0, 400]
bi->speed = bi->speed * 127 / 400;
+
//set the view independent movement
- ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed;
- ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed;
- ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed;
- //normal keyboard movement
- if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove += 127;
- if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove -= 127;
- if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove -= 127;
- if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove += 127;
+ f = DotProduct( forward, bi->dir );
+ r = DotProduct( right, bi->dir );
+ u = fabs( forward[2] ) * bi->dir[2];
+ m = fabs( f );
+
+ if ( fabs( r ) > m ) {
+ m = fabs( r );
+ }
+
+ if ( fabs( u ) > m) {
+ m = fabs( u );
+ }
+
+ if ( m > 0 ) {
+ f *= bi->speed / m;
+ r *= bi->speed / m;
+ u *= bi->speed / m;
+ }
+
+ ucmd->forwardmove = f;
+ ucmd->rightmove = r;
+ ucmd->upmove = u;
+
+ if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove = 127;
+ if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove = -127;
+ if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove = -127;
+ if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove = 127;
//jump/moveup
- if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127;
+ if (bi->actionflags & ACTION_JUMP) ucmd->upmove = 127;
//crouch/movedown
- if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127;
- //
- //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove);
- //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime);
+ if (bi->actionflags & ACTION_CROUCH) ucmd->upmove = -127;
}
/*
@@ -963,19 +1000,19 @@ int BotAI(int client, float thinktime) {
{ /*ConfigStringModified*/ }
else if (!Q_stricmp(buf, "print")) {
//remove first and last quote from the chat message
- memmove(args, args+1, (int)strlen(args));
+ memmove(args, args+1, strlen(args));
args[strlen(args)-1] = '\0';
trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args);
}
else if (!Q_stricmp(buf, "chat")) {
//remove first and last quote from the chat message
- memmove(args, args+1, (int)strlen(args));
+ memmove(args, args+1, strlen(args));
args[strlen(args)-1] = '\0';
trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
}
else if (!Q_stricmp(buf, "tchat")) {
//remove first and last quote from the chat message
- memmove(args, args+1, (int)strlen(args));
+ memmove(args, args+1, strlen(args));
args[strlen(args)-1] = '\0';
trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args);
}
@@ -999,9 +1036,15 @@ int BotAI(int client, float thinktime) {
bs->eye[2] += bs->cur_ps.viewheight;
//get the area the bot is in
bs->areanum = BotPointAreaNum(bs->origin);
+ // BFP - Reset per-frame button state
+ BotBFPBeginFrame( bs );
//the real AI
BotDeathmatchAI(bs, thinktime);
//set the weapon selection every AI frame
+ // BFP - Avoid "weapon number out of range" engine print error
+ if ( bs->weaponnum <= WP_NONE ) {
+ bs->weaponnum = WP_GAUNTLET;
+ }
trap_EA_SelectWeapon(bs->client, bs->weaponnum);
//subtract the delta angles
for (j = 0; j < 3; j++) {
@@ -1081,7 +1124,7 @@ void BotReadSessionData(bot_state_t *bs) {
var = va( "botsession%i", bs->client );
trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
- sscanf(s,
+ Q_sscanf(s,
"%i %i %i %i %i %i %i %i"
" %f %f %f"
" %f %f %f"
@@ -1111,13 +1154,16 @@ void BotReadSessionData(bot_state_t *bs) {
BotAISetupClient
==============
*/
-int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) {
+qboolean BotAISetupClient( int client, struct bot_settings_s *settings, qboolean restart ) {
char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH];
bot_state_t *bs;
int errnum;
if (!botstates[client]) botstates[client] = G_Alloc(sizeof(bot_state_t));
bs = botstates[client];
+ if ( bs == NULL ) {
+ return qfalse;
+ }
if (bs && bs->inuse) {
BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client);
@@ -1225,7 +1271,7 @@ int BotAIShutdownClient(int client, qboolean restart) {
}
trap_BotFreeMoveState(bs->ms);
- //free the goal state`
+ //free the goal state
trap_BotFreeGoalState(bs->gs);
//free the chat file
trap_BotFreeChatState(bs->cs);
@@ -1248,6 +1294,7 @@ int BotAIShutdownClient(int client, qboolean restart) {
return qtrue;
}
+
/*
==============
BotResetState
@@ -1281,6 +1328,8 @@ void BotResetState(bot_state_t *bs) {
BotFreeWaypoints(bs->patrolpoints);
//reset the whole state
memset(bs, 0, sizeof(bot_state_t));
+ // BFP - Reset BFP bot state
+ BotBFPResetState( bs );
//copy back some state stuff that should not be reset
bs->ms = movestate;
bs->gs = goalstate;
@@ -1307,16 +1356,18 @@ BotAILoadMap
==============
*/
int BotAILoadMap( int restart ) {
+ char serverinfo[MAX_INFO_STRING];
int i;
- vmCvar_t mapname;
- if (!restart) {
- trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
- trap_BotLibLoadMap( mapname.string );
+ trap_GetServerinfo( serverinfo, sizeof( serverinfo ) );
+ Q_strncpyz( mapname, Info_ValueForKey( serverinfo, "mapname" ), sizeof( mapname ) );
+
+ if ( !restart ) {
+ trap_BotLibLoadMap( mapname );
}
- for (i = 0; i < MAX_CLIENTS; i++) {
- if (botstates[i] && botstates[i]->inuse) {
+ for ( i = 0; i < level.maxclients; i++ ) {
+ if ( botstates[i] && botstates[i]->inuse ) {
BotResetState( botstates[i] );
botstates[i]->setupcount = 4;
}
@@ -1340,6 +1391,7 @@ int BotAIStartFrame(int time) {
static int local_time;
static int botlib_residual;
static int lastbotthink_time;
+ static qboolean skip[MAX_GENTITIES], *s;
G_CheckBotSpawn();
@@ -1362,7 +1414,7 @@ int BotAIStartFrame(int time) {
if (bot_pause.integer) {
// execute bot user commands every frame
- for( i = 0; i < MAX_CLIENTS; i++ ) {
+ for( i = 0; i < level.maxclients; i++ ) {
if( !botstates[i] || !botstates[i]->inuse ) {
continue;
}
@@ -1416,18 +1468,31 @@ int BotAIStartFrame(int time) {
if (!trap_AAS_Initialized()) return qfalse;
//update entities in the botlib
- for (i = 0; i < MAX_GENTITIES; i++) {
+ s = skip;
+ ent = g_entities;
+ for ( i = 0; i < level.num_entities; i++, s++, ent++ ) {
ent = &g_entities[i];
- if (!ent->inuse) {
- trap_BotLibUpdateEntity(i, NULL);
+ if ( !ent->inuse || !ent->r.linked || ent->r.svFlags & SVF_NOCLIENT ) {
+ if ( *s == qfalse ) {
+ *s = qtrue;
+ trap_BotLibUpdateEntity( i, NULL );
+ }
continue;
}
- if (!ent->r.linked) {
- trap_BotLibUpdateEntity(i, NULL);
+ // do not update missiles
+ if ( ent->s.eType == ET_MISSILE && ent->s.weapon != WP_GRAPPLING_HOOK ) {
+ if ( *s == qfalse ) {
+ *s = qtrue;
+ trap_BotLibUpdateEntity( i, NULL );
+ }
continue;
}
- if (ent->r.svFlags & SVF_NOCLIENT) {
- trap_BotLibUpdateEntity(i, NULL);
+ // do not update event only entities
+ if ( ent->s.eType > ET_EVENTS ) {
+ if ( *s == qfalse ) {
+ *s = qtrue;
+ trap_BotLibUpdateEntity( i, NULL );
+ }
continue;
}
// do not update missiles
@@ -1459,7 +1524,7 @@ int BotAIStartFrame(int time) {
state.groundent = ent->s.groundEntityNum;
state.modelindex = ent->s.modelindex;
state.modelindex2 = ent->s.modelindex2;
- state.frame = ent->s.frame;
+ // state.frame = ent->s.frame;
state.event = ent->s.event;
state.eventParm = ent->s.eventParm;
state.powerups = ent->s.powerups;
@@ -1467,7 +1532,8 @@ int BotAIStartFrame(int time) {
state.torsoAnim = ent->s.torsoAnim;
state.weapon = ent->s.weapon;
//
- trap_BotLibUpdateEntity(i, &state);
+ *s = qfalse;
+ trap_BotLibUpdateEntity( i, &state );
}
BotAIRegularUpdate();
@@ -1476,7 +1542,7 @@ int BotAIStartFrame(int time) {
floattime = trap_AAS_Time();
// execute scheduled bot AI
- for( i = 0; i < MAX_CLIENTS; i++ ) {
+ for( i = 0; i < level.maxclients; i++ ) {
if( !botstates[i] || !botstates[i]->inuse ) {
continue;
}
@@ -1496,7 +1562,7 @@ int BotAIStartFrame(int time) {
// execute bot user commands every frame
- for( i = 0; i < MAX_CLIENTS; i++ ) {
+ for( i = 0; i < level.maxclients; i++ ) {
if( !botstates[i] || !botstates[i]->inuse ) {
continue;
}
@@ -1505,6 +1571,10 @@ int BotAIStartFrame(int time) {
}
BotUpdateInput(botstates[i], time, elapsed_time);
+ // BFP - BFP Battle nodes
+ BotBFPUniversalCheck( botstates[i] );
+ // BFP - BFP buttons
+ BotBFPApplyButtons( botstates[i], &botstates[i]->lastucmd );
trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
}
@@ -1516,65 +1586,95 @@ int BotAIStartFrame(int time) {
BotInitLibrary
==============
*/
-int BotInitLibrary(void) {
- char buf[144];
+int BotInitLibrary( void ) {
+ char buf[MAX_CVAR_VALUE_STRING];
//set the maxclients and maxentities library variables before calling BotSetupLibrary
- trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf));
- if (!strlen(buf)) strcpy(buf, "8");
- trap_BotLibVarSet("maxclients", buf);
+ trap_Cvar_VariableStringBuffer( "sv_maxclients", buf, sizeof( buf ) );
+ if ( !buf[0] )
+ strcpy( buf, "8" );
+ trap_BotLibVarSet( "maxclients", buf );
+
Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES);
trap_BotLibVarSet("maxentities", buf);
//bsp checksum
trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "sv_mapChecksum", buf );
+
//maximum number of aas links
trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "max_aaslinks", buf );
+
//maximum number of items in a level
trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "max_levelitems", buf );
+
//game type
trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf));
- if (!strlen(buf)) strcpy(buf, "0");
+ if ( !buf[0] )
+ strcpy( buf, "0" );
trap_BotLibVarSet("g_gametype", buf);
//bot developer mode and log file
trap_BotLibVarSet("bot_developer", bot_developer.string);
trap_BotLibVarSet("log", buf);
//no chatting
trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("nochat", "0");
+ if ( buf[0] )
+ trap_BotLibVarSet( "nochat", "0" );
+
//visualize jump pads
trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "bot_visualizejumppads", buf );
+
//forced clustering calculations
trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "forceclustering", buf );
+
//forced reachability calculations
trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "forcereachability", buf );
+
//force writing of AAS to file
trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "forcewrite", buf );
+
//no AAS optimization
trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("aasoptimize", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "aasoptimize", buf );
+
//
trap_Cvar_VariableStringBuffer("bot_saveroutingcache", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("saveroutingcache", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "saveroutingcache", buf );
+
//reload instead of cache bot character files
trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf));
- if (!strlen(buf)) strcpy(buf, "0");
+ if ( !buf[0] )
+ strcpy( buf, "0" );
trap_BotLibVarSet("bot_reloadcharacters", buf);
//base directory
trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("basedir", buf);
+ if ( buf[0] )
+ trap_BotLibVarSet( "basedir", buf );
+
+ //home directory
+ trap_Cvar_VariableStringBuffer("fs_homepath", buf, sizeof(buf));
+ if ( buf[0] )
+ trap_BotLibVarSet( "homedir", buf );
+
//game directory
- trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("gamedir", buf);
- //cd directory
- trap_Cvar_VariableStringBuffer("fs_cdpath", buf, sizeof(buf));
- if (strlen(buf)) trap_BotLibVarSet("cddir", buf);
+ trap_Cvar_VariableStringBuffer( "fs_game", buf, sizeof( buf ) );
+ if ( buf[0] )
+ trap_BotLibVarSet( "gamedir", buf );
+
//setup the bot library
return trap_BotLibSetup();
}
@@ -1609,7 +1709,9 @@ int BotAISetup( int restart ) {
memset( botstates, 0, sizeof(botstates) );
errnum = BotInitLibrary();
- if (errnum != BLERR_NOERROR) return qfalse;
+ if ( errnum != BLERR_NOERROR )
+ return qfalse;
+
return qtrue;
}
@@ -1625,8 +1727,8 @@ int BotAIShutdown( int restart ) {
//if the game is restarted for a tournament
if ( restart ) {
//shutdown all the bots in the botlib
- for (i = 0; i < MAX_CLIENTS; i++) {
- if (botstates[i] && botstates[i]->inuse) {
+ for ( i = 0; i < level.maxclients; i++ ) {
+ if ( botstates[i] && botstates[i]->inuse ) {
BotAIShutdownClient(botstates[i]->client, restart);
}
}
diff --git a/source/game/ai_main.h b/source/game/ai_main.h
index 5d0e9ec..f2ff169 100644
--- a/source/game/ai_main.h
+++ b/source/game/ai_main.h
@@ -128,7 +128,7 @@ typedef struct bot_state_s
playerState_t cur_ps; //current player state
int last_eFlags; //last ps flags
usercmd_t lastucmd; //usercmd from last frame
- int entityeventTime[1024]; //last entity event time
+ int entityeventTime[MAX_GENTITIES]; //last entity event time
//
bot_settings_t settings; //several bot settings
int (*ainode)(struct bot_state_s *bs); //current AI node
@@ -249,7 +249,7 @@ typedef struct bot_state_s
float leadmessage_time; //last time a messaged was sent to the team mate
float leadbackup_time; //time backing up towards team mate
//
- char teamleader[32]; //netname of the team leader
+ char teamleader[MAX_NETNAME]; //netname of the team leader
float askteamleader_time; //time asked for team leader
float becometeamleader_time; //time the bot will become the team leader
float teamgiveorders_time; //time to give team orders
@@ -257,7 +257,6 @@ typedef struct bot_state_s
int numteammates; //number of team mates
int redflagstatus; //0 = at base, 1 = not at base
int blueflagstatus; //0 = at base, 1 = not at base
- int neutralflagstatus; //0 = at base, 1 = our team has flag, 2 = enemy team has flag, 3 = enemy team dropped the flag
int flagstatuschanged; //flag status changed
int forceorders; //true if forced to give orders
int flagcarrier; //team mate carrying the enemy flag
@@ -277,6 +276,36 @@ typedef struct bot_state_s
bot_waypoint_t *patrolpoints; //patrol points
bot_waypoint_t *curpatrolpoint; //current patrol point the bot is going for
int patrolflags; //patrol flags
+
+ // BFP - BFP combat AI (ai_bfp.c)
+ int bfpButtons; // BFP button bits (BUTTON_ENABLEFLIGHT/KI_USE/KI_CHARGE/MELEE) requested this frame
+ qboolean bfpKiRecharging; // true while the bot is deliberately charging ki
+ float bfpKiRechargeInterrupted_time; // time the bot's ki charge was broken by taking damage
+ int bfpLastHealth; // health value the BFP AI last saw, -1 until initialized
+
+ float bfpLastMelee_time; // time the bot last pressed BUTTON_MELEE, for a breather between swings
+ float bfpStrafeFlip_time; // next time the bot's forced close-range strafe direction may flip
+
+ qboolean bfpFlightDecision; // cached on/off flight decision, held for a while instead of re-rolled every think frame
+ qboolean bfpLastFlightDecision; // previous flight decision, to detect toggles
+ float bfpFlightReroll_time; // next time the flight decision above is allowed to change
+
+ int bfpZanzokenPhase; // 0=idle, 1=first tap held, 2=released waiting gap, 3=second tap held (see BotBFPCheckZanzoken)
+ float bfpZanzokenPhaseEnd_time; // when the current phase ends and the machine advances
+ int bfpZanzokenDir; // +1 or -1, which direction the in-progress zanzoken attempt is going
+ float bfpZanzokenHitstunRetry_time; // next time BotBFPCheckZanzoken() is allowed to re-roll its escape chance while hitstun is active, so it can retry a few times over the ~3s stun instead of only once
+
+ int bfpRightmoveOverride; // value to force onto lastucmd.rightmove this frame while a zanzoken attempt is in progress
+ qboolean bfpRightmoveOverrideActive; // true while bfpRightmoveOverride should be applied (0 is a valid override value, so this can't be inferred from the int alone)
+
+ qboolean bfpForceAttackOff; // forces and stops attacking
+ float bfpKiCompensate_time; // last real time BotBFPCompensateKiCharge() topped up ki, so it can scale the gain by actual elapsed time
+ float bfpSixthSenseStep_time; // cooldown between BotBFPCheckSixthSense()'s instinctive step-back reaction
+ float bfpChargeAutoFireStartTime; // time when chargeAutoFire weapon started firing, used for 4s limit
+ int bfpLastWeaponState; // last weaponstate, used to detect transitions (e.g. SBEAM ending)
+
+ float bfpEvadeTime; // evasion mode bot time
+ vec3_t bfpEvadeDir; // direction in which to move during the evasion
} bot_state_t;
//resets the whole bot state
@@ -290,10 +319,10 @@ extern float floattime;
#define FloatTime() floattime
// from the game source
-void QDECL BotAI_Print(int type, char *fmt, ...);
+void QDECL BotAI_Print( int type, const char *fmt, ... );
void QDECL QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... );
void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask);
int BotAI_GetClientState( int clientNum, playerState_t *state );
-int BotAI_GetEntityState( int entityNum, entityState_t *state );
+qboolean BotAI_GetEntityState( int entityNum, entityState_t *state );
int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state );
int BotTeamLeader(bot_state_t *bs);
diff --git a/source/game/ai_team.c b/source/game/ai_team.c
index 56ebe84..a202b3c 100644
--- a/source/game/ai_team.c
+++ b/source/game/ai_team.c
@@ -83,16 +83,12 @@ BotNumTeamMates
int BotNumTeamMates(bot_state_t *bs) {
int i, numplayers;
char buf[MAX_INFO_STRING];
- static int maxclients;
-
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
numplayers = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
@@ -127,28 +123,25 @@ int BotSortTeamMatesByBaseTravelTime(bot_state_t *bs, int *teammates, int maxtea
int i, j, k, numteammates, traveltime;
char buf[MAX_INFO_STRING];
- static int maxclients;
int traveltimes[MAX_CLIENTS];
bot_goal_t *goal = NULL;
- if (gametype == GT_CTF || gametype == GT_1FCTF) {
+ if (gametype == GT_CTF) {
if (BotTeam(bs) == TEAM_RED)
goal = &ctf_redflag;
else
goal = &ctf_blueflag;
}
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
numteammates = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
- if (BotSameTeam(bs, i)) {
+ if (BotSameTeam(bs, i) && goal) {
//
traveltime = BotClientTravelTimeToGoal(i, goal);
//
@@ -301,6 +294,7 @@ void BotCTFOrders_BothFlagsNotAtBase(bot_state_t *bs) {
int teammates[MAX_CLIENTS];
char name[MAX_NETNAME], carriername[MAX_NETNAME];
+ memset( teammates, 0, sizeof( teammates ) );
numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates));
BotSortTeamMatesByTaskPreference(bs, teammates, numteammates);
//different orders based on the number of team mates
@@ -659,6 +653,7 @@ void BotCTFOrders_BothFlagsAtBase(bot_state_t *bs) {
int teammates[MAX_CLIENTS];
char name[MAX_NETNAME];
+ memset( teammates, 0, sizeof( teammates ) );
//sort team mates by travel time to base
numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates));
//sort team mates by CTF preference
@@ -839,23 +834,19 @@ void BotCreateGroup(bot_state_t *bs, int *teammates, int groupsize) {
==================
BotTeamOrders
- FIXME: defend key areas?
+FIXME: defend key areas?
==================
*/
void BotTeamOrders(bot_state_t *bs) {
int teammates[MAX_CLIENTS];
int numteammates, i;
char buf[MAX_INFO_STRING];
- static int maxclients;
-
- if (!maxclients)
- maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
numteammates = 0;
- for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
+ for (i = 0; i < level.maxclients; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
- if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
+ if (!buf[0] || !*Info_ValueForKey(buf, "n")) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
//
@@ -970,8 +961,7 @@ void BotTeamAI(bot_state_t *bs) {
trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
BotSayVoiceTeamOrder(bs, -1, VOICECHAT_STARTLEADER);
ClientName(bs->client, netname, sizeof(netname));
- strncpy(bs->teamleader, netname, sizeof(bs->teamleader));
- bs->teamleader[sizeof(bs->teamleader)] = '\0';
+ Q_strncpyz( bs->teamleader, netname, sizeof( bs->teamleader ) );
bs->becometeamleader_time = 0;
}
return;
diff --git a/source/game/ai_vcmd.c b/source/game/ai_vcmd.c
index eae5f0d..c009f4c 100644
--- a/source/game/ai_vcmd.c
+++ b/source/game/ai_vcmd.c
@@ -61,7 +61,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
typedef struct voiceCommand_s
{
char *cmd;
- void (*func)(bot_state_t *bs, int client, int mode);
+ void(*func)(bot_state_t *bs, int client, int mode);
} voiceCommand_t;
/*
@@ -467,7 +467,7 @@ voiceCommand_t voiceCommands[] = {
};
int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voiceChat) {
- int i, voiceOnly, clientNum, color;
+ int i, clientNum;
char *ptr, buf[MAX_MESSAGE_SIZE], *cmd;
if (!TeamPlayIsOn()) {
@@ -480,15 +480,14 @@ int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voiceChat) {
Q_strncpyz(buf, voiceChat, sizeof(buf));
cmd = buf;
- for (ptr = cmd; *cmd && *cmd > ' '; cmd++);
+ for (/*ptr = cmd*/; *cmd && *cmd > ' '; cmd++);
while (*cmd && *cmd <= ' ') *cmd++ = '\0';
- voiceOnly = atoi(ptr);
+
for (ptr = cmd; *cmd && *cmd > ' '; cmd++);
while (*cmd && *cmd <= ' ') *cmd++ = '\0';
clientNum = atoi(ptr);
- for (ptr = cmd; *cmd && *cmd > ' '; cmd++);
+ for (/*ptr = cmd*/; *cmd && *cmd > ' '; cmd++);
while (*cmd && *cmd <= ' ') *cmd++ = '\0';
- color = atoi(ptr);
if (!BotSameTeam(bs, clientNum)) {
return qfalse;
diff --git a/source/game/be_ai_move.h b/source/game/be_ai_move.h
index fb80819..592d50f 100644
--- a/source/game/be_ai_move.h
+++ b/source/game/be_ai_move.h
@@ -42,7 +42,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MFL_ONGROUND 2 //bot is in the ground
#define MFL_SWIMMING 4 //bot is swimming
#define MFL_AGAINSTLADDER 8 //bot is against a ladder
-#define MFL_WATERJUMP 16 //bot is waterjumping
+// BFP - MFL_WATERJUMP is unused
+// #define MFL_WATERJUMP 16 //bot is waterjumping
#define MFL_TELEPORTED 32 //bot is being teleported
#define MFL_GRAPPLEPULL 64 //bot is being pulled by the grapple
#define MFL_ACTIVEGRAPPLE 128 //bot is using the grapple hook
diff --git a/source/game/bg_events.h b/source/game/bg_events.h
new file mode 100644
index 0000000..2c79b91
--- /dev/null
+++ b/source/game/bg_events.h
@@ -0,0 +1,129 @@
+#ifdef EVENT_ENUMS
+ #define DECLARE_EVENT( ev ) ev
+#endif
+
+#ifdef EVENT_STRINGS
+ #define DECLARE_EVENT( ev ) #ev
+#endif
+
+// BFP - Highly modified to match original BFP demo networking events
+DECLARE_EVENT( EV_NONE ), // 0
+
+DECLARE_EVENT( EV_UNUSED_INDEX1 ), // 1
+DECLARE_EVENT( EV_UNUSED_INDEX2 ), // 2
+DECLARE_EVENT( EV_UNUSED_INDEX3 ), // 3
+DECLARE_EVENT( EV_UNUSED_INDEX4 ), // 4
+DECLARE_EVENT( EV_UNUSED_INDEX5 ), // 5
+DECLARE_EVENT( EV_UNUSED_INDEX6 ), // 6
+DECLARE_EVENT( EV_UNUSED_INDEX7 ), // 7
+DECLARE_EVENT( EV_UNUSED_INDEX8 ), // 8
+DECLARE_EVENT( EV_UNUSED_INDEX9 ), // 9
+
+DECLARE_EVENT( EV_MELEE_READY ), // BFP - EV_MELEE_READY (10), original BFP: preparing melee
+DECLARE_EVENT( EV_MELEE ), // BFP - EV_MELEE (11), melee attack
+DECLARE_EVENT( EV_UNUSED_INDEX12 ), // 12
+DECLARE_EVENT( EV_TIER_RESET ), // EV_TIER_RESET (13), reset tier when the player respawns and changes to the default or a bit less ki energy?
+// BFP - EV_TIER_0-4 (14-18), when the player frags, increases their PL and obtains a new skill (in the last tier, transforms)
+DECLARE_EVENT( EV_TIER_0 ), // 14
+DECLARE_EVENT( EV_TIER_1 ), // 15
+DECLARE_EVENT( EV_TIER_2 ), // 16
+DECLARE_EVENT( EV_TIER_3 ), // 17
+DECLARE_EVENT( EV_TIER_4 ), // 18
+
+DECLARE_EVENT( EV_ZANZOKEN_IN ), // BFP - EV_ZANZOKEN_IN (19), Short-Range Teleport (Zanzoken)
+DECLARE_EVENT( EV_ZANZOKEN_OUT ), // BFP - EV_ZANZOKEN_OUT (20), stop/leaves zanzoken
+DECLARE_EVENT( EV_KI_BOOST ), // BFP - EV_KI_BOOST (21), original BFP: enables aura and ki trail
+DECLARE_EVENT( EV_ENABLE_FLIGHT ), // BFP - EV_ENABLE_FLIGHT (22), enable flight
+DECLARE_EVENT( EV_FOOTSTEP ), // 23
+DECLARE_EVENT( EV_FOOTSTEP_METAL ), // 24
+DECLARE_EVENT( EV_FOOTSPLASH ), // 25
+DECLARE_EVENT( EV_FOOTWADE ), // 26
+DECLARE_EVENT( EV_SWIM ), // 27
+DECLARE_EVENT( EV_STEP_4 ), // 28
+DECLARE_EVENT( EV_STEP_8 ), // 29
+DECLARE_EVENT( EV_STEP_12 ), // 30
+DECLARE_EVENT( EV_STEP_16 ), // 31
+
+DECLARE_EVENT( EV_FALL_SHORT ), // 32
+DECLARE_EVENT( EV_FALL_MEDIUM ), // 33
+DECLARE_EVENT( EV_FALL_FAR ), // 34
+DECLARE_EVENT( EV_JUMP_PAD ), // boing sound at origin, jump sound on player
+DECLARE_EVENT( EV_JUMP ), // 36
+DECLARE_EVENT( EV_JUMP_2 ), // 37
+
+DECLARE_EVENT( EV_WATER_TOUCH ), // foot touches
+DECLARE_EVENT( EV_WATER_LEAVE ), // foot leaves
+DECLARE_EVENT( EV_WATER_UNDER ), // head touches
+DECLARE_EVENT( EV_WATER_CLEAR ), // head leaves
+DECLARE_EVENT( EV_ITEM_PICKUP ), // normal item pickups are predictable
+DECLARE_EVENT( EV_GLOBAL_ITEM_PICKUP ), // powerup / team sounds are broadcast to everyone
+DECLARE_EVENT( EV_NOAMMO ), // 44
+DECLARE_EVENT( EV_CHANGE_WEAPON ), // 45
+DECLARE_EVENT( EV_FIRE_WEAPON ), // 46
+
+DECLARE_EVENT( EV_USE_ITEM0 ), // 47
+DECLARE_EVENT( EV_USE_ITEM1 ), // 48
+DECLARE_EVENT( EV_USE_ITEM2 ), // 49
+DECLARE_EVENT( EV_USE_ITEM3 ), // 50
+DECLARE_EVENT( EV_USE_ITEM4 ), // 51
+DECLARE_EVENT( EV_USE_ITEM5 ), // 52
+DECLARE_EVENT( EV_USE_ITEM6 ), // 53
+DECLARE_EVENT( EV_USE_ITEM7 ), // 54
+DECLARE_EVENT( EV_USE_ITEM8 ), // 55
+DECLARE_EVENT( EV_USE_ITEM9 ), // 56
+DECLARE_EVENT( EV_USE_ITEM10 ), // 57
+DECLARE_EVENT( EV_USE_ITEM11 ), // 58
+DECLARE_EVENT( EV_USE_ITEM12 ), // 59
+DECLARE_EVENT( EV_USE_ITEM13 ), // 60
+DECLARE_EVENT( EV_USE_ITEM14 ), // 61
+DECLARE_EVENT( EV_USE_ITEM15 ), // 62
+
+DECLARE_EVENT( EV_ITEM_RESPAWN ), // 63
+DECLARE_EVENT( EV_ITEM_POP ), // 64
+DECLARE_EVENT( EV_PLAYER_TELEPORT_IN ), // 65
+DECLARE_EVENT( EV_PLAYER_TELEPORT_OUT ), // 66
+DECLARE_EVENT( EV_GRENADE_BOUNCE ), // eventParm will be the soundindex
+
+DECLARE_EVENT( EV_GENERAL_SOUND ), // 68
+DECLARE_EVENT( EV_GLOBAL_SOUND ), // no attenuation
+DECLARE_EVENT( EV_GLOBAL_TEAM_SOUND ), // 70
+DECLARE_EVENT( EV_BULLET_HIT_FLESH ), // 71
+DECLARE_EVENT( EV_BULLET_HIT_WALL ), // 72
+DECLARE_EVENT( EV_MISSILE_HIT ), // 73
+DECLARE_EVENT( EV_MISSILE_MISS ), // 74
+DECLARE_EVENT( EV_MISSILE_MISS_METAL ), // 75
+DECLARE_EVENT( EV_MISSILE_DETONATE ), // BFP - EV_MISSILE_DETONATE (76), in some moment, ki attack beam/projectile explodes
+DECLARE_EVENT( EV_RAILTRAIL ), // 77
+DECLARE_EVENT( EV_SHOTGUN ), // otherEntity is the shooter
+DECLARE_EVENT( EV_UNUSED_INDEX79 ), // 79
+
+DECLARE_EVENT( EV_PAIN ), // 80
+DECLARE_EVENT( EV_DEATH1 ), // 81
+DECLARE_EVENT( EV_DEATH2 ), // 82
+DECLARE_EVENT( EV_DEATH3 ), // 83
+DECLARE_EVENT( EV_OBITUARY ), // 84
+
+DECLARE_EVENT( EV_POWERUP_QUAD ), // 85
+DECLARE_EVENT( EV_POWERUP_BATTLESUIT ), // 86
+DECLARE_EVENT( EV_UNUSED_INDEX87 ), // 87
+DECLARE_EVENT( EV_GIB_PLAYER ), // gib a previously living player
+
+DECLARE_EVENT( EV_UNUSED_INDEX89 ), // 89
+DECLARE_EVENT( EV_UNUSED_INDEX90 ), // 90
+DECLARE_EVENT( EV_SCOREPLUM ), // score plum
+
+DECLARE_EVENT( EV_TAUNT ), // 92
+DECLARE_EVENT( EV_UNUSED_INDEX93 ), // 93
+DECLARE_EVENT( EV_UNUSED_INDEX94 ), // 94
+DECLARE_EVENT( EV_UNUSED_INDEX95 ), // 95
+DECLARE_EVENT( EV_BLINDING ), // BFP - EV_BLINDING (reusing 96), when ki attack explodes after some time
+DECLARE_EVENT( EV_DEBUG_LINE ), // 97
+DECLARE_EVENT( EV_STOPLOOPINGSOUND ), // 98
+
+DECLARE_EVENT( EV_SPARK ) // BFP - EV_SPARK (99), beam struggle
+
+#ifdef EVENT_ENUMS
+ , DECLARE_EVENT( EV_MAX )
+#endif
+
+#undef DECLARE_EVENT
diff --git a/source/game/bg_lib.c b/source/game/bg_lib.c
index 617dc90..3c916d2 100644
--- a/source/game/bg_lib.c
+++ b/source/game/bg_lib.c
@@ -1,4 +1,4 @@
-//
+// Copyright (C) 1999-2000 Id Software, Inc.
//
// bg_lib,c -- standard C library replacement routines used by code
// compiled for the virtual machine
@@ -38,13 +38,6 @@
* SUCH DAMAGE.
*/
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93";
-#endif
-static const char rcsid[] =
-#endif /* LIBC_SCCS and not lint */
-
// bk001127 - needed for DLL's
#if !defined( Q3_VM )
typedef int cmp_t(const void *, const void *);
@@ -54,7 +47,7 @@ static char* med3(char *, char *, char *, cmp_t *);
static void swapfunc(char *, char *, int, int);
#ifndef min
-#define min(a, b) (a) < (b) ? a : b
+#define min(a, b) ((a) < (b) ? a : b)
#endif
/*
@@ -192,10 +185,9 @@ loop: SWAPINIT(a, es);
// this file is excluded from release builds because of intrinsics
-// bk001211 - gcc errors on compiling strcpy: parse error before `__extension__'
#if defined ( Q3_VM )
-int strlen( const char *string ) {
+size_t strlen( const char *string ) {
const char *s;
s = string;
@@ -214,7 +206,8 @@ char *strcat( char *strDestination, const char *strSource ) {
s++;
}
while ( *strSource ) {
- *s++ = *strSource++;
+ *s = *strSource;
+ s++; strSource++;
}
*s = 0;
return strDestination;
@@ -225,7 +218,8 @@ char *strcpy( char *strDestination, const char *strSource ) {
s = strDestination;
while ( *strSource ) {
- *s++ = *strSource++;
+ *s = *strSource;
+ s++; strSource++;
}
*s = 0;
return strDestination;
@@ -248,9 +242,13 @@ char *strchr( const char *string, int c ) {
}
string++;
}
- return (char *)0;
+ if(c)
+ return NULL;
+ else
+ return (char *) string;
}
+
char *strstr( const char *string, const char *strCharSet ) {
while ( *string ) {
int i;
@@ -267,12 +265,7 @@ char *strstr( const char *string, const char *strCharSet ) {
}
return (char *)0;
}
-#endif // bk001211
-// bk001120 - presumably needed for Mac
-//#if !defined(_MSC_VER) && !defined(__linux__)
-// bk001127 - undid undo
-#if defined ( Q3_VM )
int tolower( int c ) {
if ( c >= 'A' && c <= 'Z' ) {
c += 'a' - 'A';
@@ -280,7 +273,6 @@ int tolower( int c ) {
return c;
}
-
int toupper( int c ) {
if ( c >= 'a' && c <= 'z' ) {
c += 'A' - 'a';
@@ -288,9 +280,6 @@ int toupper( int c ) {
return c;
}
-#endif
-//#ifndef _MSC_VER
-
void *memmove( void *dest, const void *src, size_t count ) {
int i;
@@ -305,7 +294,7 @@ void *memmove( void *dest, const void *src, size_t count ) {
}
return dest;
}
-
+#endif
#if 0
@@ -332,211 +321,16 @@ char *strncpy( char *strDest, const char *strSource, size_t count ) {
s = strDest;
while ( *strSource && count ) {
- *s++ = *strSource++;
+ *s = *strSource;
+ s++; strSource++;
count--;
}
while ( count-- ) {
- *s++ = 0;
+ *s = '\0'; s++;
}
return strDest;
}
-double sqrt( double x ) {
- float y;
- float delta;
- float maxError;
-
- if ( x <= 0 ) {
- return 0;
- }
-
- // initial guess
- y = x / 2;
-
- // refine
- maxError = x * 0.001;
-
- do {
- delta = ( y * y ) - x;
- y -= delta / ( 2 * y );
- } while ( delta > maxError || delta < -maxError );
-
- return y;
-}
-
-
-float sintable[1024] = {
-0.000000,0.001534,0.003068,0.004602,0.006136,0.007670,0.009204,0.010738,
-0.012272,0.013805,0.015339,0.016873,0.018407,0.019940,0.021474,0.023008,
-0.024541,0.026075,0.027608,0.029142,0.030675,0.032208,0.033741,0.035274,
-0.036807,0.038340,0.039873,0.041406,0.042938,0.044471,0.046003,0.047535,
-0.049068,0.050600,0.052132,0.053664,0.055195,0.056727,0.058258,0.059790,
-0.061321,0.062852,0.064383,0.065913,0.067444,0.068974,0.070505,0.072035,
-0.073565,0.075094,0.076624,0.078153,0.079682,0.081211,0.082740,0.084269,
-0.085797,0.087326,0.088854,0.090381,0.091909,0.093436,0.094963,0.096490,
-0.098017,0.099544,0.101070,0.102596,0.104122,0.105647,0.107172,0.108697,
-0.110222,0.111747,0.113271,0.114795,0.116319,0.117842,0.119365,0.120888,
-0.122411,0.123933,0.125455,0.126977,0.128498,0.130019,0.131540,0.133061,
-0.134581,0.136101,0.137620,0.139139,0.140658,0.142177,0.143695,0.145213,
-0.146730,0.148248,0.149765,0.151281,0.152797,0.154313,0.155828,0.157343,
-0.158858,0.160372,0.161886,0.163400,0.164913,0.166426,0.167938,0.169450,
-0.170962,0.172473,0.173984,0.175494,0.177004,0.178514,0.180023,0.181532,
-0.183040,0.184548,0.186055,0.187562,0.189069,0.190575,0.192080,0.193586,
-0.195090,0.196595,0.198098,0.199602,0.201105,0.202607,0.204109,0.205610,
-0.207111,0.208612,0.210112,0.211611,0.213110,0.214609,0.216107,0.217604,
-0.219101,0.220598,0.222094,0.223589,0.225084,0.226578,0.228072,0.229565,
-0.231058,0.232550,0.234042,0.235533,0.237024,0.238514,0.240003,0.241492,
-0.242980,0.244468,0.245955,0.247442,0.248928,0.250413,0.251898,0.253382,
-0.254866,0.256349,0.257831,0.259313,0.260794,0.262275,0.263755,0.265234,
-0.266713,0.268191,0.269668,0.271145,0.272621,0.274097,0.275572,0.277046,
-0.278520,0.279993,0.281465,0.282937,0.284408,0.285878,0.287347,0.288816,
-0.290285,0.291752,0.293219,0.294685,0.296151,0.297616,0.299080,0.300543,
-0.302006,0.303468,0.304929,0.306390,0.307850,0.309309,0.310767,0.312225,
-0.313682,0.315138,0.316593,0.318048,0.319502,0.320955,0.322408,0.323859,
-0.325310,0.326760,0.328210,0.329658,0.331106,0.332553,0.334000,0.335445,
-0.336890,0.338334,0.339777,0.341219,0.342661,0.344101,0.345541,0.346980,
-0.348419,0.349856,0.351293,0.352729,0.354164,0.355598,0.357031,0.358463,
-0.359895,0.361326,0.362756,0.364185,0.365613,0.367040,0.368467,0.369892,
-0.371317,0.372741,0.374164,0.375586,0.377007,0.378428,0.379847,0.381266,
-0.382683,0.384100,0.385516,0.386931,0.388345,0.389758,0.391170,0.392582,
-0.393992,0.395401,0.396810,0.398218,0.399624,0.401030,0.402435,0.403838,
-0.405241,0.406643,0.408044,0.409444,0.410843,0.412241,0.413638,0.415034,
-0.416430,0.417824,0.419217,0.420609,0.422000,0.423390,0.424780,0.426168,
-0.427555,0.428941,0.430326,0.431711,0.433094,0.434476,0.435857,0.437237,
-0.438616,0.439994,0.441371,0.442747,0.444122,0.445496,0.446869,0.448241,
-0.449611,0.450981,0.452350,0.453717,0.455084,0.456449,0.457813,0.459177,
-0.460539,0.461900,0.463260,0.464619,0.465976,0.467333,0.468689,0.470043,
-0.471397,0.472749,0.474100,0.475450,0.476799,0.478147,0.479494,0.480839,
-0.482184,0.483527,0.484869,0.486210,0.487550,0.488889,0.490226,0.491563,
-0.492898,0.494232,0.495565,0.496897,0.498228,0.499557,0.500885,0.502212,
-0.503538,0.504863,0.506187,0.507509,0.508830,0.510150,0.511469,0.512786,
-0.514103,0.515418,0.516732,0.518045,0.519356,0.520666,0.521975,0.523283,
-0.524590,0.525895,0.527199,0.528502,0.529804,0.531104,0.532403,0.533701,
-0.534998,0.536293,0.537587,0.538880,0.540171,0.541462,0.542751,0.544039,
-0.545325,0.546610,0.547894,0.549177,0.550458,0.551738,0.553017,0.554294,
-0.555570,0.556845,0.558119,0.559391,0.560662,0.561931,0.563199,0.564466,
-0.565732,0.566996,0.568259,0.569521,0.570781,0.572040,0.573297,0.574553,
-0.575808,0.577062,0.578314,0.579565,0.580814,0.582062,0.583309,0.584554,
-0.585798,0.587040,0.588282,0.589521,0.590760,0.591997,0.593232,0.594466,
-0.595699,0.596931,0.598161,0.599389,0.600616,0.601842,0.603067,0.604290,
-0.605511,0.606731,0.607950,0.609167,0.610383,0.611597,0.612810,0.614022,
-0.615232,0.616440,0.617647,0.618853,0.620057,0.621260,0.622461,0.623661,
-0.624859,0.626056,0.627252,0.628446,0.629638,0.630829,0.632019,0.633207,
-0.634393,0.635578,0.636762,0.637944,0.639124,0.640303,0.641481,0.642657,
-0.643832,0.645005,0.646176,0.647346,0.648514,0.649681,0.650847,0.652011,
-0.653173,0.654334,0.655493,0.656651,0.657807,0.658961,0.660114,0.661266,
-0.662416,0.663564,0.664711,0.665856,0.667000,0.668142,0.669283,0.670422,
-0.671559,0.672695,0.673829,0.674962,0.676093,0.677222,0.678350,0.679476,
-0.680601,0.681724,0.682846,0.683965,0.685084,0.686200,0.687315,0.688429,
-0.689541,0.690651,0.691759,0.692866,0.693971,0.695075,0.696177,0.697278,
-0.698376,0.699473,0.700569,0.701663,0.702755,0.703845,0.704934,0.706021,
-0.707107,0.708191,0.709273,0.710353,0.711432,0.712509,0.713585,0.714659,
-0.715731,0.716801,0.717870,0.718937,0.720003,0.721066,0.722128,0.723188,
-0.724247,0.725304,0.726359,0.727413,0.728464,0.729514,0.730563,0.731609,
-0.732654,0.733697,0.734739,0.735779,0.736817,0.737853,0.738887,0.739920,
-0.740951,0.741980,0.743008,0.744034,0.745058,0.746080,0.747101,0.748119,
-0.749136,0.750152,0.751165,0.752177,0.753187,0.754195,0.755201,0.756206,
-0.757209,0.758210,0.759209,0.760207,0.761202,0.762196,0.763188,0.764179,
-0.765167,0.766154,0.767139,0.768122,0.769103,0.770083,0.771061,0.772036,
-0.773010,0.773983,0.774953,0.775922,0.776888,0.777853,0.778817,0.779778,
-0.780737,0.781695,0.782651,0.783605,0.784557,0.785507,0.786455,0.787402,
-0.788346,0.789289,0.790230,0.791169,0.792107,0.793042,0.793975,0.794907,
-0.795837,0.796765,0.797691,0.798615,0.799537,0.800458,0.801376,0.802293,
-0.803208,0.804120,0.805031,0.805940,0.806848,0.807753,0.808656,0.809558,
-0.810457,0.811355,0.812251,0.813144,0.814036,0.814926,0.815814,0.816701,
-0.817585,0.818467,0.819348,0.820226,0.821103,0.821977,0.822850,0.823721,
-0.824589,0.825456,0.826321,0.827184,0.828045,0.828904,0.829761,0.830616,
-0.831470,0.832321,0.833170,0.834018,0.834863,0.835706,0.836548,0.837387,
-0.838225,0.839060,0.839894,0.840725,0.841555,0.842383,0.843208,0.844032,
-0.844854,0.845673,0.846491,0.847307,0.848120,0.848932,0.849742,0.850549,
-0.851355,0.852159,0.852961,0.853760,0.854558,0.855354,0.856147,0.856939,
-0.857729,0.858516,0.859302,0.860085,0.860867,0.861646,0.862424,0.863199,
-0.863973,0.864744,0.865514,0.866281,0.867046,0.867809,0.868571,0.869330,
-0.870087,0.870842,0.871595,0.872346,0.873095,0.873842,0.874587,0.875329,
-0.876070,0.876809,0.877545,0.878280,0.879012,0.879743,0.880471,0.881197,
-0.881921,0.882643,0.883363,0.884081,0.884797,0.885511,0.886223,0.886932,
-0.887640,0.888345,0.889048,0.889750,0.890449,0.891146,0.891841,0.892534,
-0.893224,0.893913,0.894599,0.895284,0.895966,0.896646,0.897325,0.898001,
-0.898674,0.899346,0.900016,0.900683,0.901349,0.902012,0.902673,0.903332,
-0.903989,0.904644,0.905297,0.905947,0.906596,0.907242,0.907886,0.908528,
-0.909168,0.909806,0.910441,0.911075,0.911706,0.912335,0.912962,0.913587,
-0.914210,0.914830,0.915449,0.916065,0.916679,0.917291,0.917901,0.918508,
-0.919114,0.919717,0.920318,0.920917,0.921514,0.922109,0.922701,0.923291,
-0.923880,0.924465,0.925049,0.925631,0.926210,0.926787,0.927363,0.927935,
-0.928506,0.929075,0.929641,0.930205,0.930767,0.931327,0.931884,0.932440,
-0.932993,0.933544,0.934093,0.934639,0.935184,0.935726,0.936266,0.936803,
-0.937339,0.937872,0.938404,0.938932,0.939459,0.939984,0.940506,0.941026,
-0.941544,0.942060,0.942573,0.943084,0.943593,0.944100,0.944605,0.945107,
-0.945607,0.946105,0.946601,0.947094,0.947586,0.948075,0.948561,0.949046,
-0.949528,0.950008,0.950486,0.950962,0.951435,0.951906,0.952375,0.952842,
-0.953306,0.953768,0.954228,0.954686,0.955141,0.955594,0.956045,0.956494,
-0.956940,0.957385,0.957826,0.958266,0.958703,0.959139,0.959572,0.960002,
-0.960431,0.960857,0.961280,0.961702,0.962121,0.962538,0.962953,0.963366,
-0.963776,0.964184,0.964590,0.964993,0.965394,0.965793,0.966190,0.966584,
-0.966976,0.967366,0.967754,0.968139,0.968522,0.968903,0.969281,0.969657,
-0.970031,0.970403,0.970772,0.971139,0.971504,0.971866,0.972226,0.972584,
-0.972940,0.973293,0.973644,0.973993,0.974339,0.974684,0.975025,0.975365,
-0.975702,0.976037,0.976370,0.976700,0.977028,0.977354,0.977677,0.977999,
-0.978317,0.978634,0.978948,0.979260,0.979570,0.979877,0.980182,0.980485,
-0.980785,0.981083,0.981379,0.981673,0.981964,0.982253,0.982539,0.982824,
-0.983105,0.983385,0.983662,0.983937,0.984210,0.984480,0.984749,0.985014,
-0.985278,0.985539,0.985798,0.986054,0.986308,0.986560,0.986809,0.987057,
-0.987301,0.987544,0.987784,0.988022,0.988258,0.988491,0.988722,0.988950,
-0.989177,0.989400,0.989622,0.989841,0.990058,0.990273,0.990485,0.990695,
-0.990903,0.991108,0.991311,0.991511,0.991710,0.991906,0.992099,0.992291,
-0.992480,0.992666,0.992850,0.993032,0.993212,0.993389,0.993564,0.993737,
-0.993907,0.994075,0.994240,0.994404,0.994565,0.994723,0.994879,0.995033,
-0.995185,0.995334,0.995481,0.995625,0.995767,0.995907,0.996045,0.996180,
-0.996313,0.996443,0.996571,0.996697,0.996820,0.996941,0.997060,0.997176,
-0.997290,0.997402,0.997511,0.997618,0.997723,0.997825,0.997925,0.998023,
-0.998118,0.998211,0.998302,0.998390,0.998476,0.998559,0.998640,0.998719,
-0.998795,0.998870,0.998941,0.999011,0.999078,0.999142,0.999205,0.999265,
-0.999322,0.999378,0.999431,0.999481,0.999529,0.999575,0.999619,0.999660,
-0.999699,0.999735,0.999769,0.999801,0.999831,0.999858,0.999882,0.999905,
-0.999925,0.999942,0.999958,0.999971,0.999981,0.999989,0.999995,0.999999
-};
-
-double sin( double x ) {
- int index;
- int quad;
-
- index = 1024 * x / (M_PI * 0.5);
- quad = ( index >> 10 ) & 3;
- index &= 1023;
- switch ( quad ) {
- case 0:
- return sintable[index];
- case 1:
- return sintable[1023-index];
- case 2:
- return -sintable[index];
- case 3:
- return -sintable[1023-index];
- }
- return 0;
-}
-
-
-double cos( double x ) {
- int index;
- int quad;
-
- index = 1024 * x / (M_PI * 0.5);
- quad = ( index >> 10 ) & 3;
- index &= 1023;
- switch ( quad ) {
- case 3:
- return sintable[index];
- case 0:
- return sintable[1023-index];
- case 1:
- return -sintable[index];
- case 2:
- return -sintable[1023-index];
- }
- return 0;
-}
-
/*
void create_acostable( void ) {
@@ -764,6 +558,7 @@ double atan2( double y, double x ) {
double tan( double x ) {
return sin(x) / cos(x);
}
+#endif
// BFP - Unpack music, memory management for ui_mp3decoder
/*
@@ -843,10 +638,10 @@ ivln2_l = 7.0526075433e-06; /* 0x36eca570 =1/ln2 tail*/
/*
==================
-copysignf
+Q_copysignf
==================
*/
-static float copysignf( float x, float y )
+static float Q_copysignf( float x, float y )
{
unsigned int ix, iy;
@@ -858,10 +653,10 @@ static float copysignf( float x, float y )
/*
==================
-__scalbnf
+Q__scalbnf
==================
*/
-static float __scalbnf( float x, int n )
+static float Q__scalbnf( float x, int n )
{
int k, ix;
@@ -884,16 +679,16 @@ static float __scalbnf( float x, int n )
k = k + n;
if( n > 50000 || k > 0xfe )
- return huge * copysignf( huge, x ); /* overflow */
+ return huge * Q_copysignf( huge, x ); /* overflow */
if ( n < -50000 )
- return tiny * copysignf( tiny, x ); /*underflow*/
+ return tiny * Q_copysignf( tiny, x ); /*underflow*/
if( k > 0 ) /* normal result */
{
SET_FLOAT_WORD( x, ( ix & 0x807fffff ) | ( k << 23 ) );
return x;
}
if( k <= -25 )
- return tiny * copysignf( tiny, x ); /*underflow*/
+ return tiny * Q_copysignf( tiny, x ); /*underflow*/
k += 25; /* subnormal result */
SET_FLOAT_WORD( x, ( ix & 0x807fffff ) | ( k << 23 ) );
@@ -902,10 +697,10 @@ static float __scalbnf( float x, int n )
/*
==================
-pow
+mp3dec_pow
==================
*/
-float pow( float x, float y )
+float mp3dec_pow( float x, float y )
{
float z, ax, z_h, z_l, p_h, p_l;
float y1, t1, t2, r, s, t, u, v, w;
@@ -1150,16 +945,17 @@ float pow( float x, float y )
j += (n << 23 );
if( ( j >> 23 ) <= 0 )
- z = __scalbnf( z, n ); /* subnormal output */
+ z = Q__scalbnf( z, n ); /* subnormal output */
else
SET_FLOAT_WORD( z, j );
return s * z;
}
-#endif
-
+// BFP - End of the implementation of unpack music, memory management for ui_mp3decoder
+//==================================================================================
+#ifndef _MSC_VER
static int randSeed = 0;
void srand( unsigned seed ) {
@@ -1205,7 +1001,7 @@ double atof( const char *string ) {
c = string[0];
if ( c != '.' ) {
do {
- c = *string++;
+ c = *string; string++;
if ( c < '0' || c > '9' ) {
break;
}
@@ -1222,7 +1018,7 @@ double atof( const char *string ) {
fraction = 0.1;
do {
- c = *string++;
+ c = *string; string++;
if ( c < '0' || c > '9' ) {
break;
}
@@ -1237,19 +1033,19 @@ double atof( const char *string ) {
return value * sign;
}
+#endif
-double _atof( const char **stringPtr ) {
- const char *string;
- float sign;
- float value;
- int c = '0'; // bk001211 - uninitialized use possible
- string = *stringPtr;
+#if defined ( Q3_VM )
+int atoi( const char *string ) {
+ int sign;
+ int value;
+ int c;
+
// skip whitespace
while ( *string <= ' ' ) {
if ( !*string ) {
- *stringPtr = string;
return 0;
}
string++;
@@ -1272,149 +1068,501 @@ double _atof( const char **stringPtr ) {
// read digits
value = 0;
- if ( string[0] != '.' ) {
- do {
- c = *string++;
- if ( c < '0' || c > '9' ) {
- break;
+ do {
+ c = *string; string++;
+ if ( c < '0' || c > '9' ) {
+ break;
+ }
+ c -= '0';
+ value = value * 10 + c;
+ } while ( 1 );
+
+ // not handling 10e10 notation...
+
+ return value * sign;
+}
+
+int abs( int n ) {
+ return n < 0 ? -n : n;
+}
+
+double fabs( double x ) {
+ return x < 0 ? -x : x;
+}
+#endif
+
+
+int replace_s( char * str1, char * str2, char * src, int max_len )
+{
+ int count = 0; // replace count
+ int len1, len2, d;
+ char *match, *s0, *s1, *s2, *max;
+
+ match = strstr( src, str1 );
+
+ if ( !match )
+ return count;
+
+ len1 = (int)strlen( str1 );
+ len2 = (int)strlen( str2 );
+ d = len2-len1;
+
+ if ( d > 0 ) // expand and replace mode
+ {
+ max = src + max_len;
+ src += strlen( src );
+
+ do
+ {
+ // expand source string
+ s1 = src;
+ src += d;
+ if ( src >= max )
+ return count;
+ s2 = src;
+
+ s0 = match + len1;
+
+ while ( s1 >= s0 ) {
+ *s2-- = *s1--;
}
- c -= '0';
- value = value * 10 + c;
- } while ( 1 );
- }
- // check for decimal point
- if ( c == '.' ) {
- double fraction;
+ // replace match
+ s2 = str2;
+ while ( *s2 ) {
+ *match = *s2; match++; s2++;
+ }
+ match = strstr ( match, str1 );
- fraction = 0.1;
- do {
- c = *string++;
- if ( c < '0' || c > '9' ) {
- break;
+ count++;
+ }
+ while ( match );
+
+ return count;
+ }
+ else
+ if ( d < 0 ) // shrink and replace mode
+ {
+ do
+ {
+ // shrink source string
+ s1 = match + len1;
+ s2 = match + len2;
+ while ( (*s2++ = *s1++) != 0 );
+
+ //replace match
+ s2 = str2;
+ while ( *s2 ) {
+ *match = *s2;
+ match++; s2++;
}
- c -= '0';
- value += c * fraction;
- fraction *= 0.1;
- } while ( 1 );
+ match = strstr ( match, str1 );
+
+ count++;
+ }
+ while ( match );
+
+ return count;
}
+ else
+ do // just replace match
+ {
+ s2 = str2;
+ while ( *s2 ) {
+ *match = *s2;
+ match++; s2++;
+ }
- // not handling 10e10 notation...
- *stringPtr = string;
+ match = strstr ( match, str1 );
+ count++;
+ }
+ while ( match );
- return value * sign;
+ return count;
}
-// bk001120 - presumably needed for Mac
-//#if !defined ( _MSC_VER ) && ! defined ( __linux__ )
-
-// bk001127 - undid undo
-#if defined ( Q3_VM )
-int atoi( const char *string ) {
- int sign;
- int value;
- int c;
+qboolean replace1( const char match, const char replace, char *str )
+{
+ qboolean res = qfalse;
+ if ( !str )
+ return res;
- // skip whitespace
- while ( *string <= ' ' ) {
- if ( !*string ) {
- return 0;
+ while ( *str ) {
+ if ( *str == match ) {
+ *str = replace;
+ res = qtrue;
}
- string++;
+ str++;
}
- // check sign
- switch ( *string ) {
- case '+':
- string++;
- sign = 1;
- break;
- case '-':
- string++;
- sign = -1;
- break;
- default:
- sign = 1;
- break;
+ return res;
+}
+
+
+char *strtok( char *strToken, const char *strDelimit ) {
+ static char *lastStr = NULL;
+
+ const char *delimit = strDelimit;
+ int loop = 1;
+
+ // check some state
+ if( !strToken )
+ strToken = lastStr; // use the last string given then
+
+ // if there are no delimiters or no string to work with, bail
+ if( !strToken /*|| !strDelimit */ )
+ return NULL;
+
+ // first up we want to skip all delimiters at the start
+ // and mark our place to return
+ while( *strToken && loop )
+ {
+ loop = 0;
+
+ // check all delimiters, we must find _one_ to continue the outside loop
+ for( delimit = strDelimit ; *delimit ; delimit++ )
+ {
+ if( *strToken == *delimit )
+ {
+ strToken++;
+ loop = 1;
+ break;
+ }
+ }
}
- // read digits
- value = 0;
- do {
- c = *string++;
- if ( c < '0' || c > '9' ) {
- break;
+ // strToken now points to the first non-delimiter found
+ // now we want to find the next delimiter to terminate on
+ for( loop=1, lastStr=strToken ; *lastStr && loop ; lastStr++ )
+ {
+ // check all delimiters
+ for( delimit=strDelimit ; *delimit ; delimit++ )
+ {
+ if( *delimit == *lastStr )
+ {
+ loop = 0;
+ break;
+ }
}
- c -= '0';
- value = value * 10 + c;
- } while ( 1 );
+ }
- // not handling 10e10 notation...
+ // if the loop aborted then the token needs termination
+ if( !loop )
+ {
+ *(lastStr-1) = '\0';
- return value * sign;
+ if ( '\0' == *lastStr )
+ lastStr = NULL;
+ }
+ else
+ {
+ lastStr = NULL; // clean up for the next call
+ }
+
+ return strToken;
}
-int _atoi( const char **stringPtr ) {
- int sign;
- int value;
+char *BG_StripColor( char *string ) {
+ char *d;
+ char *s;
int c;
- const char *string;
- string = *stringPtr;
+ s = string;
+ d = string;
+ while ((c = *s) != 0 )
+ {
+ if ( Q_IsColorString( s ) )
+ s++;
+ else {
+ *d = c; d++;
+ }
+ s++;
+ }
+ *d = '\0';
+ return string;
+}
- // skip whitespace
- while ( *string <= ' ' ) {
- if ( !*string ) {
- return 0;
+
+char *EncodedString( const char *in )
+{
+ static const char hextab[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
+ static char buf[16384];
+ unsigned int c;
+ char *out;
+
+ if ( !in ) {
+ buf[0] = '\0';
+ return buf;
+ }
+
+ out = buf;
+
+ while ( ( c = *in++ ) != '\0' ) {
+ if ( c == '#' ) {
+ *out++ = '#';
+ *out++ = '#';
+ } else
+ if ( c > 127 || c == '%' ) {
+ *out++ = '#';
+ *out++ = hextab [ (c & 0xF0) >> 4 ];
+ *out++ = hextab [ (c & 0x0F) ];
+ } else {
+ *out++ = c;
}
- string++;
}
+ *out = '\0';
+ return (char *)buf;
+}
- // check sign
- switch ( *string ) {
- case '+':
- string++;
- sign = 1;
- break;
- case '-':
- string++;
- sign = -1;
- break;
- default:
- sign = 1;
- break;
+
+static int hex2dec ( char chr ) {
+ switch ( chr ) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'a': return 10;
+ case 'A': return 10;
+ case 'b': return 11;
+ case 'B': return 11;
+ case 'c': return 12;
+ case 'C': return 12;
+ case 'd': return 13;
+ case 'D': return 13;
+ case 'e': return 14;
+ case 'E': return 14;
+ case 'f': return 15;
+ case 'F': return 15;
}
+ return 0;
+}
- // read digits
- value = 0;
+
+char *DecodedString( const char *in )
+{
+ static char buf[16384];
+ char *out, c1, c2;
+
+ if ( !in ) {
+ buf[0] = '\0';
+ return (char *)buf;
+ }
+
+ out = buf;
+
+ while ( *in ) {
+ // new encoding
+ if ( *in == '#' && (c1 = *(in+1)) != 0 && (c2 = *(in+2)) != 0 &&
+ (( c1 >= '0' && c1 <= '9' ) || ( c1 >= 'a' && c1 <= 'f' )) &&
+ (( c2 >= '0' && c2 <= '9' ) || ( c2 >= 'a' && c2 <= 'f' ))) {
+ *out++ = hex2dec( c1 ) * 16 + hex2dec( c2 );
+ in += 3;
+ } else if ( *in == '#' && *(in+1) == '#' ) {
+ *out++ = '#';
+ in += 2;
+ } else {
+ *out++ = *in++;
+ }
+ }
+
+ *out = '\0';
+
+ return (char *)buf;
+}
+
+
+// a bit faster string compare function
+int BG_stricmp( const char *s1, const char *s2 ) {
+ int c1, c2;
do {
- c = *string++;
- if ( c < '0' || c > '9' ) {
+ c1 = locase[(unsigned char)*s1]; s1++;
+ c2 = locase[(unsigned char)*s2]; s2++;
+ if (c1 != c2)
+ return ((c1 < c2) ? -1 : 1);
+ } while ( c1 );
+ return 0; // strings are equal
+}
+
+
+char *Q_stristr( const char * str1, const char * str2 )
+{
+ char *cp = (char *) str1;
+ char *s1, *s2;
+
+ if ( !*str2 )
+ return( (char *)str1 );
+
+ while ( *cp )
+ {
+ s1 = cp;
+ s2 = (char *) str2;
+
+ while ( *s1 && locase[(unsigned char)*s1] == locase[(unsigned char)*s2] )
+ {
+ s1++;
+ s2++;
+ }
+
+ if ( !*s2 )
+ return( cp );
+
+ cp++;
+ }
+
+ return( NULL );
+}
+
+
+/*
+===========
+BG_CleanName
+============
+*/
+void BG_CleanName( const char *in, char *out, int outSize, const char *blankString ) {
+ int len, colorlessLen;
+ char ch;
+ char *p;
+ int spaces;
+
+ //save room for trailing null byte
+ outSize--;
+
+ len = 0;
+ colorlessLen = 0;
+ p = out;
+ *p = '\0';
+ spaces = 0;
+
+ while( 1 ) {
+ ch = *in++;
+ if( !ch ) {
break;
}
- c -= '0';
- value = value * 10 + c;
- } while ( 1 );
- // not handling 10e10 notation...
+ // don't allow leading spaces
+ if( *p == '\0' && ch <= ' ' ) {
+ continue;
+ }
- *stringPtr = string;
+ // check colors
+ if( ch == Q_COLOR_ESCAPE ) {
+ // solo trailing carat is not a color prefix
+ if( !*in ) {
+ break;
+ }
- return value * sign;
+ // don't allow black in a name, period
+ if( ColorIndex(*in) == 0 ) {
+ in++;
+ continue;
+ }
+
+ // make sure room in dest for both chars
+ if( len > outSize - 2 ) {
+ break;
+ }
+
+ *out++ = ch;
+ *out++ = *in++;
+ len += 2;
+ continue;
+ }
+
+ // let's keep it in printable range
+ if ( ch < ' ' || ch > 126 ) {
+ continue;
+ }
+
+ // don't allow too many consecutive spaces
+ if( ch == ' ' ) {
+ spaces++;
+ if( spaces > 2 ) {
+ continue;
+ }
+ }
+ else {
+ spaces = 0;
+ }
+
+ if( len > outSize - 1 ) {
+ break;
+ }
+
+ *out++ = ch;
+ colorlessLen++;
+ len++;
+ }
+ *out = '\0';
+
+ if ( blankString ) {
+ // don't allow empty names
+ if( *p == '\0' || colorlessLen == 0 ) {
+ Q_strncpyz( p, blankString, outSize );
+ }
+ }
}
-int abs( int n ) {
- return n < 0 ? -n : n;
+
+/*
+===================
+Q_strcpy
+
+string copy, without any checks
+===================
+*/
+void Q_strcpy( char *dst, const char *src )
+{
+ char c;
+ while ( (c = *src) != '\0' )
+ {
+ *dst = c; dst++; src++;
+ }
+ *dst = '\0';
}
-double fabs( double x ) {
- return x < 0 ? -x : x;
+
+char *Q_stradd( char *dst, const char *src )
+{
+ char c;
+ while ( (c = *src) != '\0' )
+ {
+ *dst = c;
+ dst++;
+ src++;
+ }
+ *dst = '\0';
+ return dst;
}
+int Q_strlen( const char *s )
+{
+ const char *b = s;
+ while ( *s != '\0' )
+ s++;
+ return s - b;
+}
+
+
+qboolean BigEndian( void )
+{
+ const char *s = { "123" };
+ int *i;
+ i = (void*)s;
+ if ( *i != 0x00333231 )
+ return qtrue;
+ else
+ return qfalse;
+}
//=========================================================
@@ -1429,61 +1577,73 @@ double fabs( double x ) {
#define SHORTINT 0x00000040 /* short integer */
#define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */
#define FPT 0x00000100 /* floating point number */
+#define REDUCE 0x00000200 /* extension: do not emit anything if value is zero */
#define to_digit(c) ((c) - '0')
#define is_digit(c) ((unsigned)to_digit(c) <= 9)
#define to_char(n) ((n) + '0')
-void AddInt( char **buf_p, int val, int width, int flags ) {
+static void AddInt( char **buf_p, int val, int width, int flags ) {
char text[32];
int digits;
int signedVal;
char *buf;
+ if ( flags & REDUCE && val == 0 )
+ return;
+
digits = 0;
signedVal = val;
if ( val < 0 ) {
val = -val;
}
do {
- text[digits++] = '0' + val % 10;
+ text[digits] = '0' + val % 10;
+ digits++;
val /= 10;
} while ( val );
if ( signedVal < 0 ) {
- text[digits++] = '-';
+ text[digits] = '-';
+ digits++;
}
buf = *buf_p;
if( !( flags & LADJUST ) ) {
while ( digits < width ) {
- *buf++ = ( flags & ZEROPAD ) ? '0' : ' ';
+ *buf = ( flags & ZEROPAD ) ? '0' : ' ';
+ buf++;
width--;
}
}
- while ( digits-- ) {
- *buf++ = text[digits];
+ while ( digits-- > 0 ) {
+ *buf = text[digits]; buf++;
width--;
}
if( flags & LADJUST ) {
- while ( width-- ) {
- *buf++ = ( flags & ZEROPAD ) ? '0' : ' ';
+ while ( width > 0 ) {
+ *buf = ( flags & ZEROPAD ) ? '0' : ' ';
+ buf++;
+ width--;
}
}
*buf_p = buf;
}
-void AddFloat( char **buf_p, float fval, int width, int prec ) {
+static void AddFloat( char **buf_p, float fval, int width, int prec, int reduce ) {
char text[32];
int digits;
float signedVal;
char *buf;
int val;
+ if ( reduce && fval == 0.0f )
+ return;
+
// get the sign
signedVal = fval;
if ( fval < 0 ) {
@@ -1494,50 +1654,61 @@ void AddFloat( char **buf_p, float fval, int width, int prec ) {
digits = 0;
val = (int)fval;
do {
- text[digits++] = '0' + val % 10;
+ text[digits] = '0' + val % 10;
+ digits++;
val /= 10;
} while ( val );
if ( signedVal < 0 ) {
- text[digits++] = '-';
+ text[digits] = '-';
+ digits++;
}
buf = *buf_p;
+ // fix precisiion
+ if ( prec < 0 ) {
+ prec = 6;
+ }
+
+ if ( prec ) {
+ width -= prec + 1;
+ }
+ // end
+
while ( digits < width ) {
- *buf++ = ' ';
+ *buf = ' '; buf++;
width--;
}
- while ( digits-- ) {
- *buf++ = text[digits];
+ while ( digits-- > 0 ) {
+ *buf = text[digits]; buf++;
}
*buf_p = buf;
- if (prec < 0)
- prec = 6;
// write the fraction
digits = 0;
while (digits < prec) {
fval -= (int) fval;
fval *= 10.0;
val = (int) fval;
- text[digits++] = '0' + val % 10;
+ text[digits] = '0' + val % 10;
+ digits++;
}
if (digits > 0) {
buf = *buf_p;
- *buf++ = '.';
+ *buf = '.'; buf++;
for (prec = 0; prec < digits; prec++) {
- *buf++ = text[prec];
+ *buf = text[prec]; buf++;
}
*buf_p = buf;
}
}
-void AddString( char **buf_p, char *string, int width, int prec ) {
+static void AddString( char **buf_p, const char *string, int width, int prec ) {
int size;
char *buf;
@@ -1556,50 +1727,51 @@ void AddString( char **buf_p, char *string, int width, int prec ) {
}
}
else {
- size = strlen( string );
+ size = Q_strlen( string );
}
width -= size;
while( size-- ) {
- *buf++ = *string++;
+ *buf = *string;
+ buf++; string++;
}
while( width-- > 0 ) {
- *buf++ = ' ';
+ *buf = ' '; buf++;
}
*buf_p = buf;
}
+
/*
-vsprintf
+Q_vsprintf
I'm not going to support a bunch of the more arcane stuff in here
-just to keep it simpler. For example, the '*' and '$' are not
+just to keep it simpler. For example, the '$' is not
currently supported. I've tried to make it so that it will just
parse and ignore formats we don't support.
+
+returns: number of char written without ending '\0'
*/
-int vsprintf( char *buffer, const char *fmt, va_list argptr ) {
- int *arg;
+int Q_vsprintf( char *buffer, const char *fmt, va_list ap ) {
char *buf_p;
char ch;
int flags;
int width;
int prec;
int n;
- char sign;
buf_p = buffer;
- arg = (int *)argptr;
while( qtrue ) {
// run through the format string until we hit a '%' or '\0'
- for ( ch = *fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++ ) {
- *buf_p++ = ch;
+ for ( /*ch = *fmt */; (ch = *fmt) != '\0' && ch != '%'; fmt++ ) {
+ *buf_p = ch; buf_p++;
}
if ( ch == '\0' ) {
- goto done;
+ break;
}
// skip over the '%'
@@ -1609,22 +1781,28 @@ int vsprintf( char *buffer, const char *fmt, va_list argptr ) {
flags = 0;
width = 0;
prec = -1;
- sign = '\0';
-
rflag:
- ch = *fmt++;
+ ch = *fmt; fmt++;
reswitch:
switch( ch ) {
+ //case ' ':
case '-':
flags |= LADJUST;
goto rflag;
case '.':
- n = 0;
- while( is_digit( ( ch = *fmt++ ) ) ) {
- n = 10 * n + ( ch - '0' );
+ if ( *fmt == '*' ) {
+ fmt++;
+ n = va_arg( ap, int );
+ prec = n < 0 ? -1 : n;
+ goto rflag;
+ } else {
+ n = 0;
+ while( is_digit( ( ch = *fmt++ ) ) ) {
+ n = 10 * n + ( ch - '0' );
+ }
+ prec = n < 0 ? -1 : n;
+ goto reswitch;
}
- prec = n < 0 ? -1 : n;
- goto reswitch;
case '0':
flags |= ZEROPAD;
goto rflag;
@@ -1640,78 +1818,287 @@ int vsprintf( char *buffer, const char *fmt, va_list argptr ) {
n = 0;
do {
n = 10 * n + ( ch - '0' );
- ch = *fmt++;
+ ch = *fmt; fmt++;
} while( is_digit( ch ) );
width = n;
goto reswitch;
+ case '*':
+ width = va_arg( ap, int );
+ goto rflag;
case 'c':
- *buf_p++ = (char)*arg;
- arg++;
+ *buf_p = (char)va_arg( ap, int ); buf_p++;
break;
case 'd':
case 'i':
- AddInt( &buf_p, *arg, width, flags );
- arg++;
+ AddInt( &buf_p, va_arg( ap, int ), width, flags );
break;
case 'f':
- AddFloat( &buf_p, *(double *)arg, width, prec );
-#ifdef __LCC__
- arg += 1; // everything is 32 bit in my compiler
-#else
- arg += 2;
-#endif
+ AddFloat( &buf_p, va_arg( ap, double ), width, prec, flags & REDUCE );
break;
case 's':
- AddString( &buf_p, (char *)*arg, width, prec );
- arg++;
+ AddString( &buf_p, va_arg( ap, char * ), width, prec );
break;
case '%':
- *buf_p++ = ch;
+ *buf_p = ch; buf_p++;
break;
+ // edawn extension:
+ case 'R':
+ flags |= REDUCE;
+ goto rflag;
default:
- *buf_p++ = (char)*arg;
- arg++;
+ *buf_p = (char)va_arg( ap, int ); buf_p++;
+ break;
+ } // switch ( ch )
+ } // while ( qtrue )
+
+ *buf_p = '\0';
+ return buf_p - buffer;
+}
+
+
+int BG_sprintf( char *buf, const char *format, ... )
+{
+ int len;
+ va_list argptr;
+ va_start( argptr, format );
+ len = Q_vsprintf( buf, format, argptr );
+ va_end( argptr );
+ return len;
+}
+
+
+static int _atoi( const char **stringPtr )
+{
+ int sign;
+ int value;
+ int c;
+ const char *string;
+
+ string = *stringPtr;
+
+ if ( !*string )
+ return 0;
+
+ // check sign
+ switch ( *string )
+ {
+ case '+':
+ string++;
+ sign = 1;
+ break;
+ case '-':
+ string++;
+ sign = -1;
+ break;
+ default:
+ sign = 1;
+ break;
+ }
+
+ // read digits
+ value = 0;
+ do
+ {
+ c = *string;
+ if ( c < '0' || c > '9' )
+ {
break;
}
+ c -= '0';
+ value = value * 10 + c;
+ string++;
+ }
+ while ( 1 );
+
+ // not handling 10e10 notation...
+
+ *stringPtr = string;
+
+ return value * sign;
+}
+
+
+static float _atof( const char **stringPtr )
+{
+ const char *string;
+ float sign;
+ float value;
+ float fraction;
+ int c = '0'; // uninitialized use possible
+
+ string = *stringPtr;
+
+ if ( !*string )
+ return 0;
+
+ // check sign
+ switch ( *string )
+ {
+ case '+':
+ string++;
+ sign = 1;
+ break;
+ case '-':
+ string++;
+ sign = -1;
+ break;
+ default:
+ sign = 1;
+ break;
}
-done:
- *buf_p = 0;
- return buf_p - buffer;
+ // read digits
+ value = 0;
+ if ( *string != '.' )
+ {
+ do
+ {
+ c = *string;
+ if ( c < '0' || c > '9' )
+ {
+ break;
+ }
+ c -= '0';
+ value = value * 10 + c;
+ string++;
+ }
+ while ( 1 );
+ }
+
+ // check for decimal point
+ if ( *string == '.' )
+ {
+ fraction = 0.1f;
+ string++;
+ do
+ {
+ c = *string;
+ if ( c < '0' || c > '9' )
+ {
+ break;
+ }
+ c -= '0';
+ value += c * fraction;
+ fraction *= 0.1f;
+ string++;
+ }
+ while ( 1 );
+ }
+
+ // not handling 10e10 notation...
+ *stringPtr = string;
+
+ return value * sign;
+}
+
+
+static void _atos( const char **stringPtr, char *buffer, int delimiter, int width )
+{
+ const char *string;
+
+ string = *stringPtr;
+
+ if ( !delimiter )
+ {
+ // skip whitespace
+ while ( *string && *string != ' ' && *string != '\t' && width-- > 0 )
+ {
+ *buffer = *string;
+ buffer++;
+ string++;
+ }
+ }
+ else while ( *string && *string != delimiter && width-- > 0 )
+ {
+ *buffer = *string;
+ buffer++;
+ string++;
+ }
+
+ *stringPtr = string;
+
+ *buffer = '\0';
}
-/* this is really crappy */
-int sscanf( const char *buffer, const char *fmt, ... ) {
- int cmd;
- int **arg;
- int count;
- arg = (int **)&fmt + 1;
+int Q_sscanf( const char *buffer, const char *fmt, ... )
+{
+ va_list ap;
+ int count;
+ int width;
+ int cmd;
+ const char *p;
+
+ va_start( ap, fmt );
count = 0;
- while ( *fmt ) {
- if ( fmt[0] != '%' ) {
+ while ( *fmt )
+ {
+ // single whitespace char validates any quantity of whitespace characters
+ // extracted from the stream (including none)
+ if ( *fmt == ' ' || *fmt == '\t' || *fmt == '\n' )
+ {
+ while ( *buffer == ' ' || *buffer == '\t' || *buffer == '\n' )
+ buffer++;
+ fmt++;
+ }
+
+ if ( *fmt != '%' )
+ {
+ if ( *fmt != *buffer )
+ break;
+
+ buffer++;
fmt++;
continue;
}
- cmd = fmt[1];
- fmt += 2;
+ width = fmt[1];
+ fmt++; // %
+ if ( width >= '0' && width <= '9' )
+ {
+ width -= '0'; // valid width;
+ fmt++; // ['0'..'9']
+ cmd = *fmt;
+ }
+ else
+ {
+ cmd = width;
+ width = 1024; // some assumption
+ }
+
+ p = buffer;
+
+ fmt++; // switch to delimiter?
+
+ //printf( "cmd=%c buffer=%s width=%i delim='%c'\n", cmd, buffer, width, *fmt );
- switch ( cmd ) {
+ switch ( cmd )
+ {
case 'i':
case 'd':
case 'u':
- **arg = _atoi( &buffer );
+ *(va_arg(ap, int *)) = _atoi( &buffer );
break;
case 'f':
- *(float *)*arg = _atof( &buffer );
+ *(va_arg(ap, float *)) = _atof( &buffer );
break;
+ case 'c':
+ *(va_arg(ap, char *)) = *buffer; buffer++;
+ break;
+ case 's':
+ _atos( &buffer, va_arg(ap, char *), *fmt, width );
+ break;
+ default:
+ return count;
}
- arg++;
+
+ if ( p != buffer )
+ count++;
+ else
+ break;
}
+ va_end( ap );
return count;
}
-
-#endif
diff --git a/source/game/bg_lib.h b/source/game/bg_lib.h
index f7c4545..44c5602 100644
--- a/source/game/bg_lib.h
+++ b/source/game/bg_lib.h
@@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// This file is NOT included on native builds
typedef int size_t;
+typedef int intptr_t;
typedef char * va_list;
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
@@ -54,7 +55,7 @@ void srand( unsigned seed );
int rand( void );
// String functions
-int strlen( const char *string );
+size_t strlen( const char *string );
char *strcat( char *strDestination, const char *strSource );
char *strcpy( char *strDestination, const char *strSource );
int strcmp( const char *string1, const char *string2 );
@@ -65,12 +66,10 @@ int tolower( int c );
int toupper( int c );
double atof( const char *string );
-double _atof( const char **stringPtr );
int atoi( const char *string );
-int _atoi( const char **stringPtr );
-int vsprintf( char *buffer, const char *fmt, va_list argptr );
-int sscanf( const char *buffer, const char *fmt, ... );
+int Q_vsprintf( char *buffer, const char *fmt, va_list argptr );
+int Q_sscanf( const char *buffer, const char *fmt, ... );
// Memory functions
void *memmove( void *dest, const void *src, size_t count );
@@ -88,5 +87,3 @@ double tan( double x );
int abs( int n );
double fabs( double x );
double acos( double x );
-float pow( float x, float y ); // BFP - Unpack music, memory management for ui_mp3decoder
-
diff --git a/source/game/bg_local.h b/source/game/bg_local.h
index 389683d..a6dbd9d 100644
--- a/source/game/bg_local.h
+++ b/source/game/bg_local.h
@@ -26,7 +26,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define STEPSIZE 18
-#define JUMP_VELOCITY 270
+#define JUMP_VELOCITY 600 // BFP - Before 270
#define TIMER_LAND 130
#define TIMER_GESTURE (34*66+50)
@@ -60,7 +60,6 @@ extern pml_t pml;
extern float pm_stopspeed;
extern float pm_duckScale;
extern float pm_swimScale;
-extern float pm_wadeScale;
extern float pm_accelerate;
extern float pm_airaccelerate;
@@ -80,4 +79,5 @@ void PM_AddEvent( int newEvent );
qboolean PM_SlideMove( qboolean gravity );
void PM_StepSlideMove( qboolean gravity );
+void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize );
diff --git a/source/game/bg_meansofdeath.h b/source/game/bg_meansofdeath.h
new file mode 100644
index 0000000..e019de0
--- /dev/null
+++ b/source/game/bg_meansofdeath.h
@@ -0,0 +1,42 @@
+#ifdef MOD_ENUMS
+ #define DECLARE_MOD( mod ) mod
+#endif
+
+#ifdef MOD_STRINGS
+ #define DECLARE_MOD( mod ) #mod
+#endif
+
+// BFP - NOTE: Some original BFP meansOfDeath aren't applied correctly like this replica does
+DECLARE_MOD( MOD_UNKNOWN ),
+DECLARE_MOD( MOD_SHOTGUN ),
+DECLARE_MOD( MOD_GAUNTLET ),
+DECLARE_MOD( MOD_MACHINEGUN ),
+DECLARE_MOD( MOD_GRENADE ),
+DECLARE_MOD( MOD_GRENADE_SPLASH ),
+DECLARE_MOD( MOD_ROCKET ),
+DECLARE_MOD( MOD_ROCKET_SPLASH ),
+DECLARE_MOD( MOD_PLASMA ),
+DECLARE_MOD( MOD_PLASMA_SPLASH ),
+DECLARE_MOD( MOD_RAILGUN ),
+DECLARE_MOD( MOD_LIGHTNING ),
+DECLARE_MOD( MOD_BFG ),
+DECLARE_MOD( MOD_BFG_SPLASH ),
+DECLARE_MOD( MOD_WATER ),
+DECLARE_MOD( MOD_SLIME ),
+DECLARE_MOD( MOD_LAVA ),
+DECLARE_MOD( MOD_CRUSH ),
+DECLARE_MOD( MOD_TELEFRAG ),
+DECLARE_MOD( MOD_FALLING ),
+DECLARE_MOD( MOD_SUICIDE ),
+DECLARE_MOD( MOD_TARGET_LASER ),
+DECLARE_MOD( MOD_TRIGGER_HURT ),
+DECLARE_MOD( MOD_KI_ATTACK ), // BFP - Ki attack kill message
+DECLARE_MOD( MOD_MELEE ), // BFP - Melee
+DECLARE_MOD( MOD_ILLEGAL_PLAYER_MODEL ), // BFP - Illegal player model
+DECLARE_MOD( MOD_GRAPPLE )
+
+#ifdef MOD_ENUMS
+ , DECLARE_MOD( MOD_MAX )
+#endif
+
+#undef DECLARE_MOD
diff --git a/source/game/bg_misc.c b/source/game/bg_misc.c
index 812b639..c6ac948 100644
--- a/source/game/bg_misc.c
+++ b/source/game/bg_misc.c
@@ -62,6 +62,7 @@ gitem_t bg_itemlist[] =
// ARMOR
//
+// BFP - 1
/*QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -79,6 +80,7 @@ gitem_t bg_itemlist[] =
/* sounds */ ""
},
+// BFP - 2
/*QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -95,6 +97,7 @@ gitem_t bg_itemlist[] =
/* sounds */ ""
},
+// BFP - 3
/*QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -114,6 +117,7 @@ gitem_t bg_itemlist[] =
//
// health
//
+// BFP - 4
/*QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -131,6 +135,7 @@ gitem_t bg_itemlist[] =
/* sounds */ ""
},
+// BFP - 5
/*QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -148,6 +153,7 @@ gitem_t bg_itemlist[] =
/* sounds */ ""
},
+// BFP - 6
/*QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -165,6 +171,7 @@ gitem_t bg_itemlist[] =
/* sounds */ ""
},
+// BFP - 7
/*QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -186,7 +193,135 @@ gitem_t bg_itemlist[] =
//
// WEAPONS
//
+#define ENABLE_Q3_WEAPONS 1
+#if !ENABLE_Q3_WEAPONS
+// BFP - TODO: IMPORTANT: Check config attack implementation, original BFP demo networking uses weapons from 8 until 12
+// BFP - 8
+ {
+ "weapon",
+ "sound/misc/w_pkup.wav",
+ { "models/powerups/instant/quad.md3", 0, 0, 0 },
+/* icon */ "icons/largekiblast",
+/* pickup */ "Weapon",
+ 0,
+ IT_WEAPON,
+ WP_NONE,
+/* precache */ "",
+/* sounds */ ""
+ },
+// BFP - 9 ... 116
+ { "weapon", "sound/misc/w_pkup.wav", {"models/powerups/instant/quad.md3", 0, 0, 0}, "icons/largekiblast", "Weapon", 0, IT_WEAPON, WP_GAUNTLET, "", "" },
+ { "weapon", "sound/misc/w_pkup.wav", {"models/powerups/instant/quad.md3", 0, 0, 0}, "icons/largekiblast", "Weapon", 0, IT_WEAPON, WP_SHOTGUN, "", "" },
+ { "weapon", "sound/misc/w_pkup.wav", {"models/powerups/instant/quad.md3", 0, 0, 0}, "icons/largekiblast", "Weapon", 0, IT_WEAPON, WP_MACHINEGUN, "", "" },
+ { "weapon", "sound/misc/w_pkup.wav", {"models/powerups/instant/quad.md3", 0, 0, 0}, "icons/largekiblast", "Weapon", 0, IT_WEAPON, WP_GRENADE_LAUNCHER, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+ { NULL, NULL, {NULL, NULL, 0, 0}, NULL, NULL, 0, 0, 0, "", "" },
+#else
+// BFP - TODO: IMPORTANT: AFTER COMPLETING CONFIG ATTACK IMPLEMENTATION,
+// REMOVE ALL Q3 WEAPONS AND KEEP ORIGINAL BFP ITEM INDEXES, MACRO INCLUDED
/*QUAKED weapon_gauntlet (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -346,142 +481,14 @@ gitem_t bg_itemlist[] =
/* precache */ "",
/* sounds */ ""
},
+#endif
- //
- // AMMO ITEMS
- //
-
-/*QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_shells",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/shotgunam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_shotgun",
-/* pickup */ "Shells",
- 10,
- IT_AMMO,
- WP_SHOTGUN,
-/* precache */ "",
-/* sounds */ ""
- },
-
-/*QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_bullets",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/machinegunam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_machinegun",
-/* pickup */ "Bullets",
- 50,
- IT_AMMO,
- WP_MACHINEGUN,
-/* precache */ "",
-/* sounds */ ""
- },
-
-/*QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_grenades",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/grenadeam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_grenade",
-/* pickup */ "Grenades",
- 5,
- IT_AMMO,
- WP_GRENADE_LAUNCHER,
-/* precache */ "",
-/* sounds */ ""
- },
-
-/*QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_cells",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/plasmaam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_plasma",
-/* pickup */ "Cells",
- 30,
- IT_AMMO,
- WP_PLASMAGUN,
-/* precache */ "",
-/* sounds */ ""
- },
-
-/*QUAKED ammo_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_lightning",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/lightningam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_lightning",
-/* pickup */ "Lightning",
- 60,
- IT_AMMO,
- WP_LIGHTNING,
-/* precache */ "",
-/* sounds */ ""
- },
-
-/*QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_rockets",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/rocketam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_rocket",
-/* pickup */ "Rockets",
- 5,
- IT_AMMO,
- WP_ROCKET_LAUNCHER,
-/* precache */ "",
-/* sounds */ ""
- },
-/*QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_slugs",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/railgunam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_railgun",
-/* pickup */ "Slugs",
- 10,
- IT_AMMO,
- WP_RAILGUN,
-/* precache */ "",
-/* sounds */ ""
- },
-
-/*QUAKED ammo_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "ammo_bfg",
- "sound/misc/am_pkup.wav",
- { "models/powerups/ammo/bfgam.md3",
- 0, 0, 0},
-/* icon */ "icons/icona_bfg",
-/* pickup */ "Bfg Ammo",
- 15,
- IT_AMMO,
- WP_BFG,
-/* precache */ "",
-/* sounds */ ""
- },
//
// HOLDABLE ITEMS
//
+// BFP - 117
/*QUAKED holdable_teleporter (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -497,6 +504,8 @@ gitem_t bg_itemlist[] =
/* precache */ "",
/* sounds */ ""
},
+
+// BFP - 118
/*QUAKED holdable_medkit (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -518,6 +527,7 @@ gitem_t bg_itemlist[] =
//
// POWERUP ITEMS
//
+// BFP - 119
/*QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -535,6 +545,7 @@ gitem_t bg_itemlist[] =
/* sounds */ "sound/items/damage2.wav sound/items/damage3.wav"
},
+// BFP - 120
/*QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -547,11 +558,12 @@ gitem_t bg_itemlist[] =
/* pickup */ "Battle Suit",
30,
IT_POWERUP,
- PW_BATTLESUIT,
+ 0, //PW_BATTLESUIT, // BFP - No battlesuit powerup
/* precache */ "",
/* sounds */ "sound/items/airout.wav sound/items/protect3.wav"
},
+// BFP - 121
/*QUAKED item_haste (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -564,11 +576,12 @@ gitem_t bg_itemlist[] =
/* pickup */ "Speed",
30,
IT_POWERUP,
- PW_HASTE,
+ 0,
/* precache */ "",
/* sounds */ ""
},
+// BFP - 122
/*QUAKED item_invis (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
*/
{
@@ -581,45 +594,12 @@ gitem_t bg_itemlist[] =
/* pickup */ "Invisibility",
30,
IT_POWERUP,
- PW_INVIS,
+ 0,
/* precache */ "",
/* sounds */ ""
},
-/*QUAKED item_regen (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "item_regen",
- "sound/items/regeneration.wav",
- { "models/powerups/instant/regen.md3",
- "models/powerups/instant/regen_ring.md3",
- 0, 0 },
-/* icon */ "icons/regen",
-/* pickup */ "Regeneration",
- 30,
- IT_POWERUP,
- PW_REGEN,
-/* precache */ "",
-/* sounds */ "sound/items/regen.wav"
- },
-
-/*QUAKED item_flight (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
-*/
- {
- "item_flight",
- "sound/items/flight.wav",
- { "models/powerups/instant/flight.md3",
- "models/powerups/instant/flight_ring.md3",
- 0, 0 },
-/* icon */ "icons/flight",
-/* pickup */ "Flight",
- 60,
- IT_POWERUP,
- PW_FLIGHT,
-/* precache */ "",
-/* sounds */ "sound/items/flight.wav"
- },
-
+// BFP - 123
/*QUAKED team_CTF_redflag (1 0 0) (-16 -16 -16) (16 16 16)
Only in CTF games
*/
@@ -637,6 +617,7 @@ Only in CTF games
/* sounds */ ""
},
+// BFP - 124
/*QUAKED team_CTF_blueflag (0 0 1) (-16 -16 -16) (16 16 16)
Only in CTF games
*/
@@ -747,7 +728,7 @@ grabbing them easier
============
*/
qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ) {
- vec3_t origin;
+ vec3_t origin = {0, 0, 0};
BG_EvaluateTrajectory( &item->pos, atTime, origin );
@@ -880,7 +861,7 @@ void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result )
VectorMA( tr->trBase, deltaTime, tr->trDelta, result );
break;
case TR_SINE:
- deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration;
+ deltaTime = ( ( atTime - tr->trTime ) % tr->trDuration ) / (float) tr->trDuration;
phase = sin( deltaTime * M_PI * 2 );
VectorMA( tr->trBase, phase, tr->trDelta, result );
break;
@@ -900,7 +881,7 @@ void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result )
result[2] -= 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity...
break;
default:
- Com_Error( ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trTime );
+ Com_Error( ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trType );
break;
}
}
@@ -943,99 +924,17 @@ void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t resu
result[2] -= DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity...
break;
default:
- Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trTime );
+ Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trType );
break;
}
}
-char *eventnames[] = {
- "EV_NONE",
-
- "EV_FOOTSTEP",
- "EV_FOOTSTEP_METAL",
- "EV_FOOTSPLASH",
- "EV_FOOTWADE",
- "EV_SWIM",
-
- "EV_STEP_4",
- "EV_STEP_8",
- "EV_STEP_12",
- "EV_STEP_16",
-
- "EV_FALL_SHORT",
- "EV_FALL_MEDIUM",
- "EV_FALL_FAR",
-
- "EV_JUMP_PAD", // boing sound at origin", jump sound on player
-
- "EV_JUMP",
- "EV_WATER_TOUCH", // foot touches
- "EV_WATER_LEAVE", // foot leaves
- "EV_WATER_UNDER", // head touches
- "EV_WATER_CLEAR", // head leaves
-
- "EV_ITEM_PICKUP", // normal item pickups are predictable
- "EV_GLOBAL_ITEM_PICKUP", // powerup / team sounds are broadcast to everyone
-
- "EV_NOAMMO",
- "EV_CHANGE_WEAPON",
- "EV_FIRE_WEAPON",
-
- "EV_USE_ITEM0",
- "EV_USE_ITEM1",
- "EV_USE_ITEM2",
- "EV_USE_ITEM3",
- "EV_USE_ITEM4",
- "EV_USE_ITEM5",
- "EV_USE_ITEM6",
- "EV_USE_ITEM7",
- "EV_USE_ITEM8",
- "EV_USE_ITEM9",
- "EV_USE_ITEM10",
- "EV_USE_ITEM11",
- "EV_USE_ITEM12",
- "EV_USE_ITEM13",
- "EV_USE_ITEM14",
- "EV_USE_ITEM15",
-
- "EV_ITEM_RESPAWN",
- "EV_ITEM_POP",
- "EV_PLAYER_TELEPORT_IN",
- "EV_PLAYER_TELEPORT_OUT",
-
- "EV_GRENADE_BOUNCE", // eventParm will be the soundindex
-
- "EV_GENERAL_SOUND",
- "EV_GLOBAL_SOUND", // no attenuation
- "EV_GLOBAL_TEAM_SOUND",
-
- "EV_BULLET_HIT_FLESH",
- "EV_BULLET_HIT_WALL",
-
- "EV_MISSILE_HIT",
- "EV_MISSILE_MISS",
- "EV_MISSILE_MISS_METAL",
- "EV_RAILTRAIL",
- "EV_SHOTGUN",
- "EV_BULLET", // otherEntity is the shooter
-
- "EV_PAIN",
- "EV_DEATH1",
- "EV_DEATH2",
- "EV_DEATH3",
- "EV_OBITUARY",
-
- "EV_POWERUP_QUAD",
- "EV_POWERUP_BATTLESUIT",
- "EV_POWERUP_REGEN",
-
- "EV_GIB_PLAYER", // gib a previously living player
- "EV_SCOREPLUM", // score plum
-
- "EV_DEBUG_LINE",
- "EV_STOPLOOPINGSOUND",
- "EV_TAUNT"
-
+const char *eventnames[EV_MAX] = {
+ // BFP - Events are declared in bg_events.h file
+#define EVENT_STRINGS
+ #include "bg_events.h"
+#undef EVENT_STRINGS
+ "NULL" // avoid -Wpedantic warnings
};
/*
@@ -1046,9 +945,13 @@ Handles the sequence numbers
===============
*/
+#ifdef CGAME
+void CG_StoreEvent( entity_event_t ev, int eventParm, int entityNum );
+#endif
+
void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize );
-void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ) {
+void BG_AddPredictableEventToPlayerstate( entity_event_t newEvent, int eventParm, playerState_t *ps, int entityNum ) {
#ifdef _DEBUG
{
@@ -1063,6 +966,11 @@ void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerSta
}
}
#endif
+
+#ifdef CGAME
+ CG_StoreEvent( newEvent, eventParm, entityNum );
+#endif
+
ps->events[ps->eventSequence & (MAX_PS_EVENTS-1)] = newEvent;
ps->eventParms[ps->eventSequence & (MAX_PS_EVENTS-1)] = eventParm;
ps->eventSequence++;
@@ -1083,10 +991,13 @@ void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) {
return;
}
+// BFP - Flight status can be interacted to bounce pads
+#if 0
// flying characters don't hit bounce pads
if ( ps->powerups[PW_FLIGHT] ) {
return;
}
+#endif
// if we didn't hit this same jumppad the previous frame
// then don't play the event sound again if we are in a fat trigger
@@ -1099,13 +1010,16 @@ void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) {
} else {
effectNum = 1;
}
- BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps );
+ BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps, -1 );
}
// remember hitting this jumppad this frame
ps->jumppad_ent = jumppad->number;
ps->jumppad_frame = ps->pmove_framecount;
// give the player the velocity from the jumppad
VectorCopy( jumppad->origin2, ps->velocity );
+
+ // BFP - Keep the speed (take a look on bg_pmove.c about PM_AirMove where handles that PMF flag)
+ ps->pm_flags &= ~PMF_AIR_GRAVITY;
}
/*
diff --git a/source/game/bg_pmove.c b/source/game/bg_pmove.c
index 655bb35..1469a80 100644
--- a/source/game/bg_pmove.c
+++ b/source/game/bg_pmove.c
@@ -33,18 +33,17 @@ pml_t pml;
// movement parameters
float pm_stopspeed = 100.0f;
float pm_duckScale = 0.25f;
-float pm_swimScale = 0.50f;
-float pm_wadeScale = 0.70f;
+// float pm_swimScale = 0.50f; // BFP - No water speed slowness
float pm_accelerate = 10.0f;
-float pm_airaccelerate = 1.0f;
-float pm_wateraccelerate = 4.0f;
-float pm_flyaccelerate = 8.0f;
+float pm_airaccelerate = 4.5f; // BFP - Add more air acceleration to handle user movement intentions, before 1.0f
+float pm_wateraccelerate = 20.0f; // BFP - Add more water acceleration to handle user movement intentions, before 4.0f
+float pm_flyaccelerate = 2.0f; // BFP - Add less flight acceleration, before 8.0f
float pm_friction = 6.0f;
float pm_waterfriction = 1.0f;
-float pm_flightfriction = 3.0f;
-float pm_spectatorfriction = 5.0f;
+float pm_flightfriction = 2.0f; // BFP - Add less flight friction, before 3.0f
+float pm_spectatorfriction = 2.0f; // BFP - Add less spectator movement friction, before 5.0f
int c_pmove = 0;
@@ -55,7 +54,7 @@ PM_AddEvent
===============
*/
void PM_AddEvent( int newEvent ) {
- BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
+ BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps, -1 );
}
/*
@@ -133,6 +132,369 @@ static void PM_ForceLegsAnim( int anim ) {
PM_StartLegsAnim( anim );
}
+/*
+===============
+PM_SetupBFP_weaponCfg
+===============
+*/
+static void PM_SetupBFP_weaponCfg( void ) { // BFP - TODO: Set initialized bfp_weapon.cfg properties to every weapon
+ // BFP - Testing randomWeaponTime, loopingAnim, noAttackAnim,
+ // chargeAttack, chargeAutoFire, movementPenalty
+ switch ( pm->ps->weapon ) {
+ case WP_GAUNTLET:
+ pm->attackType = ATK_HITSCAN;
+ pm->weaponTime = 100;
+ pm->kiCostAsPct = qtrue;
+ pm->kiPct = .010;
+ pm->kiCost = 0;
+ pm->chargeAttack = qfalse;
+ pm->chargeAutoFire = qfalse;
+ pm->loopingAnim = qtrue;
+ pm->noAttackAnim = qfalse;
+ pm->railTrail = qfalse;
+ return;
+ case WP_MACHINEGUN:
+ pm->attackType = ATK_HITSCAN;
+ pm->weaponTime = 50;
+ pm->randomWeaponTime = 100;
+ pm->kiCostAsPct = qtrue;
+ pm->kiPct = .010;
+ pm->kiCost = 0;
+ pm->chargeAttack = qfalse;
+ pm->chargeAutoFire = qfalse;
+ pm->loopingAnim = qtrue;
+ pm->noAttackAnim = qfalse;
+ pm->railTrail = qfalse;
+ return;
+ case WP_SHOTGUN:
+ pm->attackType = ATK_FORCEFIELD;
+ pm->weaponTime = 200;
+ pm->kiCostAsPct = qfalse;
+ pm->kiPct = 0;
+ pm->kiCost = 200;
+ pm->minCharge = 4;
+ pm->maxCharge = 0;
+ pm->chargeAttack = qtrue;
+ pm->chargeAutoFire = qtrue;
+ pm->loopingAnim = qfalse;
+ pm->noAttackAnim = qfalse;
+ pm->movementPenalty = 2 * 1000;
+ return;
+ case WP_GRENADE_LAUNCHER:
+ case WP_ROCKET_LAUNCHER:
+ pm->attackType = ATK_MISSILE;
+ pm->weaponTime = 1000;
+ pm->kiCostAsPct = qfalse;
+ pm->kiPct = 0;
+ pm->kiCost = 10;
+ pm->chargeAttack = qfalse;
+ pm->chargeAutoFire = qfalse;
+ pm->loopingAnim = qfalse;
+ pm->noAttackAnim = qfalse;
+ return;
+ case WP_BFG:
+ pm->attackType = ATK_MISSILE;
+ pm->weaponTime = 1000;
+ pm->kiCostAsPct = qfalse;
+ pm->kiPct = 0;
+ pm->kiCost = 250;
+ pm->minCharge = 2;
+ pm->maxCharge = 6;
+ pm->chargeAttack = qtrue;
+ pm->chargeAutoFire = qfalse;
+ pm->loopingAnim = qfalse;
+ pm->noAttackAnim = qfalse;
+ return;
+ case WP_RAILGUN:
+ pm->attackType = ATK_HITSCAN;
+ pm->weaponTime = 1500;
+ pm->kiCostAsPct = qfalse;
+ pm->kiPct = 0;
+ pm->kiCost = 500;
+ pm->chargeAttack = qfalse;
+ pm->chargeAutoFire = qfalse;
+ pm->loopingAnim = qfalse;
+ pm->noAttackAnim = qfalse;
+ pm->railTrail = qtrue;
+ return;
+ case WP_PLASMAGUN:
+ pm->attackType = ATK_RDMISSILE;
+ pm->weaponTime = 1250;
+ pm->kiCostAsPct = qfalse;
+ pm->kiPct = 0;
+ pm->kiCost = 200;
+ pm->chargeAttack = qtrue;
+ pm->chargeAutoFire = qfalse;
+ pm->minCharge = 2;
+ pm->maxCharge = 6;
+ pm->loopingAnim = qfalse;
+ pm->noAttackAnim = qfalse;
+ return;
+ case WP_LIGHTNING:
+ pm->attackType = ATK_HITSCAN;
+ pm->weaponTime = 50;
+ pm->kiCostAsPct = qtrue;
+ pm->kiPct = .015;
+ pm->kiCost = 0;
+ pm->chargeAttack = qfalse;
+ pm->chargeAutoFire = qfalse;
+ pm->loopingAnim = qfalse;
+ pm->noAttackAnim = qfalse;
+ pm->railTrail = qfalse;
+ return;
+ case WP_GRAPPLING_HOOK:
+ pm->attackType = ATK_BEAM;
+ pm->weaponTime = 500;
+ pm->kiCostAsPct = qfalse;
+ pm->kiPct = 0;
+ pm->kiCost = 250;
+ pm->chargeAttack = qtrue;
+ pm->chargeAutoFire = qfalse;
+ pm->minCharge = 2;
+ pm->maxCharge = 6;
+ pm->loopingAnim = qfalse;
+ pm->noAttackAnim = qfalse;
+ }
+}
+
+
+/*
+================
+PM_IsInKiAttackState
+
+Returns qtrue when the player is actively attacking
+(ki attack animation phase, or rapid-fire cooldown)
+================
+*/
+static qboolean PM_IsInKiAttackState( void ) { // BFP - Ki attack state
+ switch ( pm->ps->weaponstate ) {
+ case WEAPON_ACTIVE:
+ case WEAPON_BEAMSTRUGGLE:
+ case WEAPON_FIRING:
+ return qtrue;
+ case WEAPON_READY:
+ // Rapid-fire weapons set weaponstate back to READY immediately
+ // but weaponTime stays > 0 during cooldown. Keep ki attack anim running
+ return ( pm->ps->weaponTime > 0 ) ? qtrue : qfalse;
+ default:
+ return qfalse;
+ }
+}
+
+/*
+================
+PM_AttackAnim
+
+Handles attack animations through noAttackAnim and loopingAnim
+================
+*/
+static void PM_AttackAnim( int anim, int prepareAnim ) { // BFP - Handles attack animations through noAttackAnim and loopingAnim
+ // noAttackAnim
+ if ( pm->noAttackAnim ) {
+ if ( !PM_IsInKiAttackState() && pm->ps->weaponstate != WEAPON_FIRING ) {
+ PM_ContinueTorsoAnim( prepareAnim );
+ return;
+ }
+ PM_ContinueTorsoAnim( anim );
+ return;
+ }
+
+ // loopingAnim
+ if ( !pm->loopingAnim // chargeAutoFire animates this way
+ && pm->chargeAutoFire && pm->ps->generic1 < 3 ) {
+ PM_ContinueTorsoAnim( prepareAnim );
+ return;
+ }
+ if ( !pm->loopingAnim // chargeAutoFire animates this way
+ && pm->chargeAutoFire && pm->ps->generic1 >= 3 ) {
+ PM_ContinueTorsoAnim( anim );
+ return;
+ }
+ if ( !pm->loopingAnim
+ && ( !PM_IsInKiAttackState() || pm->ps->weaponstate == WEAPON_FIRING ) ) {
+ PM_ContinueTorsoAnim( prepareAnim );
+ return;
+ }
+ if ( PM_IsInKiAttackState() || pm->loopingAnim ) {
+ PM_ContinueTorsoAnim( anim );
+ }
+}
+
+
+/*
+==================
+PM_KiAttackTorsoAnim
+
+BFP - TODO: When implementing ki attacks, look up about the properties of the ki attacks from cfg and correct animation changes if required
+And tweak pmove_t struct, so we can handle that on g_active.c (like meleeHit), adding:
+attackType ("beam", "hitscan", "missile", "rdmissile", "sbeam" or "forcefield"), // type of attack
+randomWeaponTime (int, number of miliseconds), // random weapon time, maybe the max msec range of the random value
+chargeAttack (int / qboolean), // charging yes or no
+chargeAutoFire (int / qboolean), // even if it's charging the ki attack, fire
+minCharge (int [0-6]), // min charge points
+maxCharge (int [0-6]), // max charge points
+loopingAnim (int / qboolean), // Maybe it's: use PM_ContinueTorsoAnim, if not: PM_StartTorsoAnim
+noAttackAnim (int / qboolean), // no animation strike yes or no
+priority (int [0-32767]), // projectile's priority
+movementPenalty (int, number of seconds) // enters WEAPON_STUN when the ki attack was being used
+-----
+The following sample testing torso ki attack animations are used with:
+(ultimate_blast) WP_GRAPPLING_HOOK would be "beam", chargeAttack 1, chargeAutoFire 0, loopingAnim 0, noAttackAnim 0, priority 1, movementPenalty 0
+(finger_blast) WP_MACHINEGUN would be "hitscan", chargeAttack 0, chargeAutoFire 0, loopingAnim 1, noAttackAnim 0, priority 0, movementPenalty 0
+(ki_blast) WP_ROCKET_LAUNCHER would be "missile", chargeAttack 0, chargeAutoFire 0, loopingAnim 0, noAttackAnim 0, priority 0, movementPenalty 0
+(super_homing) WP_GRENADE_LAUNCHER would be "missile", chargeAttack 1, chargeAutoFire 0, loopingAnim 1, noAttackAnim 0, priority 0, movementPenalty 0
+(finger_beam) WP_RAILGUN would be "hitscan", chargeAttack 0, chargeAutoFire 0, loopingAnim 0, noAttackAnim 0, priority 0, movementPenalty 0
+(eyebeam) WP_LIGHTNING would be "hitscan", chargeAttack 0, chargeAutoFire 0, loopingAnim 0, noAttackAnim 0, priority 0, movementPenalty 0
+(homing_special) WP_PLASMAGUN would be "rdmissile", chargeAttack 1, chargeAutoFire 0, loopingAnim 0, noAttackAnim 0, priority 0, movementPenalty 0
+(aga) WP_SHOTGUN would be "forcefield", chargeAttack 1, chargeAutoFire 1, loopingAnim 1, noAttackAnim 0, priority 2, movementPenalty 2
+(blinding_flash) would be "forcefield", chargeAttack 1, chargeAutoFire 0, loopingAnim 0, noAttackAnim 1, priority 0, movementPenalty 0
+WP_BFG would be like super_homing, just throw the homing ki ball
+
+About "sbeam" attackType would be like a beam that, by holding down the attack key,
+you direct it wherever you want by moving the cursor.
+If you stop pressing the attack key, it explodes to the point where it arrived.
+This attackType was originally left unfinished,
+so there's a bug: after colliding the beam into something solid and
+keep holding down the attack key, keeps muzzling and
+doesn't shoot anything while the ki is wasted out of control.
+==================
+*/
+static void PM_KiAttackTorsoAnim( void ) { // BFP - Torso ki attack anims
+ if ( ( pm->cmd.buttons & BUTTON_ATTACK ) && !PM_IsInKiAttackState() ) {
+ switch( pm->ps->weapon ) {
+ case WP_ROCKET_LAUNCHER: { PM_AttackAnim( TORSO_ATTACK1_STRIKE, TORSO_ATTACK1_PREPARE ); break; }
+ case WP_GRENADE_LAUNCHER: { PM_AttackAnim( TORSO_ATTACK2_STRIKE, TORSO_ATTACK2_PREPARE ); break; }
+ case WP_RAILGUN: { PM_AttackAnim( TORSO_ATTACK3_STRIKE, TORSO_ATTACK3_PREPARE ); break; }
+ case WP_PLASMAGUN: { PM_AttackAnim( TORSO_ATTACK3_STRIKE, TORSO_ATTACK3_PREPARE ); break; }
+ case WP_SHOTGUN:
+ case WP_BFG:
+ case WP_GRAPPLING_HOOK: { PM_AttackAnim( TORSO_ATTACK4_STRIKE, TORSO_ATTACK4_PREPARE ); break; }
+ }
+ } else if ( PM_IsInKiAttackState() ) {
+ pm->cmd.buttons &= ~BUTTON_GESTURE;
+ switch( pm->ps->weapon ) {
+ default:
+ case WP_MACHINEGUN: { PM_AttackAnim( TORSO_ATTACK0_STRIKE, TORSO_ATTACK0_PREPARE ); break; }
+ case WP_ROCKET_LAUNCHER: { PM_AttackAnim( TORSO_ATTACK1_STRIKE, TORSO_ATTACK1_PREPARE ); break; }
+ case WP_GRENADE_LAUNCHER: { PM_AttackAnim( TORSO_ATTACK2_STRIKE, TORSO_ATTACK2_PREPARE ); break; }
+ case WP_PLASMAGUN:
+ case WP_RAILGUN: { PM_AttackAnim( TORSO_ATTACK3_STRIKE, TORSO_ATTACK3_PREPARE ); break; }
+ case WP_SHOTGUN:
+ case WP_BFG:
+ case WP_GRAPPLING_HOOK: { PM_AttackAnim( TORSO_ATTACK4_STRIKE, TORSO_ATTACK4_PREPARE ); break; }
+ }
+ }
+}
+
+/*
+==================
+PM_TorsoStatusAnim
+==================
+*/
+static void PM_TorsoStatusAnim( int anim ) { // BFP - Torso status handling
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+ if ( pm->ps->pm_flags & PMF_BLOCK ) PM_ContinueTorsoAnim( TORSO_BLOCK );
+ else if ( ( pm->cmd.buttons & BUTTON_MELEE ) && !( pm->ps->pm_flags & PMF_MELEE ) ) PM_ContinueTorsoAnim( TORSO_MELEE_READY );
+ else if ( pm->ps->pm_flags & PMF_MELEE ) PM_ContinueTorsoAnim( TORSO_MELEE_STRIKE );
+ else if ( ( pm->cmd.buttons & BUTTON_ATTACK )
+ || PM_IsInKiAttackState() ) PM_KiAttackTorsoAnim();
+ else PM_ContinueTorsoAnim( anim );
+}
+
+/*
+==================
+PM_ForceJumpAnim
+==================
+*/
+static void PM_ForceJumpAnim( void ) { // BFP - Jump anim handling
+ ( pm->cmd.forwardmove >= 0 ) ? PM_ForceLegsAnim( LEGS_JUMP ) : PM_ForceLegsAnim( LEGS_JUMPB );
+}
+
+/*
+==================
+PM_ContinueFlyAnim
+==================
+*/
+static void PM_ContinueFlyAnim( void ) { // BFP - Continuous fly anim handling
+ if ( pm->ps->pm_flags & PMF_MELEE ) { return; }
+ if ( pm->cmd.forwardmove > 0 ) { PM_TorsoStatusAnim( TORSO_FLYA ); PM_ContinueLegsAnim( LEGS_FLYA ); }
+ else if ( pm->cmd.forwardmove < 0 ) { PM_TorsoStatusAnim( TORSO_FLYB ); PM_ContinueLegsAnim( LEGS_FLYB ); }
+ else { PM_TorsoStatusAnim( TORSO_STAND ); PM_ContinueLegsAnim( LEGS_FLYIDLE ); }
+}
+
+/*
+===========================
+PM_ContinueMeleeStrikeLegsAnim
+===========================
+*/
+static void PM_ContinueMeleeStrikeLegsAnim( qboolean condition ) { // BFP - Melee strike legs anim handling
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+ // keep moving the legs when the player is attacking to the target through melee
+ // if the condition variable isn't used leave using this value: 1 or qtrue
+ if ( ( condition ) && ( pm->ps->pm_flags & PMF_MELEE )
+ && pm->ps->stats[STAT_HITSTUN_TIME] <= 0 && !( pm->ps->pm_flags & PMF_KI_CHARGE ) ) { PM_ContinueLegsAnim( LEGS_MELEE_STRIKE ); }
+}
+
+/*
+==================
+PM_SlopesNeargroundAnim
+==================
+*/
+static void PM_SlopesNeargroundAnim( qboolean is_slope ) { // BFP - Animation handling on the slopes and when being near to the ground
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
+ if ( pm->waterlevel > 0 ) { // not for water
+ return;
+ }
+
+ // don't apply if it's already striking
+ if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_MELEE_STRIKE ) {
+ return;
+ }
+
+ if ( is_slope ) {
+ if ( pm->ps->pm_flags & PMF_DUCKED ) {
+ PM_ContinueLegsAnim( LEGS_IDLECR );
+ if ( pm->cmd.forwardmove < 0
+ || ( pm->cmd.forwardmove > 0
+ || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) ) { PM_ContinueLegsAnim( LEGS_WALKCR ); }
+ PM_TorsoStatusAnim( TORSO_STAND );
+ return;
+ }
+ } else {
+ // if it's trying to crouch, then play jumping animation once
+ if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMP
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMPB ) {
+ return;
+ }
+ if ( pm->ps->pm_flags & PMF_DUCKED ) {
+ PM_ForceJumpAnim();
+ return;
+ }
+ }
+ // if it's very near to the other entity and the melee strike is executed, continue playing the melee strike legs animation
+ if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) { PM_ContinueLegsAnim( LEGS_IDLE ); PM_ContinueMeleeStrikeLegsAnim( qtrue ); return; }
+ if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
+ if ( pm->cmd.forwardmove < 0 ) { PM_ContinueLegsAnim( LEGS_BACK ); PM_TorsoStatusAnim( TORSO_STAND ); }
+ else if ( pm->cmd.forwardmove > 0
+ || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) { PM_ContinueLegsAnim( LEGS_RUN ); PM_TorsoStatusAnim( TORSO_RUN ); }
+ } else {
+ if ( pm->cmd.forwardmove < 0 ) { PM_ContinueLegsAnim( LEGS_BACK ); }
+ else if ( pm->cmd.forwardmove > 0
+ || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) { PM_ContinueLegsAnim( LEGS_WALK ); }
+ PM_TorsoStatusAnim( TORSO_STAND );
+ }
+ PM_ContinueMeleeStrikeLegsAnim( qtrue );
+}
/*
==================
@@ -183,10 +545,14 @@ static void PM_Friction( void ) {
speed = VectorLength(vec);
if (speed < 1) {
- drop = 0;
vel[0] = 0;
vel[1] = 0; // allow sinking underwater
// FIXME: still have z friction underwater?
+
+ // BFP - Brake when flying at that speed rate, otherwise the friction continues
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ vel[2] = 0;
+ }
return;
}
@@ -195,8 +561,9 @@ static void PM_Friction( void ) {
// apply ground friction
if ( pm->waterlevel <= 1 ) {
if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
+
// if getting knocked back, no friction
- if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
+ if ( !( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) {
control = speed < pm_stopspeed ? pm_stopspeed : speed;
drop += control*pm_friction*pml.frametime;
}
@@ -204,13 +571,14 @@ static void PM_Friction( void ) {
}
// apply water friction even if just wading
- if ( pm->waterlevel ) {
+ if ( pm->waterlevel
+ && !( pm->ps->eFlags & EF_FLIGHT ) && !( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) ) { // BFP - Don't apply on flight
drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
}
// apply flying friction
// BFP - Flight
- if ( pm->ps->pm_flags & PMF_FLYING ) {
+ if ( ( pm->ps->eFlags & EF_FLIGHT ) || ( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) ) {
control = speed < pm_stopspeed ? pm_stopspeed : speed;
drop += control*pm_flightfriction*pml.frametime;
}
@@ -232,6 +600,28 @@ static void PM_Friction( void ) {
}
+/*
+==================
+PM_KiBoostPowerlevelSpeed
+==================
+*/
+static float PM_KiBoostPowerlevelSpeed( void ) { // BFP - Powerlevel ki boost speed
+ int powerlevel = pm->ps->persistant[PERS_POWERLEVEL];
+ return pm->ps->speed + ( powerlevel * 0.5 );
+}
+
+
+/*
+================
+PM_IsBeamFiring
+================
+*/
+static qboolean PM_IsBeamFiring( void ) {
+ return ( pm->attackType == ATK_BEAM || pm->attackType == ATK_SBEAM )
+ && pm->ps->weaponstate == WEAPON_ACTIVE;
+}
+
+
/*
==============
PM_Accelerate
@@ -292,6 +682,20 @@ static float PM_CmdScale( usercmd_t *cmd ) {
int max;
float total;
float scale;
+ // BFP - Monster gamemode, player monster speed is faster
+ float speed = pm->ps->speed;
+ if ( pm->ps->eFlags & EF_MONSTER ) {
+ if ( !( pm->ps->eFlags & EF_FLIGHT ) ) {
+ speed *= 2;
+ } else {
+ if ( !( pm->ps->eFlags & EF_KI_BOOST )
+ && !( pm->cmd.buttons & BUTTON_KI_USE ) ) {
+ speed *= 1.5;
+ } else {
+ speed *= 2;
+ }
+ }
+ }
max = abs( cmd->forwardmove );
if ( abs( cmd->rightmove ) > max ) {
@@ -306,7 +710,7 @@ static float PM_CmdScale( usercmd_t *cmd ) {
total = sqrt( cmd->forwardmove * cmd->forwardmove
+ cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
- scale = (float)pm->ps->speed * max / ( 127.0 * total );
+ scale = (float)speed * max / ( 127.0 * total );
return scale;
}
@@ -362,6 +766,12 @@ static qboolean PM_CheckJump( void ) {
return qfalse; // don't allow jump until all buttons are up
}
+ // BFP - With ki charge, the player can't jump. With hit stun, avoids jittering movements
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0
+ || ( pm->ps->pm_flags & PMF_KI_CHARGE ) ) {
+ return qfalse;
+ }
+
if ( pm->cmd.upmove < 10 ) {
// not holding jump
return qfalse;
@@ -377,22 +787,141 @@ static qboolean PM_CheckJump( void ) {
pml.groundPlane = qfalse; // jumping away
pml.walking = qfalse;
pm->ps->pm_flags |= PMF_JUMP_HELD;
+ pm->ps->pm_flags &= ~PMF_AIR_GRAVITY; // BFP - Air gravity
pm->ps->groundEntityNum = ENTITYNUM_NONE;
pm->ps->velocity[2] = JUMP_VELOCITY;
- PM_AddEvent( EV_JUMP );
+ if ( pm->ps->eFlags & EF_MONSTER ) { // BFP - Add more vertical jumping for player monster
+ pm->ps->velocity[2] += 250;
+ }
+ // BFP - Double jump velocity when using ki boost
+ if ( ( pm->ps->eFlags & EF_KI_BOOST )
+ || ( pm->cmd.buttons & BUTTON_KI_USE ) ) { // BFP - Handle the ki boost button if it's being pressed, that avoids jittering movements
+ pm->ps->velocity[2] = 1085;
+ if ( pm->ps->eFlags & EF_MONSTER ) { // BFP - Add more vertical jumping for player monster
+ pm->ps->velocity[2] += 400;
+ }
+ }
- if ( pm->cmd.forwardmove >= 0 ) {
- PM_ForceLegsAnim( LEGS_JUMP );
- pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
- } else {
- PM_ForceLegsAnim( LEGS_JUMPB );
- pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
+ // BFP - Jumping from slopes without backoffs
+ {
+ vec3_t point;
+ trace_t trace;
+
+ point[0] = pm->ps->origin[0];
+ point[1] = pm->ps->origin[1];
+ point[2] = pm->ps->origin[2] - 0.25;
+
+ pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
+ pml.groundTrace = trace;
+
+ if ( trace.plane.normal[2] < 1 ) {
+ float fmove, smove;
+ int i;
+
+ fmove = pm->cmd.forwardmove;
+ smove = pm->cmd.rightmove;
+ pml.forward[2] = 0;
+ pml.right[2] = 0;
+ VectorNormalize (pml.forward);
+ VectorNormalize (pml.right);
+
+ for ( i = 0 ; i < 2 ; i++ ) {
+ pm->ps->velocity[i] = pml.forward[i]*fmove + pml.right[i]*smove;
+ }
+ }
+
+ if ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove == 0 ) {
+ pm->ps->velocity[0] = 0;
+ pm->ps->velocity[1] = 0;
+ }
}
+ PM_AddEvent( EV_JUMP );
+
+ // BFP - No PMF_BACKWARDS_JUMP handling (code removed)
+ PM_ForceJumpAnim();
+
+ PM_TorsoStatusAnim( TORSO_STAND );
+
return qtrue;
}
+/*
+===================
+PM_Drifting
+
+Drifting movements
+===================
+*/
+static void PM_Drifting( void ) { // BFP - Drifting
+ vec3_t drift;
+ float forwardSpeed, rightSpeed, driftFactor = 0.0003;
+
+ if ( pm->ps->pm_type == PM_DEAD || pm->ps->pm_type == PM_SPECTATOR
+ || ( pm->ps->pm_flags & PMF_RESPAWNED )
+ // handling for underwater
+ || ( pm->waterlevel && pm->ps->velocity[0] <= 0 && pm->ps->velocity[1] <= 0 ) ) {
+ return;
+ }
+
+ forwardSpeed = -DotProduct( pm->ps->velocity, pml.forward );
+ rightSpeed = DotProduct( pm->ps->velocity, pml.right );
+
+ // apply directional drift when keys are released
+ if ( !pm->cmd.rightmove && Q_fabs( rightSpeed ) > 0.0f
+ && pm->ps->velocity[2] < 0 ) {
+ driftFactor = 0.001;
+ VectorScale( pml.up, driftFactor * Q_fabs( rightSpeed ), drift );
+ VectorAdd( pm->ps->velocity, drift, pm->ps->velocity );
+ }
+
+ if ( !pm->cmd.forwardmove ) {
+ // drift a bit more when slowing down
+ if ( Q_fabs( forwardSpeed ) < 100.0f ) {
+ driftFactor = 0.008;
+ }
+ if ( forwardSpeed > 0 ) { // left
+ VectorScale( pml.right, driftFactor * Q_fabs( forwardSpeed ) * pml.right[1], drift );
+ } else { // right
+ VectorScale( pml.right, -driftFactor * Q_fabs( forwardSpeed ) * pml.right[1], drift );
+ }
+ VectorAdd( pm->ps->velocity, drift, pm->ps->velocity );
+ }
+}
+
+/*
+=============
+PM_CheckWaterSpot
+=============
+*/
+static qboolean PM_CheckWaterSpot( vec3_t direction, vec3_t spot, int horizontalVel, int verticalVel ) { // BFP - Check spot to jump off water
+ int cont = 0;
+ // BFP - Monster gamemode, use the measures to jump correctly near to the spot
+ float spotDir = 1, spotUnits = 1;
+ if ( pm->ps->eFlags & EF_MONSTER ) {
+ spotDir = 2.5;
+ spotUnits = 5.625;
+ verticalVel *= spotDir;
+ horizontalVel *= spotDir;
+ }
+
+ VectorMA ( pm->ps->origin, 30 * spotDir, direction, spot );
+ spot[2] += 4;
+ cont = pm->pointcontents( spot, pm->ps->clientNum );
+ if ( cont & CONTENTS_SOLID ) {
+ spot[2] += 16 * spotUnits;
+ cont = pm->pointcontents( spot, pm->ps->clientNum );
+ if ( !cont ) {
+ VectorScale( pml.forward, horizontalVel, pm->ps->velocity );
+ pm->ps->velocity[2] = verticalVel;
+ PM_ForceJumpAnim();
+ return qtrue;
+ }
+ }
+ return qfalse;
+}
+
/*
=============
PM_CheckWaterJump
@@ -400,8 +929,9 @@ PM_CheckWaterJump
*/
static qboolean PM_CheckWaterJump( void ) {
vec3_t spot;
- int cont;
- vec3_t flatforward;
+ // BFP - Apply for backwards, left and right too, Q3 doesn't have that
+ vec3_t flatforward, flatbackward, flatleft, flatright;
+ const int WATER_JUMP_HORIZONTAL_VELOCITY = 200, WATER_JUMP_VERTICAL_VELOCITY = 300;
if (pm->ps->pm_time) {
return qfalse;
@@ -417,27 +947,55 @@ static qboolean PM_CheckWaterJump( void ) {
flatforward[2] = 0;
VectorNormalize (flatforward);
- VectorMA (pm->ps->origin, 30, flatforward, spot);
- spot[2] += 4;
- cont = pm->pointcontents (spot, pm->ps->clientNum );
- if ( !(cont & CONTENTS_SOLID) ) {
+ // backward direction
+ flatbackward[0] = -pml.forward[0];
+ flatbackward[1] = -pml.forward[1];
+ flatbackward[2] = 0;
+ VectorNormalize( flatbackward );
+
+ // left direction
+ flatleft[0] = -pml.right[0];
+ flatleft[1] = -pml.right[1];
+ flatleft[2] = 0;
+ VectorNormalize( flatleft );
+
+ // right direction
+ flatright[0] = pml.right[0];
+ flatright[1] = pml.right[1];
+ flatright[2] = 0;
+ VectorNormalize( flatright );
+
+ // BFP - Don't auto-jump forward/backward
+ if ( pm->cmd.forwardmove == 0 ) {
return qfalse;
}
- spot[2] += 16;
- cont = pm->pointcontents (spot, pm->ps->clientNum );
- if ( cont ) {
+ // check forward
+ if ( PM_CheckWaterSpot( flatforward, spot, WATER_JUMP_HORIZONTAL_VELOCITY, WATER_JUMP_VERTICAL_VELOCITY ) ) {
+ return qtrue;
+ }
+
+ // check backward
+ if ( PM_CheckWaterSpot( flatbackward, spot, -WATER_JUMP_HORIZONTAL_VELOCITY, WATER_JUMP_VERTICAL_VELOCITY ) ) {
+ return qtrue;
+ }
+
+ // BFP - Don't auto-jump left/right
+ if ( pm->cmd.rightmove == 0 ) {
return qfalse;
}
- // jump out of water
- VectorScale (pml.forward, 200, pm->ps->velocity);
- pm->ps->velocity[2] = 350;
+ // check left
+ if ( PM_CheckWaterSpot( flatleft, spot, -WATER_JUMP_HORIZONTAL_VELOCITY, WATER_JUMP_VERTICAL_VELOCITY ) ) {
+ return qtrue;
+ }
- pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
- pm->ps->pm_time = 2000;
+ // check right
+ if ( PM_CheckWaterSpot( flatright, spot, WATER_JUMP_HORIZONTAL_VELOCITY, WATER_JUMP_VERTICAL_VELOCITY ) ) {
+ return qtrue;
+ }
- return qtrue;
+ return qfalse;
}
//============================================================================
@@ -456,6 +1014,7 @@ static void PM_WaterJumpMove( void ) {
PM_StepSlideMove( qtrue );
pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
+
if (pm->ps->velocity[2] < 0) {
// cancel as soon as we are falling down again
pm->ps->pm_flags &= ~PMF_ALL_TIMES;
@@ -477,17 +1036,42 @@ static void PM_WaterMove( void ) {
float scale;
float vel;
+ // BFP - Avoid adding friction under water while flying
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ return;
+ }
+
+ // BFP - With ki charge, the player can't move, even up or down
+ if ( pm->ps->pm_flags & PMF_KI_CHARGE ) {
+ pm->cmd.upmove = 0;
+ }
+
if ( PM_CheckWaterJump() ) {
PM_WaterJumpMove();
return;
}
+
+ // BFP - Underwater animation handling, uses flying animation in that case
+ if ( pm->waterlevel > 2 ) {
+ PM_ContinueFlyAnim();
+ // keep charging animation, otherwise looks jerky
+ if ( ( pm->cmd.buttons & BUTTON_KI_CHARGE )
+ && !( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) { // avoid forcing animations on transformation phase
+ PM_ContinueTorsoAnim( TORSO_CHARGE );
+ PM_ContinueLegsAnim( LEGS_CHARGE );
+ }
+ }
+
+ // BFP - Melee strike legs animation
+ PM_ContinueMeleeStrikeLegsAnim( qtrue );
+
#if 0
// jump = head for surface
if ( pm->cmd.upmove >= 10 ) {
if (pm->ps->velocity[2] > -300) {
- if ( pm->watertype == CONTENTS_WATER ) {
+ if ( pm->watertype & CONTENTS_WATER ) {
pm->ps->velocity[2] = 100;
- } else if (pm->watertype == CONTENTS_SLIME) {
+ } else if ( pm->watertype & CONTENTS_SLIME ) {
pm->ps->velocity[2] = 80;
} else {
pm->ps->velocity[2] = 50;
@@ -498,6 +1082,12 @@ static void PM_WaterMove( void ) {
PM_Friction ();
scale = PM_CmdScale( &pm->cmd );
+
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ scale = 0;
+ }
+
//
// user intentions
//
@@ -506,18 +1096,53 @@ static void PM_WaterMove( void ) {
wishvel[1] = 0;
wishvel[2] = -60; // sink towards bottom
} else {
- for (i=0 ; i<3 ; i++)
+ for (i=0 ; i<3 ; i++) {
wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
+ // BFP - Avoid going up, keep sinking
+ if ( i == 2
+ && ( ( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE
+ && ( pm->ps->eFlags & EF_FIRING ) )
+ || pm->ps->weaponstate == WEAPON_STUN
+ || pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) ) {
+ wishvel[2] = 0;
+ }
+ }
+
wishvel[2] += scale * pm->cmd.upmove;
}
+ // BFP - Reduces speed when charging ki
+ if ( ( ( pm->ps->pm_flags & PMF_KI_CHARGE ) || ( pm->cmd.buttons & BUTTON_KI_CHARGE ) )
+ && ( !( pm->ps->eFlags & EF_KI_BOOST ) && !( pm->cmd.buttons & BUTTON_KI_USE ) )
+ && pm->ps->stats[STAT_HITSTUN_TIME] <= 0 ) {
+ wishvel[2] *= 0.15;
+ }
+
+ // BFP - Sink on stunned status
+ if ( ( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE
+ && ( pm->ps->eFlags & EF_FIRING ) )
+ || pm->ps->weaponstate == WEAPON_STUN
+ || pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) {
+ wishvel[2] -= pm->ps->gravity * pml.frametime;
+ PM_SlideMove( qtrue );
+ }
+
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
+// BFP - No water speed slowness
+#if 0
if ( wishspeed > pm->ps->speed * pm_swimScale ) {
wishspeed = pm->ps->speed * pm_swimScale;
}
+#endif
+
+ if ( !PM_IsBeamFiring() // BFP - Don't increase speed when beam firing
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) )
+ && ( pm->cmd.forwardmove != 0 || pm->cmd.rightmove != 0 || pm->cmd.upmove != 0 ) ) {
+ wishspeed += PM_KiBoostPowerlevelSpeed();
+ }
PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
@@ -533,6 +1158,52 @@ static void PM_WaterMove( void ) {
}
PM_SlideMove( qfalse );
+
+ PM_Drifting(); // BFP - Drifting
+}
+
+/*
+===================
+PM_FlyTiltView
+
+Fly tilt view
+===================
+*/
+static void PM_FlyTiltView( void ) { // BFP - Fly tilt
+ static float currentRollAngle = 0;
+ int targetRollAngle = 0;
+ float rollStep = 0.2;
+
+ if ( ( pm->ps->eFlags & EF_FLIGHT )
+ && !( pm->ps->pm_flags & PMF_BLOCK )
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) ) ) {
+ // determine the target roll angle based on rightmove
+ if ( pm->cmd.rightmove > 0 ) {
+ targetRollAngle = 20;
+ } else if ( pm->cmd.rightmove < 0 ) {
+ targetRollAngle = -20;
+ }
+ }
+
+ // if going back to the main angle, increase a bit the roll step
+ if ( targetRollAngle == 0 ) {
+ rollStep = 0.375;
+ }
+
+ // apply the fixed step for a more direct roll change
+ if ( currentRollAngle < targetRollAngle ) {
+ currentRollAngle += rollStep;
+ if ( currentRollAngle > targetRollAngle ) {
+ currentRollAngle = targetRollAngle;
+ }
+ } else if ( currentRollAngle > targetRollAngle ) {
+ currentRollAngle -= rollStep;
+ if ( currentRollAngle < targetRollAngle ) {
+ currentRollAngle = targetRollAngle;
+ }
+ }
+
+ pm->ps->viewangles[ROLL] = currentRollAngle;
}
/*
@@ -552,27 +1223,82 @@ static void PM_FlyMove( void ) {
// normal slowdown
PM_Friction ();
+ // BFP - With ki charge, the player can't move, even up or down
+ if ( pm->ps->pm_flags & PMF_KI_CHARGE ) {
+ pm->cmd.upmove = 0;
+ }
+
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
scale = PM_CmdScale( &pm->cmd );
//
// user intentions
//
if ( !scale ) {
- wishvel[0] = 0;
- wishvel[1] = 0;
- wishvel[2] = 0;
+ VectorClear( wishvel );
} else {
for ( i = 0; i < 3; i++ ) {
- wishvel[i] = scale * pml.forward[i] * pm->cmd.forwardmove + scale * pml.right[i] * pm->cmd.rightmove + scale * pml.up[i] * pm->cmd.upmove; // BFP - (+ scale * pml.up[i] * pm->cmd.upmove) used when flying and moving upside/downside instead according the ground
+ wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove + scale * pml.up[i]*pm->cmd.upmove;
+ // BFP - Keep moving up if forward/backward, left/right and up directional keys are pressed
+ if ( pm->cmd.forwardmove != 0 && pm->cmd.rightmove != 0 && pm->cmd.upmove != 0 ) {
+ wishvel[i] += 12;
+ }
+ }
+
+ // BFP - Going up/down a bit down when moving left/right depending how the player looks
+ if ( !( pm->ps->pm_flags & PMF_BLOCK )
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) )
+ && pm->ps->pm_type != PM_SPECTATOR ) {
+
+ // handle downward push when moving left/right without upmove
+ if ( pm->cmd.rightmove != 0 && pm->cmd.upmove <= 0 ) {
+ vec3_t downPush;
+ int pushAmount = 100;
+
+ if ( ( pm->attackType == ATK_BEAM || pm->attackType == ATK_SBEAM ) && pm->ps->weaponstate == WEAPON_ACTIVE ) {
+ pushAmount = 80;
+ }
+
+ VectorScale( pml.up, -pushAmount, downPush );
+ VectorAdd( wishvel, downPush, wishvel );
+ }
}
- // wishvel[2] += scale * pm->cmd.upmove; // BFP - disabled to work the wished velocity
}
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
+ wishspeed *= scale; // add speed
+
+ if ( !( pm->ps->pm_flags & PMF_BLOCK ) // BFP - Don't increase the speed when blocking
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) )
+ && !PM_IsBeamFiring() ) { // BFP - Don't increase speed when beam firing
+ float factor = 2.0f + ( (float)pm->ps->persistant[PERS_POWERLEVEL] * 0.001 );
+ wishspeed += PM_KiBoostPowerlevelSpeed() * factor;
+ }
+
+// BFP - Debugging view and origin
+#if 0
+ Com_Printf( "----------------------------------------------------------------------------------------------------------\n" );
+ for ( i = 0; i < 3; i++ ) {
+ Com_Printf( "^1pm->ps->origin[%d]: %f - ", i, pm->ps->origin[i] );
+ Com_Printf( "^3pml.forward[%d]: %f - ", i, pml.forward[i] );
+ Com_Printf( "^6pml.right[%d]: %f - ", i, pml.right[i] );
+ Com_Printf( "^4pml.up[%d]: %f\n", i, pml.up[i] );
+ }
+#endif
+
PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
PM_StepSlideMove( qfalse );
+
+ // BFP - Drifting
+ if ( pm->ps->pm_time <= 0 ) {
+ PM_Drifting();
+ }
}
@@ -637,8 +1363,38 @@ static void PM_AirMove( void ) {
else
PM_SlideMove ( qtrue );
#endif
+ PM_TorsoStatusAnim( TORSO_STAND );
+
+ // BFP - Reduces speed when charging ki
+ if ( ( ( pm->ps->pm_flags & PMF_KI_CHARGE ) || ( pm->cmd.buttons & BUTTON_KI_CHARGE ) )
+ && ( !( pm->ps->eFlags & EF_KI_BOOST ) && !( pm->cmd.buttons & BUTTON_KI_USE ) )
+ && pm->ps->stats[STAT_HITSTUN_TIME] <= 0 ) {
+ if ( pm->ps->velocity[2] > -10 ) {
+ pm->ps->velocity[2] *= 0.9;
+ }
+ wishvel[2] = -10;
+ VectorCopy (wishvel, wishdir);
+ wishspeed = VectorNormalize(wishdir);
+ PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
+ PM_SlideMove( qfalse );
+ pm->ps->velocity[2] *= 0.99;
+ return;
+ }
PM_StepSlideMove ( qtrue );
+
+ // BFP - Handle gravity, make the player heavier
+ if ( !( pm->ps->pm_flags & PMF_AIR_GRAVITY ) ) {
+ // BFP - Adjust step slide movement for player monster
+ if ( !( pm->ps->eFlags & EF_MONSTER ) ) {
+ PM_SlideMove ( qtrue );
+ } else {
+ PM_StepSlideMove ( qtrue );
+ }
+ return;
+ }
+
+ pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
}
// BFP - no hook
@@ -687,6 +1443,11 @@ static void PM_WalkMove( void ) {
float accelerate;
float vel;
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
// begin swimming
PM_WaterMove();
@@ -743,6 +1504,8 @@ static void PM_WalkMove( void ) {
}
}
+// BFP - No water speed slowness
+#if 0
// clamp the speed lower if wading or walking on the bottom
if ( pm->waterlevel ) {
float waterScale;
@@ -753,10 +1516,20 @@ static void PM_WalkMove( void ) {
wishspeed = pm->ps->speed * waterScale;
}
}
+#endif
+
+ if ( !PM_IsBeamFiring() // BFP - Don't increase speed when beam firing
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) ) ) {
+ if ( pm->ps->pm_flags & PMF_DUCKED ) { // ducking uses 30% of speed
+ wishspeed += ( 1.0 - pm_duckScale ) * ( PM_KiBoostPowerlevelSpeed() * 0.3 );
+ } else {
+ wishspeed += PM_KiBoostPowerlevelSpeed();
+ }
+ }
// when a player gets hit, they temporarily lose
// full control, which allows them to be moved a bit
- if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
+ if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || ( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) {
accelerate = pm_airaccelerate;
} else {
accelerate = pm_accelerate;
@@ -767,13 +1540,31 @@ static void PM_WalkMove( void ) {
//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
- if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
+ if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || ( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) {
pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
} else {
// don't reset the z velocity for slopes
// pm->ps->velocity[2] = 0;
}
+ // BFP - Sink on stunned status
+ if ( ( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE
+ && ( pm->ps->eFlags & EF_FIRING ) )
+ || pm->ps->weaponstate == WEAPON_STUN
+ || pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) {
+ pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
+
+ // slide along the ground plane
+ PM_ClipVelocity ( pm->ps->velocity, pml.groundTrace.plane.normal,
+ pm->ps->velocity, OVERCLIP );
+
+ PM_SlideMove ( qtrue );
+ PM_StepSlideMove ( qtrue );
+
+ PM_Drifting(); // BFP - Drifting
+ return;
+ }
+
vel = VectorLength(pm->ps->velocity);
// slide along the ground plane
@@ -793,6 +1584,7 @@ static void PM_WalkMove( void ) {
//Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
+ PM_Drifting(); // BFP - Drifting
}
@@ -917,15 +1709,16 @@ static void PM_CrashLand( void ) {
// decide which landing animation to use
// BFP - Non-existant animations
- /*
+#if 0
if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
PM_ForceLegsAnim( LEGS_LANDB );
} else {
PM_ForceLegsAnim( LEGS_LAND );
}
- */
+#endif
- pm->ps->legsTimer = TIMER_LAND;
+ // BFP - No timer land on the legs
+ // pm->ps->legsTimer = TIMER_LAND;
// calculate the exact velocity on landing
dist = pm->ps->origin[2] - pml.previous_origin[2];
@@ -966,20 +1759,20 @@ static void PM_CrashLand( void ) {
if ( delta < 1 ) {
return;
}
-
// create a local entity event to play the sound
// SURF_NODAMAGE is used for bounce pads where you don't ever
// want to take damage or play a crunch sound
- if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) {
- if ( delta > 180 ) { // BFP - Before Q3 default value (60), so the fall in BFP is further
+ if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)
+ && pm->ps->pm_type != PM_DEAD ) { // BFP - Don't play on dead status
+ if ( delta > 180 ) { // BFP - Before Q3 default value (60), the far fall in BFP is deeper
PM_AddEvent( EV_FALL_FAR );
- } /* else if ( delta > 40 ) { // BFP - There's no medium fall on BFP
+ } else if ( delta > 60 ) { // BFP - Before Q3 default value (40), the far medium in BFP is a bit deeper
// this is a pain grunt, so don't play it if dead
if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
PM_AddEvent( EV_FALL_MEDIUM );
}
- } */ else if ( delta > 7 ) {
+ } else if ( delta > 30 ) { // BFP - Fall at that velocity
PM_AddEvent( EV_FALL_SHORT );
} else {
PM_AddEvent( PM_FootstepForSurface() );
@@ -995,16 +1788,31 @@ static void PM_CrashLand( void ) {
PM_CheckStuck
=============
*/
-/*
-void PM_CheckStuck(void) {
+#if 0 /* BFP - Disabled, since that doesn't make sense */
+static void PM_CheckStuck(void) {
+ // BFP - NOTE: Curiously and originally, BFP uses this function to animate when the player is stuck,
+ // that can be tested when the player is pretty near to the other player
+ // or being stuck in the same origin as the other player, specially outside water.
+ // It has been implemented when melee animations were being used
trace_t trace;
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask);
if (trace.allsolid) {
//int shit = qtrue;
+
+ // BFP - Handle the animations when being stuck! (Only outside water)
+ if ( pm->waterlevel < 1 ) {
+ if ( pm->cmd.forwardmove < 0 ) {
+ PM_ContinueLegsAnim( LEGS_JUMPB );
+ } else {
+ PM_ContinueLegsAnim( LEGS_JUMP );
+ }
+ }
+ // BFP - Melee strike legs animation
+ PM_ContinueMeleeStrikeLegsAnim( qtrue );
}
}
-*/
+#endif
/*
=============
@@ -1041,11 +1849,60 @@ static int PM_CorrectAllSolid( trace_t *trace ) {
}
}
- pm->ps->groundEntityNum = ENTITYNUM_NONE;
- pml.groundPlane = qfalse;
- pml.walking = qfalse;
+ pm->ps->groundEntityNum = ENTITYNUM_NONE;
+ pml.groundPlane = qfalse;
+ pml.walking = qfalse;
+
+ return qfalse;
+}
+
+/*
+=============
+PM_ControlJumpOnGround
+=============
+*/
+static void PM_ControlJumpOnGround( void ) { // BFP - A control to handle user movement intentions when jumping off the ground
+ if ( pm->ps->weaponstate != WEAPON_STUN
+ && pm->ps->groundEntityNum != ENTITYNUM_NONE
+ && !( pm->ps->eFlags & EF_FLIGHT )
+ && ( pm->cmd.upmove > 0 || ( pm->ps->pm_flags & PMF_JUMP_HELD ) )
+ && ( pm->cmd.forwardmove > 0 || pm->cmd.forwardmove < 0
+ || pm->cmd.rightmove > 0 || pm->cmd.rightmove < 0 ) ) {
+ float fmove, smove;
+ float vel;
+ int i;
+
+ fmove = pm->cmd.forwardmove;
+ smove = pm->cmd.rightmove;
+ pml.forward[2] = 0;
+ pml.right[2] = 0;
+ VectorNormalize (pml.forward);
+ VectorNormalize (pml.right);
+
+ for ( i = 0 ; i < 2 ; i++ ) {
+ pm->ps->velocity[i] = pml.forward[i]*fmove + pml.right[i]*smove;
+ }
+
+ if ( !PM_IsBeamFiring() // BFP - Don't increase speed when beam firing
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) ) ) {
+ pm->ps->velocity[0] *= 10;
+ pm->ps->velocity[1] *= 10;
+ } else { // BFP - Add a bit of forward/backward speed
+ pm->ps->velocity[0] *= 3.5;
+ pm->ps->velocity[1] *= 3.5;
+ }
+ vel = VectorLength( pm->ps->velocity );
+ if ( vel > 640 ) { // keep maximum speed
+ vel = 640;
+ }
+
+ // slide along the ground plane
+ PM_ClipVelocity ( pm->ps->velocity, pml.groundTrace.plane.normal,
+ pm->ps->velocity, OVERCLIP );
- return qfalse;
+ VectorNormalize( pm->ps->velocity );
+ VectorScale( pm->ps->velocity, vel, pm->ps->velocity );
+ }
}
@@ -1073,19 +1930,12 @@ static void PM_GroundTraceMissed( void ) {
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
if ( trace.fraction == 1.0 ) {
- if ( pm->cmd.forwardmove >= 0 ) {
- PM_ForceLegsAnim( LEGS_JUMP );
- pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
- } else {
- PM_ForceLegsAnim( LEGS_JUMPB );
- pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
- }
+ // BFP - No PMF_BACKWARDS_JUMP handling (code removed)
+ PM_ForceJumpAnim();
}
}
pm->ps->groundEntityNum = ENTITYNUM_NONE;
- pml.groundPlane = qfalse;
- pml.walking = qfalse;
}
@@ -1105,8 +1955,8 @@ static void PM_GroundTrace( void ) {
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
pml.groundTrace = trace;
- // BFP - Flight, if flying, do nothing when touching the ground
- if ( pm->ps->pm_flags & PMF_FLYING ) {
+ // BFP - No ground trace handling in the water
+ if ( pm->waterlevel > 1 ) {
return;
}
@@ -1129,14 +1979,6 @@ static void PM_GroundTrace( void ) {
if ( pm->debugLevel ) {
Com_Printf("%i:kickoff\n", c_pmove);
}
- // go into jump animation
- if ( pm->cmd.forwardmove >= 0 ) {
- PM_ForceLegsAnim( LEGS_JUMP );
- pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
- } else {
- PM_ForceLegsAnim( LEGS_JUMPB );
- pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
- }
pm->ps->groundEntityNum = ENTITYNUM_NONE;
pml.groundPlane = qfalse;
@@ -1154,20 +1996,63 @@ static void PM_GroundTrace( void ) {
pm->ps->groundEntityNum = ENTITYNUM_NONE;
pml.groundPlane = qtrue;
pml.walking = qfalse;
+
+ // BFP - If flying, prevent from doing a jumping action on slopes
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ return;
+ }
+
+ // BFP - Handle if the player is trying to jump and/or do another movements
+ // when stepping the steep slopes
+ if ( PM_CheckJump () ) {
+ // BFP - Handle jumping and changing the direction
+ PM_ControlJumpOnGround();
+ if ( ( pm->cmd.upmove > 0 || ( pm->ps->pm_flags & PMF_JUMP_HELD ) )
+ && !PM_IsBeamFiring() // BFP - Don't increase speed when beam firing
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) ) ) {
+ pm->ps->velocity[0] *= 5;
+ pm->ps->velocity[1] *= 5;
+ }
+ // jumped away
+ if ( pm->waterlevel > 1 ) {
+ PM_WaterMove();
+ } else {
+ PM_AirMove();
+ }
+ return;
+ }
+
+ PM_SlopesNeargroundAnim( 1 );
return;
}
+ // BFP - NOTE: Originally, BFP doesn't stop "groundtracing" until here when the player is flying
+ // BFP - If flying, prevent from doing a jumping action on flat ground
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ // BFP - To stick to the movers if the player is near to them
+ pm->ps->groundEntityNum = trace.entityNum;
+ PM_AddTouchEnt( trace.entityNum );
+ return;
+ }
+
+ pm->ps->pm_flags |= PMF_AIR_GRAVITY; // BFP - Air gravity
+
pml.groundPlane = qtrue;
pml.walking = qtrue;
+ // BFP - No handling PMF_TIME_WATERJUMP
+#if 0
// hitting solid ground will end a waterjump
if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
{
- pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
+ pm->ps->pm_flags &= ~PMF_TIME_WATERJUMP; // BFP: before: ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
pm->ps->pm_time = 0;
}
+#endif
- if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
+ // BFP - Handle when the player isn't flying
+ if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
+ && !( pm->ps->eFlags & EF_FLIGHT ) ) {
// just hit the ground
if ( pm->debugLevel ) {
Com_Printf("%i:Land\n", c_pmove);
@@ -1175,19 +2060,31 @@ static void PM_GroundTrace( void ) {
PM_CrashLand();
+ // BFP - PMF_TIME_LAND doesn't exist and it doesn't have any handle checks
+#if 0
// don't do landing time if we were just going down a slope
if ( pml.previous_velocity[2] < -200 ) {
// don't allow another jump for a little while
pm->ps->pm_flags |= PMF_TIME_LAND;
pm->ps->pm_time = 250;
}
+#endif
}
- if ( !( pm->ps->pm_flags & PMF_FLYING ) ) // BFP - Flight
- pm->ps->groundEntityNum = trace.entityNum;
+ pm->ps->groundEntityNum = trace.entityNum;
+
+ // BFP - Avoid abnormal speed (and strafe - or defrag)
+ if ( !( pm->ps->pm_flags & PMF_JUMP_HELD ) ) { // don't use the jump key for that
+ PM_ControlJumpOnGround();
+ }
// don't reset the z velocity for slopes
-// pm->ps->velocity[2] = 0;
+ // pm->ps->velocity[2] = 0;
+
+ // BFP - Avoid jumping unintentionally when that happens
+ if ( trace.plane.normal[2] == 1.0 ) {
+ pm->ps->velocity[2] = 0;
+ }
PM_AddTouchEnt( trace.entityNum );
}
@@ -1246,22 +2143,6 @@ static void PM_CheckDuck (void)
{
trace_t trace;
- if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
- if ( pm->ps->pm_flags & PMF_INVULEXPAND ) {
- // invulnerability sphere has a 42 units radius
- VectorSet( pm->mins, -42, -42, -42 );
- VectorSet( pm->maxs, 42, 42, 42 );
- }
- else {
- VectorSet( pm->mins, -15, -15, MINS_Z );
- VectorSet( pm->maxs, 15, 15, 16 );
- }
- pm->ps->pm_flags |= PMF_DUCKED;
- // pm->ps->viewheight = CROUCH_VIEWHEIGHT; // BFP - don't look a bit down while pressing down
- return;
- }
- pm->ps->pm_flags &= ~PMF_INVULEXPAND;
-
pm->mins[0] = -15;
pm->mins[1] = -15;
@@ -1296,13 +2177,24 @@ static void PM_CheckDuck (void)
if (pm->ps->pm_flags & PMF_DUCKED)
{
pm->maxs[2] = 16;
- // pm->ps->viewheight = CROUCH_VIEWHEIGHT; // BFP - don't look a bit down while pressing down
+ pm->ps->viewheight = CROUCH_VIEWHEIGHT;
}
else
{
pm->maxs[2] = 32;
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
}
+
+ // BFP - Monster gamemode, player monster bounding box sizes
+ if ( pm->ps->eFlags & EF_MONSTER ) {
+ pm->mins[0] = -37.5; // -15 * 2.5
+ pm->mins[1] = -37.5; // -15 * 2.5
+ pm->mins[2] = -135; // -24(MINS_Z) * 5.625
+
+ pm->maxs[0] = 37.5; // 15 * 2.5
+ pm->maxs[1] = 37.5; // 15 * 2.5
+ pm->maxs[2] *= 5.625;
+ }
}
@@ -1316,10 +2208,47 @@ PM_Footsteps
===============
*/
static void PM_Footsteps( void ) {
- float bobmove;
+ float bobmove = 0.0f;
int old;
qboolean footstep;
+ // BFP - Hit stun and ultimate tier
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0 || ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) {
+ return;
+ }
+
+ // BFP - Ki explosion wave state
+ if ( ( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE
+ && ( pm->ps->eFlags & EF_FIRING ) ) ) {
+ PM_ContinueLegsAnim( LEGS_IDLE );
+ return;
+ }
+
+ // BFP - Ki explosion wave stun state
+ if ( pm->ps->weaponstate == WEAPON_STUN ) {
+ PM_ContinueTorsoAnim( TORSO_STUN );
+ PM_ContinueLegsAnim( LEGS_IDLE );
+ return;
+ }
+
+ // BFP - Avoid when charging
+ if ( pm->ps->pm_flags & PMF_KI_CHARGE ) {
+ pm->ps->eFlags &= ~EF_FIRING; // don't display shooting effects
+ return;
+ }
+
+ // BFP - Handle torso melee animation
+ if ( ( pm->cmd.buttons & BUTTON_MELEE ) || ( pm->ps->pm_flags & PMF_MELEE ) || pm->meleeHit ) {
+ PM_TorsoStatusAnim( TORSO_MELEE_READY );
+ }
+
+ // BFP - Avoid when flying (for melee strike animation, that's applied)
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ // BFP - Melee strike legs animation, don't apply if it's playing the starting jump animation in the flight status
+ PM_ContinueMeleeStrikeLegsAnim( pm->ps->pm_time <= 0 );
+ return;
+ }
+
//
// calculate speed and cycle to be used for
// all cyclic walking effects
@@ -1328,14 +2257,25 @@ static void PM_Footsteps( void ) {
+ pm->ps->velocity[1] * pm->ps->velocity[1] );
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
-
- // if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
- // PM_ContinueLegsAnim( LEGS_IDLECR );
- // }
- // airborne leaves position in cycle intact, but doesn't advance
- if ( pm->waterlevel > 1 ) {
- PM_ContinueLegsAnim( LEGS_SWIM );
+ // BFP - PM_CheckStuck has been moved here, Q3 and the rest of mods hadn't used this. Currently disabled, since that doesn't make sense
+ // PM_CheckStuck();
+
+ // BFP - Underwater animation handling, uses flying animation in that case
+ // also keep the torso
+ if ( pm->waterlevel > 0 ) { // BFP - Avoid bad animations when jumping off water
+ if ( pm->cmd.upmove == 0 ) {
+ PM_ContinueFlyAnim();
+ }
+ if ( pm->waterlevel <= 1 && pm->cmd.upmove > 0 ) {
+ PM_ForceJumpAnim();
+ }
+ } else {
+ // BFP - Keep the torso when using a ki attack even after charged, avoid when melee is being used
+ if ( !( pm->cmd.buttons & BUTTON_MELEE ) ) {
+ PM_KiAttackTorsoAnim();
+ }
}
+
return;
}
@@ -1343,12 +2283,20 @@ static void PM_Footsteps( void ) {
if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
if ( pm->xyspeed < 5 ) {
pm->ps->bobCycle = 0; // start at beginning of cycle again
+ if ( pm->ps->pm_flags & PMF_DUCKED ) {
+ PM_ContinueLegsAnim( LEGS_IDLECR );
+ } else if ( !( pm->ps->pm_flags & PMF_KI_CHARGE ) ) {
+ PM_ContinueLegsAnim( LEGS_IDLE );
+ }
+ } else { // BFP - Handle the legs while it isn't doing nothing
if ( pm->ps->pm_flags & PMF_DUCKED ) {
PM_ContinueLegsAnim( LEGS_IDLECR );
} else {
PM_ContinueLegsAnim( LEGS_IDLE );
}
}
+ // BFP - Melee strike legs animation
+ PM_ContinueMeleeStrikeLegsAnim( qtrue );
return;
}
@@ -1357,10 +2305,14 @@ static void PM_Footsteps( void ) {
if ( pm->ps->pm_flags & PMF_DUCKED ) {
bobmove = 0.5; // ducked characters bob much faster
- if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
- PM_ContinueLegsAnim( LEGS_WALKCR ); // BFP - before LEGS_BACKCR
- } else {
- PM_ContinueLegsAnim( LEGS_WALKCR );
+ // BFP - Replaced PMF_BACKWARDS_RUN handling
+ if ( pml.groundTrace.contents & MASK_PLAYERSOLID ) {
+ if ( pm->cmd.forwardmove < 0 ) {
+ PM_ContinueLegsAnim( LEGS_WALKCR ); // BFP - before LEGS_BACKCR
+ } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
+ PM_ContinueLegsAnim( LEGS_WALKCR );
+ }
+ PM_TorsoStatusAnim( TORSO_STAND ); // BFP - Keep the torso
}
// ducked characters never play footsteps
/*
@@ -1374,25 +2326,32 @@ static void PM_Footsteps( void ) {
PM_ContinueLegsAnim( LEGS_BACK );
*/
} else {
- if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
+ if ( !( pm->cmd.buttons & BUTTON_WALKING ) && ( pml.groundTrace.contents & MASK_PLAYERSOLID ) ) {
bobmove = 0.4f; // faster speeds bob faster
- if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
+ // BFP - Replaced PMF_BACKWARDS_RUN handling
+ if ( pm->cmd.forwardmove < 0 ) {
PM_ContinueLegsAnim( LEGS_BACK );
- } else {
+ PM_TorsoStatusAnim( TORSO_STAND ); // BFP - Keep the torso
+ } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
PM_ContinueLegsAnim( LEGS_RUN );
- PM_ContinueTorsoAnim( TORSO_RUN ); // BFP - Torso run animation
+ PM_TorsoStatusAnim( TORSO_RUN ); // BFP - Keep the torso
}
footstep = qtrue;
- } else {
+ } else if ( pml.groundTrace.contents & MASK_PLAYERSOLID ) {
bobmove = 0.3f; // walking bobs slow
- if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
+ // BFP - Replaced PMF_BACKWARDS_RUN handling
+ if ( pm->cmd.forwardmove < 0 ) {
PM_ContinueLegsAnim( LEGS_BACK );
- } else {
+ } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
PM_ContinueLegsAnim( LEGS_WALK );
}
+ PM_TorsoStatusAnim( TORSO_STAND ); // BFP - Keep the torso
}
}
+ // BFP - Melee strike legs animation
+ PM_ContinueMeleeStrikeLegsAnim( qtrue );
+
// check for footstep / splash sounds
old = pm->ps->bobCycle;
pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
@@ -1425,18 +2384,48 @@ Generate sound events for entering and leaving water
==============
*/
static void PM_WaterEvents( void ) { // FIXME?
+ vec3_t point;
+ int cont;
+
+ // BFP - Jumping off water surface, handle sound event for footsplash
+ point[0] = pm->ps->origin[0];
+ point[1] = pm->ps->origin[1];
+ point[2] = pm->ps->origin[2] + MINS_Z + 6;
+ cont = pm->pointcontents( point, pm->ps->clientNum );
+
//
// if just entered a water volume, play a sound
//
if (!pml.previous_waterlevel && pm->waterlevel) {
- PM_AddEvent( EV_WATER_TOUCH );
+ if ( !( cont & MASK_WATER )
+ && !( pm->ps->eFlags & EF_FLIGHT )
+ && pm->cmd.upmove > 0 ) {
+ PM_AddEvent( EV_FOOTSPLASH ); // BFP - Play a different and smooth sound
+ return;
+ } else {
+ PM_AddEvent( EV_WATER_TOUCH );
+ }
}
//
// if just completely exited a water volume, play a sound
//
if (pml.previous_waterlevel && !pm->waterlevel) {
- PM_AddEvent( EV_WATER_LEAVE );
+ if ( !( cont & MASK_WATER )
+ && !( pm->ps->eFlags & EF_FLIGHT )
+ && pm->cmd.upmove > 0 ) {
+ PM_AddEvent( EV_FOOTSPLASH ); // BFP - Play a different and smooth sound
+ } else {
+ PM_AddEvent( EV_WATER_LEAVE );
+ }
+ if ( !( pm->ps->eFlags & EF_FLIGHT )
+ && !( pm->cmd.buttons & BUTTON_KI_CHARGE )
+ && !( pm->ps->pm_flags & PMF_KI_CHARGE )
+ && !( pm->ps->pm_flags & PMF_MELEE )
+ && !( pm->ps->pm_flags & PMF_ULTIMATE_TIER )
+ && pm->ps->stats[STAT_HITSTUN_TIME] <= 0 ) {
+ PM_ForceJumpAnim(); // BFP - Keep legs animation
+ }
}
//
@@ -1475,8 +2464,10 @@ static void PM_BeginWeaponChange( int weapon ) {
PM_AddEvent( EV_CHANGE_WEAPON );
pm->ps->weaponstate = WEAPON_DROPPING;
- pm->ps->weaponTime += 200;
- PM_StartTorsoAnim( TORSO_ATTACK0_PREPARE ); // BFP - before TORSO_DROP
+ // BFP - Don't add weaponTime when changing ki attack
+ // pm->ps->weaponTime += 200;
+ // BFP - Non-existant animation
+ // PM_StartTorsoAnim( TORSO_DROP );
}
@@ -1499,8 +2490,10 @@ static void PM_FinishWeaponChange( void ) {
pm->ps->weapon = weapon;
pm->ps->weaponstate = WEAPON_RAISING;
- pm->ps->weaponTime += 250;
- PM_StartTorsoAnim( TORSO_ATTACK0_PREPARE ); // BFP - before TORSO_RAISE
+ // BFP - Don't add weaponTime when changing ki attack
+ // pm->ps->weaponTime += 250;
+ // BFP - Non-existant animation
+ // PM_StartTorsoAnim( TORSO_RAISE );
}
@@ -1511,11 +2504,105 @@ PM_TorsoAnimation
==============
*/
static void PM_TorsoAnimation( void ) {
+ // BFP - NOTE: That function could be called as "PM_NearGround",
+ // here is tracing something similar to PM_GroundTraceMissed
+ trace_t trace;
+ vec3_t point;
+ int cont;
+ // BFP - Handle the jump velocity after touching water surface,
+ // the maximum speed is one-third of the powerlevel
+ float vel, maxSpeed = 400 + ( pm->ps->persistant[PERS_POWERLEVEL] * 0.25 );
+
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
+ // BFP - Jumping off water surface
+ point[0] = pm->ps->origin[0];
+ point[1] = pm->ps->origin[1];
+ point[2] = pm->ps->origin[2] + MINS_Z + 6;
+ cont = pm->pointcontents( point, pm->ps->clientNum );
+
+ if ( ( cont & MASK_WATER )
+ && pm->waterlevel <= 2
+ && !( pm->ps->eFlags & EF_FLIGHT )
+ && pm->cmd.upmove > 0 ) {
+ pm->ps->velocity[2] = 200;
+ // Control the player depending their moves
+ if ( pm->cmd.forwardmove > 0 || pm->cmd.forwardmove < 0
+ || pm->cmd.rightmove > 0 || pm->cmd.rightmove < 0 ) {
+ float fmove, smove;
+ int i;
+
+ fmove = pm->cmd.forwardmove;
+ smove = pm->cmd.rightmove;
+ pml.forward[2] = 0;
+ pml.right[2] = 0;
+ VectorNormalize (pml.forward);
+ VectorNormalize (pml.right);
+
+ for ( i = 0 ; i < 2 ; i++ ) {
+ pm->ps->velocity[i] = pml.forward[i]*fmove + pml.right[i]*smove;
+ }
+
+ if ( !PM_IsBeamFiring() // BFP - Don't increase speed when beam firing
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) ) ) {
+ pm->ps->velocity[0] *= 10;
+ pm->ps->velocity[1] *= 10;
+ }
+ }
+ // increase jumping speed using ki boost while not moving directionally
+ if ( !PM_IsBeamFiring() // BFP - Don't increase speed when beam firing
+ && ( ( pm->ps->eFlags & EF_KI_BOOST ) || ( pm->cmd.buttons & BUTTON_KI_USE ) )
+ && !( pm->cmd.forwardmove > 0 || pm->cmd.forwardmove < 0
+ || pm->cmd.rightmove > 0 || pm->cmd.rightmove < 0 ) ) {
+ pm->ps->velocity[2] *= 5;
+ }
+
+ // fix high speed bug (some strafe - or defrag trick) by touching and jumping off the water surface
+ vel = VectorLength( pm->ps->velocity );
+ if ( vel > maxSpeed ) { // keep maximum speed
+ vel = maxSpeed;
+ }
+ VectorNormalize( pm->ps->velocity );
+ VectorScale( pm->ps->velocity, vel, pm->ps->velocity );
+
+ // BFP - Handle PMF flag
+ pm->ps->pm_flags &= ~PMF_AIR_GRAVITY;
+
+ PM_ForceJumpAnim();
+ return;
+ }
+
+ // BFP - No ground trace handling in the water
+ if ( pm->waterlevel > 1 ) {
+ return;
+ }
+
+ VectorCopy( pm->ps->origin, point );
+ point[2] -= 95; // BFP - Put more down, obviously it was 64, but BFP does that
+
+ pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
+ pml.groundTrace = trace;
+
+ // BFP - Falling distantly from the ground
+ if ( trace.fraction == 1.0 && !( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMP
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMPB )
+ && !( pm->ps->eFlags & EF_FLIGHT ) ) {
+ PM_ForceJumpAnim();
+ PM_TorsoStatusAnim( TORSO_STAND );
+ }
- // BFP - TODO: torso animations may not be needed here,
- // possibly weaponstates could be replaced/removed
- // or this function could be replaced/removed
+ // If idling, keep the torso
+ if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
+ PM_TorsoStatusAnim( TORSO_STAND );
+ }
+ // BFP - Melee strike legs animation, don't apply if it isn't touching the ground
+ PM_ContinueMeleeStrikeLegsAnim( pm->ps->groundEntityNum != ENTITYNUM_NONE );
+
+#if 0
if ( pm->ps->weaponstate == WEAPON_READY ) {
if ( pm->ps->weapon == WP_GAUNTLET ) {
PM_ContinueTorsoAnim( TORSO_STAND ); // BFP - before TORSO_STAND2
@@ -1524,83 +2611,388 @@ static void PM_TorsoAnimation( void ) {
}
return;
}
+#endif
+}
+
+/*
+==============
+PM_CheckFlightState
+==============
+*/
+static void PM_CheckFlightState( pmove_t *pm ) { // BFP - Checks if the flight is disabled while the key is held
+ if ( pm->ps->pm_type == PM_DEAD || pm->ps->pm_type == PM_SPECTATOR
+ || ( pm->ps->pm_flags & PMF_RESPAWNED ) ) {
+ pm->ps->pm_flags &= ~PMF_FLIGHT_LATCH;
+ // add a small fall while respawning and holding the key
+ if ( ( pm->ps->pm_flags & PMF_RESPAWNED ) && ( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) ) {
+ pm->ps->velocity[2] -= 150;
+ } else {
+ pm->cmd.buttons &= ~BUTTON_ENABLEFLIGHT;
+ }
+ return;
+ }
+
+ if ( ( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) && !pm->noFlight ) {
+ if ( !( pm->ps->pm_flags & PMF_FLIGHT_LATCH ) ) {
+ // do not play the sound in the charging status
+ if ( !( pm->ps->eFlags & EF_FLIGHT ) && !( pm->ps->pm_flags & PMF_KI_CHARGE ) ) {
+ BG_AddPredictableEventToPlayerstate( EV_ENABLE_FLIGHT, 0, pm->ps, -1 ); // play the sound
+ }
+ // change state and lock until release
+ pm->ps->eFlags ^= EF_FLIGHT;
+ pm->ps->pm_flags |= PMF_FLIGHT_LATCH;
+ }
+ } else {
+ pm->ps->pm_flags &= ~PMF_FLIGHT_LATCH;
+ }
+
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ pm->cmd.buttons |= BUTTON_ENABLEFLIGHT;
+ } else {
+ pm->cmd.buttons &= ~BUTTON_ENABLEFLIGHT;
+ }
+}
+
+/*
+==============
+PM_FlightStart
+==============
+*/
+static void PM_FlightStart( void ) { // BFP - Start flight handling
+ vec3_t point;
+ trace_t trace;
+
+ // BFP - Hit stun
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) {
+ return;
+ }
+
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
+ // BFP - Avoid entering jump flying status after recharging ki
+ if ( !( pm->ps->pm_flags & PMF_KI_CHARGE ) && !( pm->cmd.buttons & BUTTON_KI_CHARGE )
+ && pm->ps->pm_time > 0 ) {
+ return;
+ }
+
+ point[0] = pm->ps->origin[0];
+ point[1] = pm->ps->origin[1];
+ point[2] = pm->ps->origin[2] - 0.25;
+
+ pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask );
+ pml.groundTrace = trace;
+
+ // BFP - If the player is in the ground, then jump!
+ // And make sure when the player isn't flying and falling
+ if ( ( ( pm->ps->eFlags & EF_FLIGHT ) || ( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) ) // handle the flight button if it's being pressed, that avoids jittering
+ && ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLECR
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_WALK
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_WALKCR
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_BACK
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_RUN )
+ && ( pml.groundTrace.contents & MASK_PLAYERSOLID )
+ && !( pm->cmd.buttons & BUTTON_KI_CHARGE )
+ && !( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE )
+ && pm->ps->weaponstate != WEAPON_STUN ) {
+ pm->ps->pm_time = 1120; // to avoid drifting while standing the jump velocity
+ pm->ps->velocity[2] = JUMP_VELOCITY - 200;
+
+ // don't play the animation when being transformed
+ if ( !( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) {
+ if ( !PM_IsInKiAttackState()
+ && !( pm->ps->pm_flags & PMF_MELEE ) ) {
+ if ( pm->cmd.forwardmove > 0 ) {
+ PM_TorsoStatusAnim( TORSO_FLYA );
+ } else if ( pm->cmd.forwardmove < 0 ) {
+ PM_TorsoStatusAnim( TORSO_FLYB );
+ } else {
+ PM_TorsoStatusAnim( TORSO_STAND );
+ }
+ }
+ PM_ForceLegsAnim( LEGS_JUMP );
+ }
+ }
+}
+
+/*
+==============
+PM_FlightAnimation
+==============
+*/
+static void PM_FlightAnimation( void ) { // BFP - Flight
+
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
+ if ( ( ( pm->ps->eFlags & EF_FLIGHT ) || ( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) )
+ && pm->ps->pm_time <= 570 ) { // smooth jump animation
+ pm->ps->pm_flags |= PMF_AIR_GRAVITY; // BFP - Air gravity
+ PM_ContinueFlyAnim();
+ return;
+ }
+
+ // BFP - That happens when the player is landing nearly
+ if ( !( pm->ps->eFlags & EF_FLIGHT )
+ && !( pm->ps->pm_flags & PMF_JUMP_HELD )
+ && pm->ps->groundEntityNum == ENTITYNUM_NONE // hasn't touched the ground yet
+ && ( pml.groundTrace.contents & MASK_PLAYERSOLID ) ) {
+ PM_SlopesNeargroundAnim( 0 );
+ }
+
+ if ( !( pml.groundTrace.contents & MASK_PLAYERSOLID )
+ && !( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMP
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMPB )
+ && !( pm->ps->eFlags & EF_FLIGHT )
+ && pm->waterlevel < 1 ) {
+ if ( pm->cmd.forwardmove < 0 ) { // when failing backwards after flying
+ PM_StartLegsAnim( LEGS_JUMPB );
+ } else {
+ PM_StartLegsAnim( LEGS_JUMP );
+ }
+ PM_TorsoStatusAnim( TORSO_STAND );
+ }
+}
+
+/*
+==============
+PM_KiChargeAnimation
+==============
+*/
+static void PM_KiChargeAnimation( void ) { // BFP - Ki Charge
+
+ // stop charging if it's using ki boost
+ if ( ( pm->cmd.buttons & BUTTON_KI_USE ) && ( pm->cmd.buttons & BUTTON_KI_CHARGE ) ) {
+ // handle the button to avoid toggling the animations forward and backwards while using ki boost
+ pm->cmd.buttons &= ~BUTTON_KI_CHARGE;
+ }
+
+ if ( ( pm->cmd.buttons & BUTTON_KI_USE ) && ( pm->ps->pm_flags & PMF_KI_CHARGE ) ) {
+ pm->ps->pm_flags &= ~PMF_KI_CHARGE;
+ pm->ps->pm_time = 0;
+ // do jump animation if it's falling
+ if ( !( pml.groundTrace.contents & MASK_PLAYERSOLID )
+ && !( pm->ps->eFlags & EF_FLIGHT ) && !( pm->cmd.buttons & BUTTON_ENABLEFLIGHT )
+ && !( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMP
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMPB )
+ && pm->waterlevel <= 1 // Don't force inside the water
+ && !( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) { // avoid forcing animations on transformation phase
+ PM_ForceJumpAnim();
+ PM_ContinueTorsoAnim( TORSO_STAND ); // Keep the torso
+ }
+ return;
+ }
+
+ if ( ( pm->ps->pm_flags & PMF_KI_CHARGE ) && !( pm->cmd.buttons & BUTTON_KI_CHARGE ) ) {
+ pm->ps->pm_time = 200; // Make sure to avoid entering jump flying status after recharging ki
+ pm->ps->eFlags &= ~EF_AURA; // Make sure the aura is off, otherwise the ki use proceeds
+ pm->ps->pm_flags &= ~PMF_KI_CHARGE;
+ if ( !( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) { // avoid forcing animations on transformation phase
+ PM_ContinueLegsAnim( LEGS_IDLE ); // Keep the legs when being near to the ground at that height
+ }
+ // do jump animation if it's falling
+ if ( !( pml.groundTrace.contents & MASK_PLAYERSOLID )
+ && !( pm->ps->eFlags & EF_FLIGHT )
+ && !( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMP
+ || ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_JUMPB )
+ && pm->waterlevel <= 1 // don't force inside the water
+ && ( !( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) ) { // avoid forcing animations on transformation phase
+ PM_ForceJumpAnim();
+ PM_ContinueTorsoAnim( TORSO_STAND ); // Keep the torso
+ }
+ }
+
+ if ( pm->cmd.buttons & BUTTON_KI_CHARGE ) {
+ if ( !( pm->ps->pm_flags & PMF_KI_CHARGE ) ) {
+ pm->ps->eFlags &= ~EF_AURA; // Make sure the aura is off, otherwise the visual charging effect continues without handling correctly
+ }
+ pm->ps->eFlags &= ~EF_KI_BOOST;
+ pm->ps->eFlags &= ~EF_FIRING; // don't display shooting effects
+ pm->ps->pm_flags |= PMF_KI_CHARGE;
+ if ( !( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) { // avoid forcing animations on transformation phase
+ PM_ContinueTorsoAnim( TORSO_CHARGE );
+ PM_ContinueLegsAnim( LEGS_CHARGE );
+ }
+ }
+
+ // handle the button to avoid toggling ki boost when already used "kiusetoggle" key bind
+ if ( ( pm->cmd.buttons & BUTTON_KI_USE ) && ( pm->ps->eFlags & EF_KI_BOOST ) ) {
+ pm->ps->eFlags &= ~EF_KI_BOOST;
+ }
+}
+
+/*
+==============
+PM_UltimateTierTransformAnimation
+==============
+*/
+static void PM_UltimateTierTransformAnimation( void ) { // BFP - Ultimate Tier transform animation
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ pm->ps->pm_flags &= ~( PMF_BLOCK | PMF_MELEE );
+ pm->ps->eFlags &= ~EF_FIRING;
+ pm->ps->weaponstate = WEAPON_READY;
+ pm->cmd.buttons &= ~( BUTTON_ATTACK | BUTTON_WALKING | BUTTON_GESTURE | BUTTON_USE_HOLDABLE | BUTTON_MELEE | BUTTON_BLOCK );
+ pm->cmd.forwardmove = pm->cmd.rightmove = pm->cmd.upmove = 0;
+ pm->ps->viewangles[PITCH] = 0;
+ pm->ps->viewangles[ROLL] = 0;
+ PM_ContinueTorsoAnim( TORSO_CHARGE );
+ PM_ContinueLegsAnim( LEGS_CHARGE );
+ }
+}
+
+/*
+==============
+PM_HitStunAnimation
+==============
+*/
+static void PM_HitStunAnimation( void ) { // BFP - Hit stun
+
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) {
+ PM_StartTorsoAnim( TORSO_STUN );
+ PM_StartLegsAnim( LEGS_IDLECR );
+ }
+}
+
+/*
+==============
+PM_Melee
+==============
+*/
+static void PM_Melee( void ) { // BFP - Melee
+ // Don't allow pressing ki attack and block buttons when melee is being used
+ if ( ( pm->ps->pm_flags & PMF_MELEE )
+ || ( pm->cmd.buttons & BUTTON_MELEE ) ) {
+ pm->ps->eFlags &= ~EF_FIRING; // don't display shooting effects
+ pm->ps->eFlags &= ~EF_READY_KI_ATTACK; // leave ready ki attack
+ pm->cmd.buttons &= ~( BUTTON_ATTACK | BUTTON_BLOCK );
+ }
}
/*
==============
-PM_FlightAnimation
+PM_KiExplosionWave
+
+Handle ki explosion wave during and at the end of its use
==============
*/
-static void PM_FlightAnimation( void ) { // BFP - Flight
- if ( pm->ps->pm_flags & PMF_FLYING ) {
-
- // make sure to handle the PM_flag
- pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
-
- if ( pm->cmd.forwardmove > 0 ) {
- PM_StartTorsoAnim( TORSO_FLYA );
- PM_ForceLegsAnim( LEGS_FLYA );
- PM_StartLegsAnim( LEGS_FLYA );
- } else if ( pm->cmd.forwardmove < 0 ) {
- PM_StartTorsoAnim( TORSO_FLYB );
- PM_ForceLegsAnim( LEGS_FLYB );
- PM_StartLegsAnim( LEGS_FLYB );
- } else {
- // PM_ForceLegsAnim( LEGS_FLYIDLE );
- PM_ContinueLegsAnim( LEGS_FLYIDLE );
- }
+static void PM_KiExplosionWave( void ) { // BFP - Ki explosion wave handling
+ // ki explosion wave state
+ if ( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE
+ && ( pm->ps->eFlags & EF_FIRING ) ) {
+ // don't move, also these keys cannot be used; blocking, ki charge and ki boost statuses are removed
+ pm->cmd.forwardmove = pm->cmd.rightmove = pm->cmd.upmove = 0;
+ pm->cmd.buttons &= ~( BUTTON_KI_USE | BUTTON_KI_CHARGE | BUTTON_BLOCK );
+ pm->ps->pm_flags &= ~( PMF_KI_CHARGE | PMF_BLOCK );
+ pm->ps->pm_flags |= PMF_JUMP_HELD; // avoid pressing jump key
+ pm->ps->eFlags &= ~EF_KI_BOOST;
return;
}
- // Handle the player movement animation if trying to change quickly the direction of forward or backward
- if ( !pm->isFlying && !( pml.groundTrace.contents & CONTENTS_SOLID )
- && ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) ) {
+ // ki explosion wave stun state
+ if ( pm->ps->weaponstate == WEAPON_STUN ) {
+ // don't move, also these keys cannot be used; melee, attacking, blocking, ki charge and ki boost statuses are removed
+ pm->cmd.forwardmove = pm->cmd.rightmove = pm->cmd.upmove = 0;
+ pm->cmd.buttons &= ~( BUTTON_ATTACK | BUTTON_KI_CHARGE | BUTTON_KI_USE | BUTTON_BLOCK | BUTTON_MELEE );
+ pm->ps->pm_flags &= ~( PMF_KI_CHARGE | PMF_BLOCK | PMF_MELEE );
+ pm->ps->pm_flags |= PMF_JUMP_HELD; // avoid pressing jump key
+ pm->ps->eFlags &= ~EF_KI_BOOST;
+ return;
+ }
+}
- // stops entering again here and don't change the animation to backwards/forward
- pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
+/*
+==============
+PM_BeamStruggleStatus
- if ( pm->cmd.forwardmove > 0 ) {
- PM_ForceLegsAnim( LEGS_JUMP );
- PM_StartLegsAnim( LEGS_JUMP );
- } else if ( pm->cmd.forwardmove < 0 ) { // when failing backwards after flying
- PM_ForceLegsAnim( LEGS_JUMPB );
- PM_StartLegsAnim( LEGS_JUMPB );
- } else {
- PM_ForceLegsAnim( LEGS_JUMP );
- PM_StartLegsAnim( LEGS_JUMP );
+Handle beam struggle status
+==============
+*/
+static void PM_BeamStruggleStatus( void ) { // BFP - Beam struggle handling
+ if ( pm->ps->weaponstate == WEAPON_BEAMSTRUGGLE ) {
+ // don't move, also these keys cannot be used
+ pm->cmd.forwardmove = pm->cmd.rightmove = pm->cmd.upmove = 0;
+ }
+}
+
+
+/*
+===========
+PM_KiConsumption
+===========
+*/
+static void PM_KiConsumption( int addTime, int kiConsume ) { // BFP - Ki consumption when using ki attacks
+ if ( pm->ps->stats[STAT_KI] >= 0 ) { // avoid consuming more ki than available
+ pm->ps->stats[STAT_KI] -= kiConsume;
+ if ( pm->ps->stats[STAT_KI] < 0 ) {
+ pm->ps->stats[STAT_KI] = 0;
}
+ pm->ps->weaponTime += addTime;
+ } else { // not enough ki
+ pm->ps->eFlags &= ~EF_FIRING;
+ pm->ps->eFlags &= ~EF_READY_KI_ATTACK;
}
}
+
/*
-==============
-PM_KiChargeAnimation
-==============
+===========
+PM_KiCost
+===========
*/
-static void PM_KiChargeAnimation( void ) { // BFP - Ki Charge
- if ( pm->cmd.buttons & BUTTON_KI_CHARGE ) {
- PM_StartTorsoAnim( TORSO_CHARGE );
- //PM_ForceLegsAnim( LEGS_CHARGE );
- PM_StartLegsAnim( LEGS_CHARGE );
+static float PM_KiCost( void ) { // BFP - kiCost, kiCostAsPct and kiPct
+ float kiCost = ( pm->kiCost > 0 ) ? pm->kiCost : 0;
+ float kiPct = ( pm->kiPct > 1 ) ? 1 : pm->kiPct;
+ if ( pm->kiCostAsPct && kiPct > 0 ) {
+ kiCost = pm->ps->stats[STAT_MAX_KI] * kiPct;
}
+ return kiCost;
}
+
/*
-==============
-PM_HitStunAnimation
-==============
+============
+PM_ChargeKiAttackState
+============
*/
-static void PM_HitStunAnimation( void ) { // BFP - Hit stun
+static void PM_ChargeKiAttackState( int minCharge, int maxCharge, int addTime, int kiConsume ) { // BFP - Charge ki attack state
+ const int ATTACK_CHARGE_LIMIT = 6;
- // When the player doesn't have more ki, play hit stun animation
- if ( pm->ps->stats[STAT_KI] <= 0
- && pm->ps->pm_type != PM_DEAD
- && pm->ps->pm_type != PM_SPECTATOR ) {
- PM_StartTorsoAnim( TORSO_STUN );
- PM_ForceLegsAnim( LEGS_IDLECR );
+ PM_KiConsumption( addTime, kiConsume );
+ if ( !pm->chargeAttack && !pm->chargeAutoFire ) {
+ return;
+ }
+ if ( ( pm->chargeAutoFire
+ || maxCharge <= minCharge
+ || ( maxCharge > 0 && pm->ps->generic1 < maxCharge ) )
+ && pm->ps->generic1 < ATTACK_CHARGE_LIMIT ) {
+ ++pm->ps->generic1;
}
+ if ( pm->ps->generic1 >= minCharge && !pm->chargeAutoFire ) {
+ pm->ps->eFlags |= EF_READY_KI_ATTACK;
+ }
+}
+
+/*
+============
+PM_RandomWeaponTime
+============
+*/
+static int PM_RandomWeaponTime( void ) { // BFP - randomWeaponTime calculation
+ int weaponTime = pm->weaponTime;
+ int randomWeaponTime = 0;
+ if ( pm->randomWeaponTime > 0 ) {
+ randomWeaponTime = random() * pm->randomWeaponTime;
+ }
+ weaponTime += randomWeaponTime;
+ return weaponTime;
}
+
/*
==============
PM_Weapon
@@ -1609,7 +3001,19 @@ Generates weapon events and modifes the weapon counter
==============
*/
static void PM_Weapon( void ) {
- int addTime;
+ // BFP - HIGHLY MODIFIED
+ // BFP - Ki cost
+ float kiCost = PM_KiCost();
+ // BFP - Weapon time
+ int weaponTime = PM_RandomWeaponTime();
+
+ // BFP - Hit stun and ultimate tier, avoid shooting if the player is in this status
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0 || ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) ) {
+ pm->ps->eFlags &= ~EF_READY_KI_ATTACK;
+ pm->ps->generic1 = 0;
+ pm->ps->weaponTime = 0;
+ return;
+ }
// don't allow attack until all buttons are up
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
@@ -1631,7 +3035,7 @@ static void PM_Weapon( void ) {
if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT
- && pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) {
+ && pm->ps->stats[STAT_HEALTH] >= pm->ps->stats[STAT_MAX_HEALTH] ) { // BFP - Before Q3: + 25
// don't use medkit if at max health
} else {
pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
@@ -1644,7 +3048,6 @@ static void PM_Weapon( void ) {
pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
}
-
// make weapon function
if ( pm->ps->weaponTime > 0 ) {
pm->ps->weaponTime -= pml.msec;
@@ -1653,139 +3056,287 @@ static void PM_Weapon( void ) {
// check for weapon change
// can't change if weapon is firing, but can change
// again if lowering or raising
- if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
+ if ( pm->ps->weaponstate != WEAPON_ACTIVE // BFP - Avoid if the beam/rdmissile/forcefield is still firing
+ && pm->ps->weaponstate != WEAPON_STUN // BFP - Avoid when being stunned
+ && ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) ) {
if ( pm->ps->weapon != pm->cmd.weapon ) {
PM_BeginWeaponChange( pm->cmd.weapon );
}
}
- if ( pm->ps->weaponTime > 0 ) {
- return;
- }
-
// change weapon if time
- if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
+ if ( pm->ps->weaponstate == WEAPON_DROPPING
+ && pm->ps->weaponTime <= 0 ) { // BFP - Handling ki attack animations when these already shoot
PM_FinishWeaponChange();
return;
}
- if ( pm->ps->weaponstate == WEAPON_RAISING ) {
- pm->ps->weaponstate = WEAPON_READY;
- if ( pm->ps->weapon == WP_GAUNTLET ) {
- PM_StartTorsoAnim( TORSO_STAND ); // BFP - before TORSO_STAND2
- } else {
- PM_StartTorsoAnim( TORSO_STAND );
+ // BFP - Melee, avoid shooting if the player is in this status
+ if ( ( pm->cmd.buttons & BUTTON_MELEE )
+ && !( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE )
+ && pm->ps->weaponstate != WEAPON_STUN ) {
+ // only use when there's no splitting ki ball until it has been splitted or collided,
+ // unless if the player wanna change the weapon from this state
+ if ( pm->ps->weaponstate != WEAPON_ACTIVE ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ pm->ps->generic1 = 0;
+ }
+ // Melee fight handling
+ if ( pm->meleeHit && pm->ps->weaponTime <= 0 ) {
+ int rndSnd = rand() % 6;
+ pm->ps->weaponTime += 300;
+ pm->ps->pm_flags |= PMF_MELEE;
+ // melee sound event is randomly executed
+ if ( rndSnd > 3 ) {
+ PM_AddEvent( EV_MELEE );
+ }
}
return;
}
- // check for fire
- if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) {
- pm->ps->weaponTime = 0;
- pm->ps->weaponstate = WEAPON_READY;
- return;
- }
-
- // start the animation even if out of ammo
- if ( pm->ps->weapon == WP_GAUNTLET ) {
- // the guantlet only "fires" when it actually hits something
- if ( !pm->gauntletHit ) {
+ // BFP - Weapon states, Q3 doesn't have this way
+ switch( pm->ps->weaponstate ) {
+ case WEAPON_READY:
+ pm->ps->eFlags &= ~EF_READY_KI_ATTACK;
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) {
+ pm->ps->generic1 = 0;
+ } else {
+ if ( pm->ps->weaponTime <= 0 ) {
+ // BFP - sbeam attack type
+ if ( pm->attackType == ATK_SBEAM ) {
+ pm->ps->weaponTime += weaponTime;
+ pm->ps->stats[STAT_KI] -= kiCost;
+ pm->ps->weaponstate = WEAPON_ACTIVE;
+ // fire and make a sound
+ PM_AddEvent( EV_FIRE_WEAPON );
+ break;
+ }
+ if ( pm->chargeAttack || pm->chargeAutoFire ) {
+ pm->ps->weaponTime += weaponTime;
+ }
+ pm->ps->weaponstate = WEAPON_FIRING;
+ }
+ }
+ break;
+ case WEAPON_DROPPING:
+ case WEAPON_RAISING:
+ if ( pm->ps->weaponTime <= 0 ) {
pm->ps->weaponTime = 0;
+ pm->ps->generic1 = 0;
pm->ps->weaponstate = WEAPON_READY;
- return;
}
- PM_StartTorsoAnim( TORSO_STAND );
- } else {
- PM_StartTorsoAnim( TORSO_STAND );
- }
-
- pm->ps->weaponstate = WEAPON_FIRING;
+ break;
+ case WEAPON_FIRING:
+ // don't allow ki charging while charging the attack
+ if ( ( pm->ps->pm_flags & PMF_KI_CHARGE ) || ( pm->cmd.buttons & BUTTON_KI_CHARGE ) ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ pm->ps->weaponTime = 0;
+ break;
+ }
-// BFP - disable ammo stuff, take ki instead \o/
-#if 0
- // check for out of ammo
- if ( ! pm->ps->ammo[ pm->ps->weapon ] ) {
- PM_AddEvent( EV_NOAMMO );
- pm->ps->weaponTime += 500;
- return;
- }
+ // BFP - There are charging states here
+ if ( pm->chargeAttack && !pm->chargeAutoFire ) {
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) {
+ // BFP - When the ki attack is fully charged, enter beam firing state
+ // or enter splitting ki ball firing state if it's a splitting ki ball
+ pm->ps->eFlags &= ~EF_READY_KI_ATTACK;
+ // no fully charged, skip...
+ if ( pm->ps->generic1 < pm->minCharge ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ pm->ps->weaponTime = 0;
+ break;
+ }
- // take an ammo away if not infinite
- if ( pm->ps->ammo[pm->ps->weapon] != -1 ) {
- pm->ps->ammo[pm->ps->weapon]--;
- }
-#endif
+ // handle the animation for the start of beam or ball shoot
+ switch( pm->attackType ) {
+ case ATK_MISSILE:
+ pm->ps->weaponstate = WEAPON_READY;
+ pm->ps->weaponTime += weaponTime;
+ break;
+ case ATK_RDMISSILE:
+ case ATK_BEAM:
+ pm->ps->weaponstate = WEAPON_ACTIVE;
+ pm->ps->weaponTime += weaponTime;
+ break;
+ case ATK_FORCEFIELD:
+ pm->ps->eFlags |= EF_FIRING;
+ pm->ps->weaponstate = WEAPON_ACTIVE;
+ pm->ps->weaponTime += weaponTime;
+ }
- // fire weapon
- PM_AddEvent( EV_FIRE_WEAPON );
+ // fire and make a sound
+ PM_AddEvent( EV_FIRE_WEAPON );
+ }
+ }
- // BFP - TODO:
- // 1st attackset simple ki shot attack:
- // pm->ps->stats[STAT_KI] -= 10;
-
- // 2nd attackset second simple ki shot attack:
- // pm->ps->stats[STAT_KI] -= 100;
-
- // 3rd attackset ki blasts:
- // pm->ps->stats[STAT_KI] -= 240;
+ // chargeAutoFire: keep firing while attack key is holding
+ if ( pm->chargeAutoFire
+ && pm->ps->weaponTime <= 0 ) {
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK )
+ || ( pm->cmd.buttons & BUTTON_MELEE )
+ || pm->ps->weapon != pm->cmd.weapon ) { // avoid when changing weapon
+ pm->ps->weaponstate = WEAPON_READY;
+ break;
+ }
+ if ( pm->cmd.buttons & BUTTON_ATTACK ) {
+ pm->ps->eFlags |= EF_FIRING;
+ PM_AddEvent( EV_FIRE_WEAPON );
+ }
+ if ( pm->attackType == ATK_BEAM
+ || pm->attackType == ATK_FORCEFIELD
+ || pm->attackType == ATK_RDMISSILE ) {
+ pm->ps->weaponstate = WEAPON_ACTIVE;
+ }
+ }
- // 4th attackset charging some ki attack:
- // pm->ps->stats[STAT_KI] -= 250; // reduces every time you stand until you shot or stop
-
- // 5th attackset charging some ki attack:
- // pm->ps->stats[STAT_KI] -= 1000; // reduces every time you stand until you shot or stop
+ // check for fire
+ if ( pm->ps->weaponTime <= 0 && ( pm->cmd.buttons & BUTTON_ATTACK )
+ && ( pm->chargeAttack || pm->chargeAutoFire ) ) {
+ PM_ChargeKiAttackState( pm->minCharge, pm->maxCharge, weaponTime, kiCost );
+ }
+ if ( !pm->chargeAttack && !pm->chargeAutoFire
+ && pm->ps->weaponTime <= 0 ) {
+ if ( pm->attackType == ATK_HITSCAN ) {
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ break;
+ }
+ if ( pm->cmd.buttons & BUTTON_ATTACK ) {
+ pm->ps->eFlags |= EF_FIRING;
+ PM_AddEvent( EV_FIRE_WEAPON );
+ PM_KiConsumption( weaponTime, kiCost );
+ pm->ps->weaponstate = WEAPON_READY;
+ }
+ break;
+ }
- switch( pm->ps->weapon ) {
- default:
- case WP_GAUNTLET:
- addTime = 400;
- pm->ps->stats[STAT_KI] -= 0;
- break;
- case WP_MACHINEGUN:
- addTime = 100;
- pm->ps->stats[STAT_KI] -= 50;
- break;
- case WP_SHOTGUN:
- addTime = 1000;
- pm->ps->stats[STAT_KI] -= 100;
- break;
- case WP_GRENADE_LAUNCHER:
- addTime = 800;
- pm->ps->stats[STAT_KI] -= 100;
- break;
- case WP_ROCKET_LAUNCHER:
- addTime = 800;
- pm->ps->stats[STAT_KI] -= 50;
- break;
- case WP_LIGHTNING:
- addTime = 50;
- pm->ps->stats[STAT_KI] -= 70;
- break;
- case WP_PLASMAGUN:
- addTime = 100;
- pm->ps->stats[STAT_KI] -= 120;
- break;
- case WP_RAILGUN:
- addTime = 1500;
- pm->ps->stats[STAT_KI] -= 150;
- break;
- case WP_BFG:
- addTime = 200;
- pm->ps->stats[STAT_KI] -= 20;
+ pm->ps->eFlags |= EF_FIRING;
+ PM_AddEvent( EV_FIRE_WEAPON );
+ PM_KiConsumption( weaponTime, kiCost );
+ if ( pm->attackType == ATK_BEAM ) {
+ pm->ps->weaponstate = WEAPON_ACTIVE;
+ }
+ if ( pm->attackType == ATK_MISSILE
+ || pm->attackType == ATK_RDMISSILE ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ }
+ }
break;
- case WP_GRAPPLING_HOOK:
- addTime = 400;
- pm->ps->stats[STAT_KI] -= 100;
+ // BFP - NOTE: The beam is triggering until pressing the attack key again after holded, using ki charge or blocking
+ // Pressing attack key again or changing weapon, the beam is exploded before the impact
+ case WEAPON_ACTIVE:
+ // BFP - NOTE: Lock movement during beam struggle
+ case WEAPON_BEAMSTRUGGLE:
+ pm->ps->eFlags |= EF_FIRING; // keep playing firing sound
+
+ // chargeAutoFire: keep firing while attack key is holding
+ if ( pm->attackType == ATK_BEAM
+ && pm->chargeAutoFire ) {
+ if ( ( pm->cmd.buttons & BUTTON_ATTACK )
+ && pm->ps->weaponTime <= 0 ) {
+ // PM_AddEvent( EV_FIRE_WEAPON );
+ PM_ChargeKiAttackState( pm->minCharge, pm->maxCharge, weaponTime, kiCost );
+ }
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK )
+ || ( ( pm->ps->pm_flags & PMF_KI_CHARGE ) && ( pm->ps->eFlags & EF_AURA ) )
+ || ( pm->ps->pm_flags & PMF_BLOCK ) ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ }
+ break;
+ }
+
+ if ( ( pm->chargeAttack || pm->chargeAutoFire )
+ && pm->attackType == ATK_FORCEFIELD ) {
+ if ( ( pm->cmd.buttons & BUTTON_ATTACK )
+ && pm->ps->weaponTime <= 0 ) {
+ PM_AddEvent( EV_FIRE_WEAPON );
+ PM_ChargeKiAttackState( pm->minCharge, pm->maxCharge, weaponTime, kiCost );
+ }
+ pm->ps->eFlags &= ~( EF_AURA | EF_KI_BOOST );
+
+ // fall even whether the player is flying
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ pm->ps->velocity[2] -= pm->ps->gravity * 2 * pml.frametime;
+ }
+
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK )
+ || ( pm->cmd.buttons & BUTTON_MELEE )
+ || pm->ps->weapon != pm->cmd.weapon ) { // avoid when changing weapon
+ pm->ps->weaponTime = pm->movementPenalty;
+ pm->ps->eFlags &= ~EF_FIRING;
+ if ( pm->movementPenalty > 0 ) {
+ pm->ps->weaponstate = WEAPON_STUN;
+ } else {
+ pm->ps->weaponstate = WEAPON_READY;
+ }
+ }
+ break;
+ }
+
+ if ( pm->attackType == ATK_RDMISSILE
+ && pm->chargeAutoFire ) {
+ if ( ( pm->cmd.buttons & BUTTON_ATTACK )
+ && pm->ps->weaponTime <= 0 ) {
+ PM_AddEvent( EV_FIRE_WEAPON );
+ PM_ChargeKiAttackState( pm->minCharge, pm->maxCharge, weaponTime, kiCost );
+ }
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK )
+ || ( pm->cmd.buttons & BUTTON_MELEE )
+ || pm->ps->weapon != pm->cmd.weapon ) { // avoid when changing weapon
+ pm->ps->weaponstate = WEAPON_READY;
+ pm->ps->weaponTime = 0;
+ }
+ break;
+ }
+ pm->ps->generic1 = 0;
+
+ // BFP - sbeam attack type
+ if ( pm->attackType == ATK_SBEAM ) {
+ if ( pm->ps->weaponTime <= 0 ) {
+ pm->ps->stats[STAT_KI] -= kiCost;
+ pm->ps->weaponTime += weaponTime;
+ }
+ if ( !( pm->cmd.buttons & BUTTON_ATTACK )
+ || ( ( pm->ps->pm_flags & PMF_KI_CHARGE ) && ( pm->ps->eFlags & EF_AURA ) )
+ || ( pm->cmd.buttons & BUTTON_MELEE ) ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ }
+ break;
+ }
+
+ if ( pm->attackType == ATK_BEAM
+ && ( ( pm->cmd.buttons & BUTTON_ATTACK )
+ || ( ( pm->ps->pm_flags & PMF_KI_CHARGE ) && ( pm->ps->eFlags & EF_AURA ) )
+ || ( pm->ps->pm_flags & PMF_BLOCK ) ) ) {
+ pm->ps->weaponstate = WEAPON_READY;
+ }
break;
- }
+ case WEAPON_STUN:
+ if ( pm->ps->weaponTime <= 0 ) {
+ pm->ps->weaponTime = 0;
+ pm->ps->weaponstate = WEAPON_READY;
+ }
+ pm->ps->generic1 = 0;
- // BFP - Ki Use
- if ( pm->cmd.buttons & BUTTON_KI_USE ) {
- addTime /= 1.3;
+ // fall even whether the player is flying
+ if ( pm->ps->eFlags & EF_FLIGHT ) {
+ pm->ps->velocity[2] -= pm->ps->gravity * 2 * pml.frametime;
+ }
}
-
- pm->ps->weaponTime += addTime;
+
+ // debug print about weapon states and weapon time
+#if 0
+ switch( pm->ps->weaponstate ) {
+ case WEAPON_READY: Com_Printf( "WEAPON_READY\n" ); break;
+ case WEAPON_RAISING: Com_Printf( "WEAPON_RAISING\n" ); break;
+ case WEAPON_DROPPING: Com_Printf( "WEAPON_DROPPING\n" ); break;
+ case WEAPON_FIRING: Com_Printf( "WEAPON_FIRING\n" ); break;
+ case WEAPON_ACTIVE: Com_Printf( "WEAPON_ACTIVE\n" ); break;
+ case WEAPON_STUN: Com_Printf( "WEAPON_STUN\n" ); break;
+ }
+ Com_Printf( "weaponTime: %d\n", pm->ps->weaponTime );
+#endif
}
/*
@@ -1795,6 +3346,12 @@ PM_Animate
*/
static void PM_Animate( void ) {
+
+ // BFP - Ultimate tier
+ if ( pm->ps->pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
if ( pm->cmd.buttons & BUTTON_GESTURE ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_GESTURE );
@@ -1821,6 +3378,11 @@ static void PM_DropTimers( void ) {
}
}
+ // BFP - Hit stun time
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) {
+ pm->ps->stats[STAT_HITSTUN_TIME] -= pml.msec;
+ }
+
// drop animation counter
if ( pm->ps->legsTimer > 0 ) {
pm->ps->legsTimer -= pml.msec;
@@ -1857,12 +3419,10 @@ void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
return; // no view changes at all
}
- // Com_Printf( "pml.groundPlane == qtrue: %d\n", pml.groundPlane == qtrue ? "true" : "false" );
-
// circularly clamp the angles with deltas
for (i=0 ; i<3 ; i++) {
temp = cmd->angles[i] + ps->delta_angles[i];
- if ( i == PITCH && !( ps->pm_flags & PMF_FLYING ) ) { // BFP - Avoid that when flying
+ if ( i == PITCH && !( ps->eFlags & EF_FLIGHT ) ) { // BFP - Avoid that when flying
// don't let the player look up or down more than 90 degrees
if ( temp > 16000 ) {
ps->delta_angles[i] = 16000 - cmd->angles[i];
@@ -1885,20 +3445,13 @@ Enables/disables flight
*/
static qboolean PM_EnableFlight( void ) { // BFP - Flight
- if ( !( pm->ps->pm_flags & PMF_FLYING ) ) {
- if ( pm->isFlying && ( pml.groundTrace.contents & CONTENTS_SOLID ) ) {
- pm->isFlying = qfalse;
- }
+ // BFP - Hit stun, avoid enabling flight if the player is in this status
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) {
return qfalse;
}
- if ( pm->ps->pm_flags & PMF_FLYING ) {
- if ( !pm->isFlying && ( pml.groundTrace.contents & CONTENTS_SOLID )
- && pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
- pm->ps->velocity[2] = JUMP_VELOCITY;
- }
- pm->isFlying = qtrue;
- pm->ps->groundEntityNum = ENTITYNUM_NONE;
+ if ( !( pm->ps->eFlags & EF_FLIGHT ) && !( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) ) {
+ return qfalse;
}
return qtrue;
@@ -1911,21 +3464,42 @@ PM_KiCharge
Charges ki
================
*/
-static void PM_KiCharge( pmove_t *pmove ) { // BFP - Ki Charge
- pm = pmove;
+static void PM_KiCharge( void ) { // BFP - Ki Charge
+
+ // BFP - Hit stun, avoid charging if the player is in this status
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0 ) {
+ return;
+ }
- if ( pmove->cmd.buttons & ( BUTTON_ATTACK | BUTTON_MELEE | BUTTON_KI_USE | BUTTON_BLOCK | BUTTON_ENABLEFLIGHT ) ) {
- pmove->cmd.buttons &= ~( BUTTON_ATTACK | BUTTON_MELEE | BUTTON_KI_USE | BUTTON_BLOCK | BUTTON_ENABLEFLIGHT );
+ // BFP - Ki explosion wave and stun after using it, avoid charging also
+ if ( ( pm->attackType == ATK_FORCEFIELD && pm->ps->weaponstate == WEAPON_ACTIVE
+ && ( pm->ps->eFlags & EF_FIRING ) )
+ || pm->ps->weaponstate == WEAPON_STUN ) {
+ return;
}
- pmove->cmd.forwardmove = 0;
- pmove->cmd.rightmove = 0;
- pmove->cmd.upmove = 0;
+ pm->cmd.forwardmove = pm->cmd.rightmove = 0;
+
+ if ( pm->cmd.buttons & ( BUTTON_ATTACK | BUTTON_KI_USE | BUTTON_MELEE | BUTTON_BLOCK | BUTTON_ENABLEFLIGHT ) ) {
+ pm->cmd.buttons &= ~( BUTTON_ATTACK | BUTTON_KI_USE | BUTTON_MELEE | BUTTON_BLOCK | BUTTON_ENABLEFLIGHT );
+ }
- pm->ps->velocity[0] = 0;
- pm->ps->velocity[1] = 0;
- pm->ps->velocity[2] = 0;
- pm->ps->stats[STAT_KI]++;
+ // BFP - Smoothing horizontal and forward/backward fall while ki charging
+ if ( !( pm->ps->eFlags & EF_FLIGHT ) && !( pm->cmd.buttons & BUTTON_ENABLEFLIGHT ) ) {
+ float speed = VectorLength( pm->ps->velocity );
+ if ( speed > 0 ) {
+ float control = speed < pm_stopspeed ? pm_stopspeed : speed;
+ float drop = control * pm_friction * pml.frametime;
+
+ // scale the velocity
+ float newspeed = speed - drop;
+ if ( newspeed < 0 ) newspeed = 0;
+ newspeed /= speed;
+
+ pm->ps->velocity[0] *= newspeed;
+ pm->ps->velocity[1] *= newspeed;
+ }
+ }
}
/*
@@ -1935,40 +3509,17 @@ PM_HitStun
Receives hit stun
================
*/
-static void PM_HitStun( pmove_t *pmove ) { // BFP - Hit stun
- pm = pmove;
+static void PM_HitStun( void ) { // BFP - Hit stun
- if ( pmove->cmd.buttons & ( BUTTON_ATTACK | BUTTON_MELEE | BUTTON_KI_USE | BUTTON_BLOCK | BUTTON_ENABLEFLIGHT ) ) {
- pmove->cmd.buttons &= ~( BUTTON_ATTACK | BUTTON_MELEE | BUTTON_KI_USE | BUTTON_BLOCK | BUTTON_ENABLEFLIGHT );
- }
+ pm->cmd.buttons &= ~( BUTTON_MELEE | BUTTON_KI_USE | BUTTON_BLOCK | BUTTON_ENABLEFLIGHT );
+ pm->cmd.upmove = 0;
- pm->ps->pm_flags &= ~PMF_FLYING;
- pm->ps->pm_flags &= ~PMF_KI_BOOST;
+ pm->ps->eFlags &= ~EF_FLIGHT;
+ pm->ps->eFlags &= ~EF_KI_BOOST;
+ // don't display shot effects on the stunned status
+ pm->ps->eFlags &= ~EF_FIRING;
pm->ps->eFlags &= ~EF_AURA;
-
- pmove->cmd.forwardmove = 0;
- pmove->cmd.rightmove = 0;
- pmove->cmd.upmove = 0;
-
-// BFP - TODO: Hit stun time, maybe remove this and find another way?
-#if 0
- if ( pm->ps->hitStunTime > 0 ) {
- pm->ps->hitStunTime -= pml.msec;
- return;
- }
-
- // drop misc timing counter
- /*
- if ( pm->ps->pm_time ) {
- if ( pml.msec >= pm->ps->pm_time ) {
- pm->ps->pm_flags &= ~PMF_ALL_TIMES;
- pm->ps->pm_time = 0;
- } else {
- pm->ps->pm_time -= pml.msec;
- }
- }
- */
-#endif
+ pm->ps->weaponstate = WEAPON_READY;
}
/*
@@ -1991,6 +3542,9 @@ void PmoveSingle (pmove_t *pmove) {
pm->watertype = 0;
pm->waterlevel = 0;
+ // BFP - Setup bfp_weapon.cfg
+ PM_SetupBFP_weaponCfg();
+
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies
}
@@ -2008,12 +3562,40 @@ void PmoveSingle (pmove_t *pmove) {
pm->ps->eFlags &= ~EF_TALK;
}
+ // BFP - Handling the PMF flag when stepping the ground and when preparing to attack
+ if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
+ // BFP - TODO: Set to the first selected weapon
+ pm->ps->pm_flags &= ~PMF_AIR_GRAVITY; // BFP - Air gravity
+ }
+
+ // BFP - No flight
+ if ( pm->noFlight ) {
+ pm->cmd.buttons &= ~BUTTON_ENABLEFLIGHT;
+ pm->ps->eFlags &= ~EF_FLIGHT;
+ }
+
+ // BFP - Melee only
+ if ( pm->meleeOnly ) {
+ pm->cmd.buttons &= ~BUTTON_ATTACK;
+ pm->ps->eFlags &= ~EF_FIRING;
+ }
+
+ // BFP - When blocking, disable the ki use button, also that avoids jittering
+ if ( pm->ps->pm_flags & PMF_BLOCK ) {
+ pm->cmd.buttons &= ~BUTTON_KI_USE;
+ }
+
// set the firing flag for continuous beam weapons
if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
- && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) {
+ && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ]
+ && pm->ps->weapon != WP_SHOTGUN ) { // BFP - That would be with chargeAttack set
pm->ps->eFlags |= EF_FIRING;
} else {
- pm->ps->eFlags &= ~EF_FIRING;
+ if ( pm->ps->weapon != WP_SHOTGUN ) { // BFP - That would be with chargeAttack set
+ pm->ps->eFlags &= ~EF_FIRING;
+ // BFP - Handle attack button when holding to prepare the attack at the start
+ pm->cmd.buttons &= ~BUTTON_ATTACK;
+ }
}
// clear the respawned flag if attack and use are cleared
@@ -2064,31 +3646,46 @@ void PmoveSingle (pmove_t *pmove) {
pm->ps->pm_flags &= ~PMF_JUMP_HELD;
}
+ // BFP - No handling PMF_BACKWARDS_RUN
+#if 0
// decide if backpedaling animations should be used
if ( pm->cmd.forwardmove < 0 ) {
pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
}
+#endif
if ( pm->ps->pm_type >= PM_DEAD ) {
// BFP - If player is dead, disable the following statuses
- pm->ps->pm_flags &= ~PMF_FLYING;
- pm->ps->pm_flags &= ~PMF_KI_BOOST;
+ pm->ps->eFlags &= ~EF_FLIGHT;
+ pm->ps->eFlags &= ~EF_KI_BOOST;
pm->ps->eFlags &= ~EF_AURA;
-// BFP - NOTE: disabled for notes, don't allow pressing these buttons
-#if 0
pm->cmd.buttons &= ~BUTTON_KI_CHARGE;
pm->cmd.buttons &= ~BUTTON_KI_USE;
-#endif
pm->cmd.forwardmove = 0;
pm->cmd.rightmove = 0;
pm->cmd.upmove = 0;
}
+ // BFP - Ki Charge
+ if ( ( pmove->cmd.buttons & BUTTON_KI_CHARGE )
+ && !( pmove->cmd.buttons & BUTTON_KI_USE )
+ && pm->ps->pm_type != PM_DEAD
+ && pm->ps->pm_type != PM_SPECTATOR ) {
+ PM_KiCharge();
+ }
+
+ // BFP - Hit stun
+ if ( pm->ps->stats[STAT_HITSTUN_TIME] > 0
+ && pm->ps->pm_type != PM_DEAD
+ && pm->ps->pm_type != PM_SPECTATOR ) {
+ PM_HitStun();
+ }
+
if ( pm->ps->pm_type == PM_SPECTATOR ) {
PM_CheckDuck ();
PM_FlyMove ();
@@ -2117,6 +3714,9 @@ void PmoveSingle (pmove_t *pmove) {
// set mins, maxs, and viewheight
PM_CheckDuck ();
+ // BFP - Flight start
+ PM_FlightStart();
+
// set groundentity
PM_GroundTrace();
@@ -2124,37 +3724,16 @@ void PmoveSingle (pmove_t *pmove) {
PM_DeadMove ();
}
- // BFP - Ki Charge
- if ( ( pmove->cmd.buttons & BUTTON_KI_CHARGE )
- && ( pm->ps->pm_type != PM_DEAD ) ) {
- PM_KiCharge( pmove );
- }
+ // BFP - Melee
+ PM_Melee();
- PM_DropTimers();
+ // BFP - Ki explosion wave handling
+ PM_KiExplosionWave();
- // BFP - Hit stun
- if ( pm->ps->stats[STAT_KI] <= 0
- && pm->ps->pm_type != PM_DEAD
- && pm->ps->pm_type != PM_SPECTATOR ) {
- PM_HitStun( pmove );
-// BFP - TODO: Hit stun time, maybe remove this and find another way?
-#if 0
- if ( pm->ps->hitStunTime <= 0 ) {
- pm->ps->hitStunTime = pml.msec + 1000; // give 1 second to be stunned
- }
-#endif
- }
-// BFP - TODO: Hit stun time, maybe remove this and find another way?
-#if 0
- if ( pm->ps->hitStunTime == -3
- && pm->ps->pm_type != PM_DEAD
- && pm->ps->pm_type != PM_SPECTATOR ) { // when receives attack from a ki boost melee
- pm->ps->hitStunTime = pml.msec + 3000; // give 3 seconds to be stunned
- }
- if ( pm->ps->hitStunTime > 0 ) {
- PM_HitStun( pmove );
- }
-#endif
+ // BFP - Beam struggle handling
+ PM_BeamStruggleStatus();
+
+ PM_DropTimers();
// BFP - Flight
if ( PM_EnableFlight() ) {
@@ -2169,9 +3748,13 @@ void PmoveSingle (pmove_t *pmove) {
PM_AirMove();
}
#endif
+ // BFP - No handling PMF_TIME_WATERJUMP
+#if 0
else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
PM_WaterJumpMove();
- } else if ( pm->waterlevel > 1 ) {
+ }
+#endif
+ else if ( pm->waterlevel > 1 ) {
// swimming
PM_WaterMove();
} else if ( pml.walking ) {
@@ -2182,6 +3765,9 @@ void PmoveSingle (pmove_t *pmove) {
PM_AirMove();
}
+ // BFP - Fly tilt
+ PM_FlyTiltView();
+
PM_Animate();
// set groundentity, watertype, and waterlevel
@@ -2200,6 +3786,9 @@ void PmoveSingle (pmove_t *pmove) {
// BFP - Ki Charge animation
PM_KiChargeAnimation();
+ // BFP - Ultimate Tier transform animation
+ PM_UltimateTierTransformAnimation();
+
// BFP - Hit stun animation
PM_HitStunAnimation();
@@ -2209,8 +3798,9 @@ void PmoveSingle (pmove_t *pmove) {
// entering / leaving water splashes
PM_WaterEvents();
+ // BFP - BFP disabled that because the velocity calcualtions aren't correct when timescale is less than 1
// snap some parts of playerstate to save network bandwidth
- trap_SnapVector( pm->ps->velocity );
+ // trap_SnapVector( pm->ps->velocity );
}
@@ -2236,6 +3826,9 @@ void Pmove (pmove_t *pmove) {
pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<ps->commandTime != finalTime ) {
@@ -2260,8 +3853,4 @@ void Pmove (pmove_t *pmove) {
pmove->cmd.upmove = 20;
}
}
-
- //PM_CheckStuck();
-
}
-
diff --git a/source/game/bg_public.h b/source/game/bg_public.h
index 09988d8..4221b72 100644
--- a/source/game/bg_public.h
+++ b/source/game/bg_public.h
@@ -40,7 +40,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ITEM_RADIUS 15 // item sizes are needed for client side pickup detection
-#define LIGHTNING_RANGE 768
+#define LIGHTNING_RANGE 1500 //768
#define SCORE_NOT_PRESENT -9999 // for the CS_SCORES[12] when only one player is present
@@ -99,13 +99,14 @@ typedef enum {
GT_TOURNAMENT, // one on one tournament
GT_SINGLE_PLAYER, // single player ffa
+ GT_SURVIVAL, // BFP - Survival "survival": g_gametype 3
+ GT_MONSTER, // BFP - Monster "monster" / "oozaru": g_gametype 4
+
//-- team games go after this --
GT_TEAM, // team deathmatch
+ GT_TLMS, // BFP - Team Last Man Standing "lms" / "tlms": g_gametype 6
GT_CTF, // capture the flag
- GT_1FCTF,
- GT_OBELISK,
- GT_HARVESTER,
GT_MAX_GAME_TYPE
} gametype_t;
@@ -132,36 +133,57 @@ typedef enum {
PM_SPINTERMISSION // no movement or status bar
} pmtype_t;
+// BFP - Cannot be more than 16, weaponstate is a 4-bit integer size
typedef enum {
WEAPON_READY,
WEAPON_RAISING,
WEAPON_DROPPING,
- WEAPON_FIRING
+ WEAPON_FIRING,
+
+ // BFP - The following weapon states are to handle the movements
+ // (originally BFP didn't use that because of their abuse of WP_, PW_ and STAT_ stuff in their networking):
+ WEAPON_ACTIVE, // BFP - Active fire
+ WEAPON_BEAMSTRUGGLE, // BFP - Beam struggle
+ WEAPON_STUN // BFP - Stun status (not hit stun) when using ki explosion wave
} weaponstate_t;
+// BFP - Attack types
+#define ATK_MISSILE 0
+#define ATK_RDMISSILE 1
+#define ATK_BEAM 2
+#define ATK_SBEAM 3
+#define ATK_HITSCAN 4
+#define ATK_FORCEFIELD 5
+
+// BFP - If you want to keep demo networking, change the way to use the PMF_ flags
// pmove->pm_flags
#define PMF_DUCKED 1
#define PMF_JUMP_HELD 2
-#define PMF_FLYING 4 // BFP - Flight
-#define PMF_BACKWARDS_JUMP 8 // go into backwards land
-#define PMF_BACKWARDS_RUN 16 // coast down to backwards run
-#define PMF_TIME_LAND 32 // pm_time is time before rejump
+#define PMF_BLOCK 4 // BFP - Block
+// BFP - PMF_BACKWARDS_JUMP is unused
+//#define PMF_BACKWARDS_JUMP 8 // go into backwards land
+// BFP - PMF_BACKWARDS_RUN is renamed // coast down to backwards run
+#define PMF_MELEE 16 // BFP - Melee
+// BFP - PMF_TIME_LAND is unused
+//#define PMF_TIME_LAND 32 // pm_time is time before rejump
#define PMF_TIME_KNOCKBACK 64 // pm_time is an air-accelerate only time
-#define PMF_KI_BOOST 128 // BFP - Using Ki
-#define PMF_TIME_WATERJUMP 256 // pm_time is waterjump
+#define PMF_KI_CHARGE 128 // BFP - Ki charge
+// BFP - PMF_TIME_WATERJUMP is renamed // pm_time is waterjump
+#define PMF_AIR_GRAVITY 256 // BFP - Air gravity check
#define PMF_RESPAWNED 512 // clear after attack and jump buttons come up
#define PMF_USE_ITEM_HELD 1024
-// BFP - no hook
-// #define PMF_GRAPPLE_PULL 2048 // pull towards grapple location
+// BFP - PMF_GRAPPLE_PULL is unused
+//#define PMF_GRAPPLE_PULL 2048 // pull towards grapple location
#define PMF_FOLLOW 4096 // spectate following another player
-// BFP - PMF_SCOREBOARD is unused
-// #define PMF_SCOREBOARD 8192 // spectate as a scoreboard
-// BFP - TODO: Reuse the following flag (used on Team Arena), change name if it'll be used
-#define PMF_INVULEXPAND 16384 // invulnerability sphere set to full size
+// BFP - PMF_SCOREBOARD is renamed // spectate as a scoreboard
+#define PMF_ULTIMATE_TIER 8192 // BFP - Ultimate tier status
+#define PMF_FLIGHT_LATCH 16384 // BFP - Flight latch toggling
+// #define PMF_UNUSED_FLAG 32768 // BFP - Some unused pm_flag
// BFP - Last pm_flag after 32768. That's the limit of pm_flags, it can't reach more
// #define PMF_SOMEFLAG 65536 // some pm_flag
-#define PMF_ALL_TIMES (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK)
+// BFP - That combination of PMF_TIME_* flags is unused
+#define PMF_ALL_TIMES PMF_TIME_KNOCKBACK //(PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK)
#define MAXTOUCH 32
typedef struct {
@@ -173,9 +195,30 @@ typedef struct {
int tracemask; // collide against these types of surfaces
int debugLevel; // if set, diagnostic output will be printed
qboolean noFootsteps; // if the game is setup for no footsteps by the server
- qboolean gauntletHit; // true if a gauntlet attack would actually hit something
- qboolean isFlying; // BFP - Flight
- //qboolean hitStunReceived; // BFP - TODO: Hit stun
+ qboolean meleeHit; // BFP - before: gauntletHit - true if a melee attack would actually hit something
+
+ qboolean noFlight; // BFP - No flight
+ qboolean meleeOnly; // BFP - Melee only
+
+ // BFP - TODO: bfp_weapon.cfg
+ int weaponTime; // BFP - Weapon time
+ int randomWeaponTime; // BFP - Milliseconds of random extra fire delay
+ qboolean kiCostAsPct; // BFP - Enables ki cost percentage
+ float kiPct; // BFP - Ki cost percentage (0.0 - 1.0)
+ int kiCost; // BFP - Ki cost
+
+ qboolean chargeAttack; // BFP - Charge attack
+ qboolean chargeAutoFire; // BFP - Charge autofire
+ int minCharge; // BFP - Minimum charge points
+ int maxCharge; // BFP - Maximum charge points
+
+ qboolean loopingAnim; // BFP - Looping ki attack animation
+ qboolean noAttackAnim; // BFP - No prepare ki attack animation
+
+ qboolean railTrail; // BFP - Rail trail weapon check
+ int attackType; // BFP - Attack type check
+
+ int movementPenalty; // BFP - Seconds of movement penalty
int framecount;
@@ -211,15 +254,25 @@ void Pmove (pmove_t *pmove);
// NOTE: may not have more than 16
typedef enum {
STAT_HEALTH,
- STAT_KI, // BFP - KI amount
STAT_HOLDABLE_ITEM,
STAT_WEAPONS, // 16 bit fields
STAT_ARMOR,
- STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?)
+ // BFP - Got rid of STAT_DEAD_YAW, now uses ps->damageYaw and ps->damagePitch
+ STAT_UNUSED_INDEX4, // unused stat index (don't remove if you want to keep demo networking!)
STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?)
- STAT_MAX_HEALTH // health / armor limit, changable by handicap
-} statIndex_t;
+ STAT_UNUSED_INDEX6, // BFP - Powerlevel
+ STAT_UNUSED_INDEX7, // BFP - Flight jump anim transition seconds, maximum is 21 sec and stops changing to 0, even when stop flying also reproduces this stat index like starting to fly (looks weird)
+ STAT_KI, // BFP - Ki
+ STAT_MAX_KI, // BFP - Maximum ki
+ STAT_UNUSED_INDEX10, // BFP - Melee attack time
+ STAT_MAX_HEALTH, // health / armor limit, changable by handicap
+ STAT_UNUSED_INDEX12, // unused stat index (don't remove if you want to keep demo networking!)
+ STAT_UNUSED_INDEX13, // BFP - Beam firing weapon state
+ STAT_UNUSED_INDEX14, // BFP - Force field weapon state
+ //STAT_UNUSED_INDEX15 // BFP - Fly tilt angles (left: moves to -80, right: moves to 80), rename during the config implementation
+ STAT_HITSTUN_TIME // BFP - Hit stun time
+} statIndex_t;
// player_state->persistant[] indexes
// these fields are the only part of player_state that isn't
@@ -236,61 +289,91 @@ typedef enum {
PERS_ATTACKEE_ARMOR, // health/armor of last person we attacked
PERS_KILLED, // count of the number of times you died
// player awards tracking
- PERS_IMPRESSIVE_COUNT, // two railgun hits in a row
PERS_EXCELLENT_COUNT, // two successive kills in a short amount of time
- PERS_DEFEND_COUNT, // defend awards
- PERS_ASSIST_COUNT, // assist awards
- PERS_GAUNTLET_FRAG_COUNT, // kills with the guantlet
- PERS_CAPTURES // captures
-} persEnum_t;
+ // BFP - NOTE: These indexes are being kept for original BFP networking
+ // BFP - No impressive, gauntlet, defend, assist and capture (PERS_CAPTURES is renamed) counters are used. Renamed as PERS_UNUSED_INDEX00
+ PERS_UNUSED_INDEX10, //PERS_IMPRESSIVE_COUNT, // two railgun hits in a row
+ PERS_UNUSED_INDEX11, //PERS_DEFEND_COUNT, // defend awards
+ PERS_UNUSED_INDEX12, //PERS_ASSIST_COUNT, // assist awards
+ // BFP - This index is used in original BFP networking, but remains unknown
+ PERS_UNUSED_INDEX13, //PERS_GAUNTLET_FRAG_COUNT, // kills with the guantlet
+
+ PERS_POWERLEVEL, // BFP - Powerlevel (before Q3: //PERS_CAPTURES, // captures)
+
+ PERS_UNUSED_INDEX15 // BFP - ??? (Original BFP networking says it appears when spawning at the first time of all in-game)
+} persEnum_t;
+// BFP - If you want to keep demo networking, change the way to use the EF_ flags
// entityState_t->eFlags
#define EF_DEAD 0x00000001 // don't draw a foe marker over players with EF_DEAD
#define EF_AURA 0x00000002 // BFP - Aura, used to display players' aura
#define EF_TELEPORT_BIT 0x00000004 // toggled every time the origin abruptly changes
#define EF_AWARD_EXCELLENT 0x00000008 // draw an excellent sprite
#define EF_PLAYER_EVENT 0x00000010
-#define EF_BOUNCE 0x00000010 // for missiles
-#define EF_BOUNCE_HALF 0x00000020 // for missiles
-#define EF_AWARD_GAUNTLET 0x00000040 // draw a gauntlet sprite
+// BFP - Unused EF_BOUNCE and EF_BOUNCE_HALF
+//#define EF_BOUNCE 0x00000010 // for missiles
+// BFP - This eFlag hack is used for client/player only:
+#define EF_READY_KI_ATTACK 0x00000010 // BFP - Ready ki attack
+//#define EF_BOUNCE_HALF 0x00000020 // for missiles
+// BFP - EF_AWARD_GAUNTLET flag is renamed // draw a gauntlet sprite
+#define EF_FLIGHT 0x00000040 // BFP - Used for flying status
#define EF_NODRAW 0x00000080 // may have an event, but no model (unspawned items)
#define EF_FIRING 0x00000100 // for lightning gun
-#define EF_KAMIKAZE 0x00000200
+#define EF_KI_BOOST 0x00000200 // BFP - Used for ki boost status
#define EF_MOVER_STOP 0x00000400 // will push otherwise
-#define EF_AWARD_CAP 0x00000800 // draw the capture sprite
+// BFP - This eFlag hack is used for client/player only:
+#define EF_AURA_TIER_UP 0x00000400 // BFP - Aura tier up effect when transforms or passes to the next tier
+// BFP - EF_AWARD_CAP is unused
+//#define EF_AWARD_CAP 0x00000800 // draw the capture sprite
#define EF_TALK 0x00001000 // draw a talk balloon
#define EF_CONNECTION 0x00002000 // draw a connection trouble sprite
#define EF_VOTED 0x00004000 // already cast a vote
-#define EF_AWARD_IMPRESSIVE 0x00008000 // draw an impressive sprite
-#define EF_AWARD_DEFEND 0x00010000 // draw a defend sprite
-#define EF_AWARD_ASSIST 0x00020000 // draw a assist sprite
-#define EF_AWARD_DENIED 0x00040000 // denied
+// BFP - EF_AWARD_IMPRESSIVE flag is renamed // draw an impressive sprite
+#define EF_MONSTER 0x00008000 // BFP - Player marked as monster on monster gametype (g_gametype 4)
+// BFP - No EF_AWARD_DEFEND and EF_AWARD_ASSIST flags
+// #define EF_AWARD_DEFEND 0x00010000 // draw a defend sprite
+// #define EF_AWARD_ASSIST 0x00020000 // draw a assist sprite
+// BFP - Unused EF flag
+// #define EF_AWARD_DENIED 0x00040000 // denied
#define EF_TEAMVOTED 0x00080000 // already cast a team vote
-// BFP - NOTE: Should use PW_* instead using pm_flags?
-// That goes for PW_HASTE and PW_FLIGHT, instead using PMF_KI_BOOST and PMF_FLYING
+// BFP - We can use generic1 integer 8-bit fields, but if we use as integer, only can use until 128 ~ 256
+// used for ki charge points
+// entityState_t->generic1
+// #define GENF_FLAG_1 1
+// #define GENF_FLAG_2 2
+// #define GENF_FLAG_3 4
+// #define GENF_FLAG_4 8
+// #define GENF_FLAG_5 16
+// #define GENF_FLAG_6 32
+// #define GENF_FLAG_7 64
+// #define GENF_FLAG_8 128
// NOTE: may not have more than 16
typedef enum {
+ // BFP - NOTE: Modified according to keep original BFP networking
+ // PW_REGEN doesn't appear and has been replaced by other feature
+ // BFP - that's where PW_HASTE, PW_BATTLESUIT and PW_INVIS are marked as PW_NONE after picking up
PW_NONE,
PW_QUAD,
- PW_BATTLESUIT,
- PW_HASTE,
- PW_INVIS,
- PW_REGEN,
- PW_FLIGHT,
-
- PW_REDFLAG,
- PW_BLUEFLAG,
- PW_NEUTRALFLAG,
-
- PW_SCOUT,
- PW_GUARD,
- PW_DOUBLER,
- PW_AMMOREGEN,
- PW_INVULNERABILITY,
+
+ // BFP - Red and blue flag are replaced from PW_BATTLESUIT and PW_HASTE according to original BFP networking
+ PW_REDFLAG, //PW_BATTLESUIT,
+ PW_BLUEFLAG, //PW_HASTE,
+
+ PW_UNUSED_INDEX4, // BFP - Unused powerup index //PW_INVIS
+ PW_UNUSED_INDEX5, // BFP - That would be PW_FLIGHT, used for flying
+ PW_UNUSED_INDEX6, // BFP - Used for ki recharge
+ PW_UNUSED_INDEX7, // BFP - Used for ki use/boost
+ PW_UNUSED_INDEX8, // BFP - Used for blocking seconds (defend from melee, beams, explosions and impacts, and reflect ki attack projectiles)
+ PW_UNUSED_INDEX9, // BFP - Used for melee toggle
+ PW_UNUSED_INDEX10, // BFP - Used for hit stun seconds
+ PW_UNUSED_INDEX11, // BFP - Used for ki attack charge points
+ PW_UNUSED_INDEX12, // BFP - Used for enable/disable monster in monster gamemode (g_gametype 4)
+ PW_UNUSED_INDEX13, // BFP - Used for beam firing state
+ PW_UNUSED_INDEX14, // BFP - Used for jump (looks strange...)
PW_NUM_POWERUPS
@@ -301,27 +384,31 @@ typedef enum {
HI_TELEPORTER,
HI_MEDKIT,
- HI_KAMIKAZE,
- HI_PORTAL,
- HI_INVULNERABILITY,
HI_NUM_HOLDABLE
} holdable_t;
+// BFP - NOTE: According to keep original BFP networking, these are modified for features
typedef enum {
- WP_NONE,
-
- WP_GAUNTLET,
- WP_MACHINEGUN,
- WP_SHOTGUN,
- WP_GRENADE_LAUNCHER,
- WP_ROCKET_LAUNCHER,
- WP_LIGHTNING,
- WP_RAILGUN,
- WP_PLASMAGUN,
- WP_BFG,
- WP_GRAPPLING_HOOK,
+ WP_NONE, // BFP - TODO: First attack selected, rename during the config implementation
+ WP_GAUNTLET, // BFP - TODO: Second attack selected, rename during the config implementation
+ WP_MACHINEGUN, // BFP - TODO: Third attack selected, rename during the config implementation
+ WP_SHOTGUN, // BFP - TODO: Fourth attack selected, rename during the config implementation
+ WP_GRENADE_LAUNCHER, // BFP - TODO: Fifth (or last) attack selected and also for a timer of 2000 msec when being attacked/damaged, rename during the config implementation
+
+ WP_ROCKET_LAUNCHER, // BFP - Original demo networking: Ki recharge delay time (for g_chargeDelay), rename during the config implementation
+ WP_LIGHTNING, // BFP - Original demo networking: Hit stun delay after receiving hit stun, rename during the config implementation
+ WP_RAILGUN, // BFP - Original demo networking: Block delay, rename during the config implementation
+ WP_PLASMAGUN, // BFP - Original demo networking: Ki use/boost toggle, rename during the config implementation
+ WP_BFG, // BFP - Original demo networking: Flight toggle key control, rename during the config implementation
+ WP_GRAPPLING_HOOK, // BFP - Original demo networking: Blind seconds, rename during the config implementation
+
+// WP_UNUSED_INDEX11, // BFP - Original demo networking: Rapid attacks like ki storm (alternates -1 and 1), rename during the config implementation
+// WP_UNUSED_INDEX12, // BFP - Original demo networking: Unknown or unused index
+// WP_UNUSED_INDEX13, // BFP - Original demo networking: Toggle to use Short-Range Teleport - Zanzoken, rename during the config implementation
+// WP_UNUSED_INDEX14, // BFP - Original demo networking: Directional left/right keys to move left/right while pressing, adds time msec, looks like a timer to handle for Zanzoken, rename during the config implementation
+// WP_UNUSED_INDEX15, // BFP - Original demo networking: Enables/disables beam struggle , rename during the config implementation
WP_NUM_WEAPONS
} weapon_t;
@@ -348,98 +435,10 @@ typedef enum {
#define EVENT_VALID_MSEC 300
typedef enum {
- EV_NONE,
-
- EV_FOOTSTEP,
- EV_FOOTSTEP_METAL,
- EV_FOOTSPLASH,
- EV_FOOTWADE,
- EV_SWIM,
-
- EV_STEP_4,
- EV_STEP_8,
- EV_STEP_12,
- EV_STEP_16,
-
- EV_FALL_SHORT,
- EV_FALL_MEDIUM,
- EV_FALL_FAR,
-
- EV_JUMP_PAD, // boing sound at origin, jump sound on player
-
- EV_JUMP,
- EV_WATER_TOUCH, // foot touches
- EV_WATER_LEAVE, // foot leaves
- EV_WATER_UNDER, // head touches
- EV_WATER_CLEAR, // head leaves
-
- EV_ITEM_PICKUP, // normal item pickups are predictable
- EV_GLOBAL_ITEM_PICKUP, // powerup / team sounds are broadcast to everyone
-
- EV_NOAMMO,
- EV_CHANGE_WEAPON,
- EV_FIRE_WEAPON,
-
- EV_USE_ITEM0,
- EV_USE_ITEM1,
- EV_USE_ITEM2,
- EV_USE_ITEM3,
- EV_USE_ITEM4,
- EV_USE_ITEM5,
- EV_USE_ITEM6,
- EV_USE_ITEM7,
- EV_USE_ITEM8,
- EV_USE_ITEM9,
- EV_USE_ITEM10,
- EV_USE_ITEM11,
- EV_USE_ITEM12,
- EV_USE_ITEM13,
- EV_USE_ITEM14,
- EV_USE_ITEM15,
-
- EV_ITEM_RESPAWN,
- EV_ITEM_POP,
- EV_PLAYER_TELEPORT_IN,
- EV_PLAYER_TELEPORT_OUT,
-
- EV_GRENADE_BOUNCE, // eventParm will be the soundindex
-
- EV_GENERAL_SOUND,
- EV_GLOBAL_SOUND, // no attenuation
- EV_GLOBAL_TEAM_SOUND,
-
- EV_BULLET_HIT_FLESH,
- EV_BULLET_HIT_WALL,
-
- EV_MISSILE_HIT,
- EV_MISSILE_MISS,
- EV_MISSILE_MISS_METAL,
- EV_RAILTRAIL,
- EV_SHOTGUN,
- EV_BULLET, // otherEntity is the shooter
-
- EV_PAIN,
- EV_DEATH1,
- EV_DEATH2,
- EV_DEATH3,
- EV_OBITUARY,
-
- EV_POWERUP_QUAD,
- EV_POWERUP_BATTLESUIT,
- EV_POWERUP_REGEN,
-
- EV_GIB_PLAYER, // gib a previously living player
- EV_SCOREPLUM, // score plum
-
- EV_DEBUG_LINE,
- EV_STOPLOOPINGSOUND,
- EV_TAUNT,
- EV_TAUNT_YES,
- EV_TAUNT_NO,
- EV_TAUNT_FOLLOWME,
- EV_TAUNT_GETFLAG,
- EV_TAUNT_GUARDBASE,
- EV_TAUNT_PATROL
+ // BFP - Events are declared in bg_events.h file
+#define EVENT_ENUMS
+ #include "bg_events.h"
+#undef EVENT_ENUMS
} entity_event_t;
@@ -451,14 +450,11 @@ typedef enum {
GTS_BLUE_RETURN,
GTS_RED_TAKEN,
GTS_BLUE_TAKEN,
- GTS_REDOBELISK_ATTACKED,
- GTS_BLUEOBELISK_ATTACKED,
GTS_REDTEAM_SCORED,
GTS_BLUETEAM_SCORED,
GTS_REDTEAM_TOOK_LEAD,
GTS_BLUETEAM_TOOK_LEAD,
- GTS_TEAMS_ARE_TIED,
- GTS_KAMIKAZE
+ GTS_TEAMS_ARE_TIED
} global_team_sound_t;
// animations
@@ -540,8 +536,7 @@ typedef enum {
TORSO_ATTACK4_STRIKE, // BFP
// BFP - The following attackset animations are just reminders, these aren't used as variables
-
- /*
+/*
// BFP - (point finger right hand)
TORSO_ATTACK5_PREPARE, // BFP
TORSO_ATTACK5_STRIKE, // BFP
@@ -589,7 +584,7 @@ typedef enum {
// BFP - (ken and ryu fireball)
TORSO_ATTACK16_PREPARE, // BFP
TORSO_ATTACK16_STRIKE, // BFP
- */
+*/
// BFP - The following animations are useless,
// possibly can be removed only if the game works as should
@@ -600,7 +595,7 @@ typedef enum {
TORSO_AFFIRMATIVE,
TORSO_NEGATIVE,
- MAX_ANIMATIONS, // BFP - important variable, don't remove!
+ MAX_ANIMATIONS, // BFP - important index, don't remove!
// LEGS_BACKCR, // BFP - No longer used
// LEGS_BACKWALK, // BFP - No longer used
@@ -608,7 +603,7 @@ typedef enum {
FLAG_STAND,
FLAG_STAND2RUN,
- MAX_TOTALANIMATIONS // BFP - important variable, don't remove!
+ MAX_TOTALANIMATIONS // BFP - important index, don't remove!
} animNumber_t;
typedef struct animation_s {
@@ -656,30 +651,10 @@ typedef enum {
// means of death
typedef enum {
- MOD_UNKNOWN,
- MOD_SHOTGUN,
- MOD_GAUNTLET,
- MOD_MACHINEGUN,
- MOD_GRENADE,
- MOD_GRENADE_SPLASH,
- MOD_ROCKET,
- MOD_ROCKET_SPLASH,
- MOD_PLASMA,
- MOD_PLASMA_SPLASH,
- MOD_RAILGUN,
- MOD_LIGHTNING,
- MOD_BFG,
- MOD_BFG_SPLASH,
- MOD_WATER,
- MOD_SLIME,
- MOD_LAVA,
- MOD_CRUSH,
- MOD_TELEFRAG,
- MOD_FALLING,
- MOD_SUICIDE,
- MOD_TARGET_LASER,
- MOD_TRIGGER_HURT,
- MOD_GRAPPLE
+ // BFP - Means of death are declared in bg_meansofdeath.h file
+#define MOD_ENUMS
+ #include "bg_meansofdeath.h"
+#undef MOD_ENUMS
} meansOfDeath_t;
@@ -708,6 +683,7 @@ typedef struct gitem_s {
char *world_model[MAX_ITEM_MODELS];
char *icon;
+ // BFP - TODO: Use as attackName for skin config
char *pickup_name; // for printing on pickup
int quantity; // for ammo how much, or duration of powerup
@@ -733,7 +709,7 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play
// g_dmflags->integer flags
-// #define DF_NO_FALLING 8 // BFP - Disabled, there's no fall damage on BFP
+#define DF_NO_FALLING 8
#define DF_FIXED_FOV 16
#define DF_NO_FOOTSTEPS 32
@@ -776,7 +752,7 @@ typedef enum {
void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result );
void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result );
-void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps );
+void BG_AddPredictableEventToPlayerstate( entity_event_t newEvent, int eventParm, playerState_t *ps, int entityNum );
void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad );
@@ -794,25 +770,8 @@ qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTim
#define MAX_BOTS_TEXT 8192
-// Kamikaze
-
-// 1st shockwave times
-#define KAMI_SHOCKWAVE_STARTTIME 0
-#define KAMI_SHOCKWAVEFADE_STARTTIME 1500
-#define KAMI_SHOCKWAVE_ENDTIME 2000
-// explosion/implosion times
-#define KAMI_EXPLODE_STARTTIME 250
-#define KAMI_IMPLODE_STARTTIME 2000
-#define KAMI_IMPLODE_ENDTIME 2250
-// 2nd shockwave times
-#define KAMI_SHOCKWAVE2_STARTTIME 2000
-#define KAMI_SHOCKWAVE2FADE_STARTTIME 2500
-#define KAMI_SHOCKWAVE2_ENDTIME 3000
-// radius of the models without scaling
-#define KAMI_SHOCKWAVEMODEL_RADIUS 88
-#define KAMI_BOOMSPHEREMODEL_RADIUS 72
-// maximum radius of the models during the effect
-#define KAMI_SHOCKWAVE_MAXRADIUS 1320
-#define KAMI_BOOMSPHERE_MAXRADIUS 720
-#define KAMI_SHOCKWAVE2_MAXRADIUS 704
-
+// calculated by modulus.c for appropriate dividers:
+#define TMOD_004 4272943
+#define TMOD_075 2292106
+#define TMOD_1000 5730265
+#define TMOD_2000 5730265
diff --git a/source/game/bg_slidemove.c b/source/game/bg_slidemove.c
index 6c957a5..3f4151d 100644
--- a/source/game/bg_slidemove.c
+++ b/source/game/bg_slidemove.c
@@ -232,7 +232,10 @@ PM_StepSlideMove
*/
void PM_StepSlideMove( qboolean gravity ) {
vec3_t start_o, start_v;
+#define SLIDE_DOWN_VECTOR 0
+#if SLIDE_DOWN_VECTOR
vec3_t down_o, down_v;
+#endif
trace_t trace;
// float down_dist, up_dist;
// vec3_t delta, delta2;
@@ -256,8 +259,10 @@ void PM_StepSlideMove( qboolean gravity ) {
return;
}
+#if SLIDE_DOWN_VECTOR
VectorCopy (pm->ps->origin, down_o);
VectorCopy (pm->ps->velocity, down_v);
+#endif
VectorCopy (start_o, up);
up[2] += STEPSIZE;
@@ -289,7 +294,7 @@ void PM_StepSlideMove( qboolean gravity ) {
PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
}
-#if 0
+#if SLIDE_DOWN_VECTOR
// if the down trace can trace back to the original position directly, don't step
pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask);
if ( trace.fraction == 1.0 ) {
diff --git a/source/game/g_active.c b/source/game/g_active.c
index a5faf7a..47218f8 100644
--- a/source/game/g_active.c
+++ b/source/game/g_active.c
@@ -97,18 +97,19 @@ Check for lava / slime contents and drowning
=============
*/
void P_WorldEffects( gentity_t *ent ) {
- qboolean envirosuit;
int waterlevel;
+ // BFP - No battlesuit powerup
+ //qboolean envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time;
+ waterlevel = ent->waterlevel;
+
+ // BFP - No drowning
+#if 0
if ( ent->client->noclip ) {
ent->client->airOutTime = level.time + 12000; // don't need air
return;
}
- waterlevel = ent->waterlevel;
-
- envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time;
-
//
// check for drowning
//
@@ -148,6 +149,7 @@ void P_WorldEffects( gentity_t *ent ) {
ent->client->airOutTime = level.time + 12000;
ent->damage = 2;
}
+#endif
//
// check for sizzle damage (move to pmove?)
@@ -157,9 +159,13 @@ void P_WorldEffects( gentity_t *ent ) {
if (ent->health > 0
&& ent->pain_debounce_time <= level.time ) {
+ // BFP - No battlesuit powerup
+#if 0
if ( envirosuit ) {
G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 );
- } else {
+ } else
+#endif
+ {
if (ent->watertype & CONTENTS_LAVA) {
G_Damage (ent, NULL, NULL, NULL, NULL,
30*waterlevel, 0, MOD_LAVA);
@@ -242,7 +248,8 @@ void G_TouchTriggers( gentity_t *ent ) {
gentity_t *hit;
trace_t trace;
vec3_t mins, maxs;
- static vec3_t range = { 40, 40, 52 };
+ // BFP - Disabled
+ //static vec3_t range = { 40, 40, 52 };
if ( !ent->client ) {
return;
@@ -253,14 +260,16 @@ void G_TouchTriggers( gentity_t *ent ) {
return;
}
- VectorSubtract( ent->client->ps.origin, range, mins );
- VectorAdd( ent->client->ps.origin, range, maxs );
-
- num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
+ // BFP - Disabled, to touch correctly without a defined range
+ // and num declaration moved down mins and maxs.
+ // Player monster cannot touch and that causes troubles by stucking from there
+ //VectorSubtract( ent->client->ps.origin, range, mins );
+ //VectorAdd( ent->client->ps.origin, range, maxs );
// can't use ent->absmin, because that has a one unit pad
VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
+ num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
for ( i=0 ; is.eType == ET_ITEM ) {
+ // BFP - Most players and player monster can pick up items with its bounding box
+ if ( mins[0] > hit->r.absmax[0] || maxs[0] < hit->r.absmin[0]
+ || mins[1] > hit->r.absmax[1] || maxs[1] < hit->r.absmin[1]
+ || mins[2] > hit->r.absmax[2] || maxs[2] < hit->r.absmin[2] ) {
+ continue;
+ }
+ // BFP - Disabled to make bounding box detection work
+#if 0
if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
continue;
}
+#endif
} else {
if ( !trap_EntityContact( mins, maxs, hit ) ) {
continue;
@@ -396,16 +414,60 @@ Actions that happen once a second
*/
void ClientTimerActions( gentity_t *ent, int msec ) {
gclient_t *client;
+ // BFP - Flight cost total variable
+ float flightCostTotal = g_flightCost.value + g_flightCostPct.value * ( ent->client->ps.stats[STAT_MAX_KI] * 0.01 );
+ // BFP - Random factor for ki charge last digits
+ float rndKiCharge = 0.8f + crandom() * 0.2f;
client = ent->client;
client->timeResidual += msec;
+ // BFP - Ki boost consumption
+ if ( ( ( client->pers.cmd.buttons & BUTTON_KI_USE ) || ( client->ps.eFlags & EF_KI_BOOST ) )
+ && client->ps.stats[STAT_KI] > 0
+ && client->ps.stats[STAT_HITSTUN_TIME] <= 0
+ && !( client->ps.pm_flags & PMF_BLOCK )
+ && client->ps.weaponstate != WEAPON_STUN ) {
+ // BFP - NOTE: On original BFP, this is handled into another way, so, the formula remains unknown, it tried the best
+ float boostCostTotal = ( g_boostCost.value * 0.001 ) + ( g_boostCostPct.value * 0.1 ) * client->ps.stats[STAT_MAX_KI] * 0.0001;
+ // use msec to adjust the consumption
+ client->kiResidual += boostCostTotal * msec;
+ if ( client->kiResidual >= 1.0f ) {
+ int drop = (int)client->kiResidual;
+ client->ps.stats[STAT_KI] -= drop;
+ client->kiResidual -= drop;
+ }
+ }
+
+ // BFP - Charge ki
+ if ( ( client->ps.pm_flags & PMF_KI_CHARGE ) && ( client->ps.eFlags & EF_AURA )
+ && client->ps.stats[STAT_HITSTUN_TIME] <= 0
+ && client->ps.stats[STAT_KI] < client->ps.stats[STAT_MAX_KI] ) {
+ float kiChargeTotal = ( g_kiCharge.value * 0.01 ) + g_kiChargePct.value * ( client->ps.stats[STAT_MAX_KI] * 0.0001 );
+ client->ps.stats[STAT_KI] += kiChargeTotal * rndKiCharge;
+ }
+
+ // BFP - Block ki consume
+ if ( ( client->ps.pm_flags & PMF_BLOCK )
+ && client->ps.stats[STAT_KI] > 0
+ && client->blockKnockbackTime <= 0
+ && random() < 0.75 ) { // a weird random thingy (¬_¬') tried to get the similar result
+ // BFP - NOTE: On original BFP, this is handled into another way, so, the formula remains unknown, it tried the best
+ float blockCostTotal = ( g_blockCost.value * 0.01 ) + ( g_blockCostPct.value * 0.01 ) * ( client->ps.stats[STAT_MAX_KI] * 0.0001 );
+ if ( blockCostTotal < 1 ) {
+ blockCostTotal = 1;
+ }
+ client->ps.stats[STAT_KI] -= blockCostTotal;
+ }
+
while ( client->timeResidual >= 1000 ) {
client->timeResidual -= 1000;
+ // BFP - No regen powerup
+#if 0
// regenerate
if ( client->ps.powerups[PW_REGEN] ) {
- if ( ent->health < client->ps.stats[STAT_MAX_HEALTH]) {
+ if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] ) {
ent->health += 15;
if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1 ) {
ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1;
@@ -418,33 +480,31 @@ void ClientTimerActions( gentity_t *ent, int msec ) {
}
G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
}
- } else {
+ } else
+#endif
+ {
// count down health when over max
if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] ) {
ent->health--;
}
}
- // BFP - Ki up/down when flying/ki use
- if ( client->ps.pm_flags & PMF_FLYING ) {
-
- // BFP - TODO: Add cvar for flight cost
-
- if ( client->ps.stats[STAT_KI] > 0 ) {
- client->ps.stats[STAT_KI]--;
-
- if ( client->pers.cmd.buttons & BUTTON_KI_USE )
- client->ps.stats[STAT_KI]--;
+ // BFP - Decrease ki when flying
+ if ( ( client->ps.eFlags & EF_FLIGHT )
+ && client->ps.stats[STAT_KI] > 0
+ && !( client->ps.pm_flags & PMF_KI_CHARGE ) ) { // don't decrease when charging
+ if ( g_flightCostPct.value > 0 && client->ps.persistant[PERS_POWERLEVEL] < 1000 ) { // reduce a bit if the percentage cost is more than 0 and has less powerlevel
+ --flightCostTotal;
}
- } else {
- client->ps.stats[STAT_KI]++;
+ client->ps.stats[STAT_KI] -= flightCostTotal;
}
- // BFP - if ki drops to 0, disable flight
- if ( client->ps.stats[STAT_KI] <= 0 ) {
- client->ps.stats[STAT_KI] = 0;
- client->ps.pm_flags &= ~PMF_FLYING;
- // Com_Printf( "ki amount: %d\n", client->ps.stats[STAT_KI] );
+ // BFP - Regenerate ki
+ if ( !( client->pers.cmd.buttons & BUTTON_KI_USE ) && !( client->ps.eFlags & EF_KI_BOOST )
+ && !( ( client->ps.pm_flags & PMF_KI_CHARGE ) && ( client->ps.eFlags & EF_AURA ) ) // don't increase when charging
+ && !( ( client->ps.pm_flags & PMF_KI_CHARGE ) && ( client->ps.eFlags & EF_AURA ) && client->ps.stats[STAT_HITSTUN_TIME] > 0 ) // don't increase when trying to charge when stunned
+ && !( ( client->pers.cmd.buttons & BUTTON_ATTACK ) && client->ps.stats[STAT_HITSTUN_TIME] > 0 ) ) { // don't increase when trying to attack when stunned
+ client->ps.stats[STAT_KI] += g_kiRegen.value + ( g_kiRegenPct.value * client->ps.stats[STAT_MAX_KI] * 0.01 );
}
// count down armor when over max
@@ -452,6 +512,31 @@ void ClientTimerActions( gentity_t *ent, int msec ) {
client->ps.stats[STAT_ARMOR]--;
}
}
+
+ // BFP - Set maximum ki
+ if ( client->ps.stats[STAT_KI] > client->ps.stats[STAT_MAX_KI] ) {
+ client->ps.stats[STAT_KI] = client->ps.stats[STAT_MAX_KI];
+ }
+
+ // BFP - If ki drops to 0, disable flight
+ if ( client->ps.stats[STAT_KI] <= 0 ) {
+ client->ps.stats[STAT_KI] = 0;
+ client->ps.eFlags &= ~EF_FLIGHT;
+ }
+
+ // BFP - When the player doesn't have more ki, gets a hit stun
+ if ( ( client->ps.stats[STAT_KI] <= 0 )
+ || ( ( client->ps.eFlags & EF_FLIGHT ) && client->ps.stats[STAT_KI] < flightCostTotal && client->ps.stats[STAT_HITSTUN_TIME] <= 0 )
+ || ( ( client->pers.cmd.buttons & BUTTON_ATTACK ) && client->ps.stats[STAT_HITSTUN_TIME] > 0 ) ) {
+ if ( ( client->ps.eFlags & EF_FLIGHT ) && client->ps.stats[STAT_KI] < flightCostTotal && client->ps.stats[STAT_HITSTUN_TIME] <= 0 ) {
+ client->ps.stats[STAT_KI] /= flightCostTotal;
+ }
+ if ( client->ps.stats[STAT_KI] <= 0 ) {
+ client->ps.stats[STAT_HITSTUN_TIME] = 100; // handle time
+ } else {
+ client->ps.stats[STAT_HITSTUN_TIME] = 1000;
+ }
+ }
}
/*
@@ -472,6 +557,8 @@ void ClientIntermissionThink( gclient_t *client ) {
// this used to be an ^1 but once a player says ready, it should stick
client->readyToExit = 1;
}
+
+ client->ps.commandTime = client->pers.cmd.serverTime;
}
@@ -488,7 +575,6 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) {
int event;
gclient_t *client;
int damage;
- vec3_t dir;
vec3_t origin, angles;
// qboolean fired;
gitem_t *item;
@@ -506,27 +592,34 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) {
case EV_FALL_MEDIUM:
case EV_FALL_FAR:
// BFP - There's no crash land damage when the players fell in the ground
- /*
if ( ent->s.eType != ET_PLAYER ) {
break; // not in the player model
}
if ( g_dmflags.integer & DF_NO_FALLING ) {
break;
}
- if ( event == EV_FALL_FAR ) {
- damage = 10;
- } else {
- damage = 5;
+ // BFP - When the player is falling with stunned status
+ // if powerlevel is very low, weaker at falling (player has less max health)
+ // but when the powerlevel is getting higher, the less damage it will have (player has more max health)
+ if ( client->ps.stats[STAT_HITSTUN_TIME] > 0 ) {
+ damage = 5; // medium fall
+ if ( event == EV_FALL_FAR ) {
+ damage = 10;
+ }
+ ent->pain_debounce_time = level.time + 200; // no normal pain sound
+ G_Damage ( ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING );
}
- VectorSet (dir, 0, 0, 1);
- ent->pain_debounce_time = level.time + 200; // no normal pain sound
- G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING);
- */
break;
case EV_FIRE_WEAPON:
+ {
+ // BFP - Multishot test
+ if ( client->ps.weapon == WP_GRENADE_LAUNCHER ) {
+ ent->multishot = 3;
+ }
FireWeapon( ent );
break;
+ }
case EV_USE_ITEM1: // teleporter
// drop flags in CTF
@@ -539,9 +632,6 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) {
} else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) {
item = BG_FindItemForPowerup( PW_BLUEFLAG );
j = PW_BLUEFLAG;
- } else if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) {
- item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
- j = PW_NEUTRALFLAG;
}
if ( item ) {
@@ -560,7 +650,7 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) {
break;
case EV_USE_ITEM2: // medkit
- ent->health = ent->client->ps.stats[STAT_MAX_HEALTH] + 25;
+ ent->health = client->ps.stats[STAT_MAX_HEALTH]; // BFP - Before Q3: + 25
break;
@@ -610,20 +700,252 @@ void SendPendingPredictableEvents( playerState_t *ps ) {
/*
=================
-FlyingThink
+BlockHandling
=================
*/
-void FlyingThink( gentity_t *ent, usercmd_t *ucmd ) { // BFP - Flight
+static void BlockHandling( gclient_t *client, usercmd_t *ucmd ) { // BFP - Block, reflect ki attacks and reduce health damage
+ // if the block length duration hasn't been expired yet and
+ // pressing ki charge (if the aura is lighting) or attack buttons, then stop blocking and start the delay
+ if ( ( client->ps.pm_flags & PMF_BLOCK )
+ && ( ( client->ps.pm_flags & PMF_KI_CHARGE )
+ || ( ucmd->buttons & BUTTON_KI_CHARGE )
+ || ( ucmd->buttons & BUTTON_ATTACK )
+ || ( ucmd->buttons & BUTTON_MELEE ) ) ) {
+ client->ps.pm_flags &= ~PMF_BLOCK;
+ client->blockTime = 0;
+ client->blockDelayTime = level.time + (g_blockDelay.integer * 1000);
+ }
+
+ // initialize the blocking and start the block length duration, specifically, ki boost and aura are disabled
+ if ( !( client->ps.pm_flags & PMF_BLOCK )
+ && ( ucmd->buttons & BUTTON_BLOCK )
+ && !( ucmd->buttons & BUTTON_KI_CHARGE )
+ && client->blockTime <= 0
+ && client->blockDelayTime <= 0 ) {
+ client->ps.pm_flags |= PMF_BLOCK;
+ if ( ucmd->buttons & BUTTON_KI_USE ) {
+ client->ps.eFlags &= ~EF_KI_BOOST;
+ client->ps.eFlags &= ~EF_AURA;
+ }
+ client->blockTime = level.time + (g_blockLength.integer * 1000);
+ }
+
+ // when the block length duration has been expired, then start the delay to avoid user
+ if ( ( client->ps.pm_flags & PMF_BLOCK )
+ && client->blockTime > 0
+ && level.time >= client->blockTime ) {
+ client->ps.pm_flags &= ~PMF_BLOCK;
+ client->blockTime = 0;
+ client->blockDelayTime = level.time + (g_blockDelay.integer * 1000);
+ }
+
+ // knockback when reflecting from attacks
+ if ( ( client->ps.pm_flags & PMF_BLOCK )
+ && client->blockTime > 0
+ && level.time >= client->blockKnockbackTime ) {
+ client->blockKnockbackTime = 0;
+ }
+
+ // debug print block length and delay duration
+#if 0
+ Com_Printf( "BLOCK LENGTH: %d\n", client->blockTime );
+ Com_Printf( "BLOCK DELAY: %d\n", client->blockDelayTime );
+#endif
+
+ // handle the delay and don't leave the user get away with it
+ if ( !( client->ps.pm_flags & PMF_BLOCK )
+ && client->blockDelayTime > 0
+ && level.time < client->blockDelayTime ) {
+ client->blockTime = 0;
+ ucmd->buttons &= ~BUTTON_BLOCK; // if the user holds the key, when that ends, then immediately enters to this status again
+ }
+
+ // reset block delay time if expired
+ if ( !( client->ps.pm_flags & PMF_BLOCK )
+ && client->blockDelayTime > 0
+ && level.time >= client->blockDelayTime ) {
+ client->blockDelayTime = 0;
+ }
+}
+
+/*
+=================
+MeleeHandling
+=================
+*/
+static void MeleeHandling( gentity_t *ent, usercmd_t *ucmd, pmove_t *pm ) { // BFP - Melee
gclient_t *client;
client = ent->client;
- client->oldbuttons = client->buttons;
- client->buttons = ucmd->buttons;
+ if ( client->ps.stats[STAT_HITSTUN_TIME] > 0 ) {
+ return;
+ }
- // enableflight button cycles
- if ( ( client->buttons & BUTTON_ENABLEFLIGHT ) && ! ( client->oldbuttons & BUTTON_ENABLEFLIGHT ) ) {
- Cmd_BFP_Fly_f( ent );
+ if ( !( ucmd->buttons & BUTTON_MELEE ) ) {
+ client->ps.pm_flags &= ~PMF_MELEE;
+ }
+
+ if ( !( ucmd->buttons & BUTTON_TALK ) && ( ucmd->buttons & BUTTON_MELEE )
+ && !( client->ps.pm_flags & PMF_KI_CHARGE )
+ && client->ps.weaponTime <= 0 ) {
+ pm->meleeHit = CheckMeleeAttack( ent );
+ }
+}
+
+/*
+============
+Zanzoken
+============
+*/
+qboolean Zanzoken( gentity_t *ent, int range ) { // BFP - Short-Range Teleport (Zanzoken)
+ trace_t tr;
+ vec3_t right, up, start, direction;
+ int startRightRange = ( range < 0 ) ? -10 : 10;
+
+ // set diagonal direction, included the up vector for upward detection
+ AngleVectors( ent->client->ps.viewangles, NULL, right, up );
+
+ // upward detection, avoid if the player is touching the surface above
+ VectorMA( ent->client->ps.origin, 25, up, start );
+ VectorMA( start, 100, up, direction );
+
+ trap_Trace( &tr, start, ent->r.mins, ent->r.maxs, direction, ent->s.number, MASK_PLAYERSOLID );
+ if ( tr.startsolid || tr.allsolid ) {
+ return qfalse;
+ }
+
+ // if there's something solid diagonally, then avoid the teleportation
+ VectorMA( ent->client->ps.origin, startRightRange, right, start );
+ VectorMA( ent->client->ps.origin, range, right, direction );
+
+ trap_Trace( &tr, start, ent->r.mins, ent->r.maxs, direction, ent->s.number, MASK_PLAYERSOLID );
+ if ( tr.startsolid || tr.allsolid ) {
+ return qfalse;
+ }
+
+ // TELEPORT!
+ tr.endpos[2] += 16; // place the position a bit up
+ VectorCopy( tr.endpos, ent->client->ps.origin );
+
+ // sound event
+ BG_AddPredictableEventToPlayerstate( EV_ZANZOKEN_IN, 0, &ent->client->ps, -1 );
+
+ return qtrue;
+}
+
+/*
+=================
+ZanzokenHandling
+=================
+*/
+static void ZanzokenHandling( gentity_t *ent, usercmd_t *ucmd ) { // BFP - Handling short-range teleport
+ const int ZANZOKEN_NUMBER_TIMES_ALLOWED = 10,
+ ZANZOKEN_ABUSE_DELAY = 2000,
+ MAX_ZANZOKEN_PRESS_TIME = 240,
+ ZANZOKEN_COOLDOWN = 70;
+ gclient_t *client = ent->client;
+ int currentTime = level.time; // use level.time which is not affected by timescale
+
+ if ( client->ps.weaponstate == WEAPON_ACTIVE
+ || client->ps.weaponstate == WEAPON_BEAMSTRUGGLE
+ || client->ps.weaponstate == WEAPON_STUN ) {
+ return;
+ }
+
+ // check if zanzoken is on cooldown
+ if ( client->zanzokenLastUsed > 0
+ && currentTime - client->zanzokenLastUsed < ZANZOKEN_COOLDOWN ) {
+ return;
+ }
+
+ if ( client->ps.pm_flags & PMF_ULTIMATE_TIER ) {
+ return;
+ }
+
+ // zanzoken cannot be used with ki charging status
+ if ( client->ps.pm_flags & PMF_KI_CHARGE ) {
+ return;
+ }
+
+ // restriction: stop abusing zanzoken technique all time
+ if ( client->zanzokenNumberTimesAllowed >= ZANZOKEN_NUMBER_TIMES_ALLOWED ) {
+ client->zanzokenNumberTimesAllowed = 0;
+ client->zanzokenDelay = currentTime;
+ return;
+ }
+
+ if ( client->zanzokenDelay > 0 && currentTime - client->zanzokenDelay <= ZANZOKEN_ABUSE_DELAY ) {
+ return;
+ }
+
+ if ( ucmd->rightmove && client->zanzokenPressTime <= 0 ) {
+ client->zanzokenPressTime = currentTime;
+ client->zanzokenNow = qfalse;
+ // handle directions to avoid pressing the opposite
+ if ( ucmd->rightmove > 0 ) {
+ client->zanzokenLeft = qfalse;
+ client->zanzokenRight = qtrue;
+ } else {
+ client->zanzokenLeft = qtrue;
+ client->zanzokenRight = qfalse;
+ }
+ }
+
+ // once pressed and having one moment to press again, zanzoken will be possible at these milliseconds
+ if ( !ucmd->rightmove && client->zanzokenPressTime > 0 ) {
+ int elapsed = currentTime - client->zanzokenPressTime;
+
+ if ( elapsed > 50 && elapsed <= MAX_ZANZOKEN_PRESS_TIME && !client->zanzokenNow ) {
+ client->zanzokenNow = qtrue;
+ client->zanzokenNumberTimesAllowed++;
+ }
+
+ if ( elapsed > MAX_ZANZOKEN_PRESS_TIME ) {
+ client->zanzokenNumberTimesAllowed = 0;
+ client->zanzokenPressTime = 0;
+ client->zanzokenNow = qfalse;
+ client->zanzokenLeft = qfalse;
+ client->zanzokenRight = qfalse;
+ return;
+ }
+ }
+
+ if ( client->ps.stats[STAT_KI] > ( client->ps.stats[STAT_MAX_KI] * 0.05 )
+ && ucmd->rightmove && client->zanzokenNow ) {
+ int range = ( ucmd->rightmove > 0 ) ? 500 : -500;
+
+ // handle the directions correctly
+ if ( ( ucmd->rightmove > 0 && !client->zanzokenRight )
+ || ( ucmd->rightmove < 0 && !client->zanzokenLeft ) ) {
+ client->zanzokenLeft = qfalse;
+ client->zanzokenRight = qfalse;
+ return;
+ }
+
+ // put in 0.1 msec delay before the player can 'zanzoken' out of stun
+ if ( client->ps.stats[STAT_HITSTUN_TIME] > 2900 ) {
+ client->zanzokenPressTime = 0;
+ client->zanzokenNow = qfalse;
+ client->zanzokenLeft = qfalse;
+ client->zanzokenRight = qfalse;
+ return;
+ }
+
+ if ( Zanzoken( ent, range ) ) {
+ // block and stun statuses are removed when using zanzoken
+ if ( client->ps.stats[STAT_HITSTUN_TIME] <= 3000 ) {
+ client->ps.stats[STAT_HITSTUN_TIME] = 0;
+ }
+ client->ps.pm_flags &= ~PMF_BLOCK;
+ // consumes 5% of ki
+ client->ps.stats[STAT_KI] -= ( client->ps.stats[STAT_MAX_KI] * 0.05 );
+ client->zanzokenPressTime = 0;
+ client->zanzokenNow = qfalse;
+ client->zanzokenLeft = qfalse;
+ client->zanzokenRight = qfalse;
+ client->zanzokenLastUsed = currentTime; // Set cooldown
+ }
}
}
@@ -652,7 +974,7 @@ void ClientThink_real( gentity_t *ent ) {
return;
}
// mark the time, so the connection sprite can be removed
- ucmd = &ent->client->pers.cmd;
+ ucmd = &client->pers.cmd;
// sanity check the command time to prevent speedup cheating
if ( ucmd->serverTime > level.time + 200 ) {
@@ -711,7 +1033,8 @@ void ClientThink_real( gentity_t *ent ) {
// clear the rewards if time
if ( level.time > client->rewardTime ) {
- client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
}
if ( client->noclip ) {
@@ -727,54 +1050,104 @@ void ClientThink_real( gentity_t *ent ) {
// set speed
client->ps.speed = g_speed.value;
- // BFP - Ki use has 2 options: "kiusetoggle" to toggle and "+button8" when key is being hold
- // BFP - if BUTTON_KI_USE > speed
- if ( ( ucmd->buttons & BUTTON_KI_USE ) // BFP - Using Ki
- || ( ent->client->ps.pm_flags & PMF_KI_BOOST ) ) { // BFP - When "kiusetoggle" is binded, enables/disables
- ent->client->ps.speed *= 2.5;
- ent->client->ps.eFlags |= EF_AURA;
- } else {
- ent->client->ps.eFlags &= ~EF_AURA;
- }
-
- // BFP - Ki Charge
- if ( ucmd->buttons & BUTTON_KI_CHARGE ) {
- client->ps.eFlags |= EF_AURA;
- }
-
- if ( client->ps.pm_flags & PMF_FLYING ) { // BFP - Flight speed
- client->ps.speed *= 1.5;
- }
- // BFP - TODO: When charging a ki attack like beam wave, consult FlyingThink and SpectatorThink if that's the case
-
- // BFP - if BUTTON_ENABLEFLIGHT enable flight
- FlyingThink( ent, ucmd ); // prevents client-server side issues when there's other client in-game
-
-// BFP - no hook
-#if 0
+ // BFP - Remove beam
// Let go of the hook if we aren't firing
if ( client->ps.weapon == WP_GRAPPLING_HOOK &&
client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) {
- Weapon_HookFree(client->hook);
+ // Weapon_HookFree(client->hook);
+ Weapon_BFPBeamFree( client->hook );
}
-#endif
// set up for pmove
oldEventSequence = client->ps.eventSequence;
memset (&pm, 0, sizeof(pm));
- // check for the hit-scan gauntlet, don't let the action
- // go through as an attack unless it actually hits something
- if ( client->ps.weapon == WP_GAUNTLET && !( ucmd->buttons & BUTTON_TALK ) &&
- ( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) {
- pm.gauntletHit = CheckGauntletAttack( ent );
+ if ( client->ps.pm_type != PM_DEAD && client->ps.pm_type != PM_SPECTATOR ) {
+
+ // BFP - After unlocking tier, when the time is out, the aura tier effect vanishes
+ if ( level.time >= client->tierUnlockedTime && client->ps.persistant[PERS_POWERLEVEL] < 1000 ) {
+ client->ps.eFlags &= ~EF_AURA_TIER_UP;
+ }
+
+ // BFP - Ultimate tier
+ if ( ( client->ps.pm_flags & PMF_ULTIMATE_TIER )
+ && ( level.time >= client->tierUnlockedTime - 1000 ) ) { // remove aura transformation effect after 3.6 secs
+ client->ps.eFlags &= ~EF_AURA_TIER_UP;
+ }
+
+ if ( ( client->ps.pm_flags & PMF_ULTIMATE_TIER ) && level.time >= client->tierUnlockedTime ) {
+ client->ps.pm_flags &= ~PMF_ULTIMATE_TIER;
+ }
+
+ // BFP - Short-Range Teleport (Zanzoken)
+ ZanzokenHandling( ent, ucmd );
+
+ // BFP - Hit stun melee delay time
+ if ( client->hitStunMeleeDelayTime > 0
+ && level.time >= client->hitStunMeleeDelayTime ) {
+ client->hitStunMeleeDelayTime = 0;
+ }
+
+ // BFP - g_chargeDelay cvar for ki charge animation and appearing the aura after this time
+ if ( !( ucmd->buttons & BUTTON_KI_USE )
+ && ( ucmd->buttons & BUTTON_KI_CHARGE )
+ && !( client->ps.pm_flags & PMF_KI_CHARGE ) ) {
+ client->ps.pm_time = ( g_chargeDelay.integer > 0 ) ? g_chargeDelay.integer : 0;
+ }
+
+ // BFP - Ki use has 2 options: "kiusetoggle" to toggle and "+button8" when key is being hold
+ if ( client->ps.stats[STAT_HITSTUN_TIME] <= 0
+ && !( client->ps.pm_flags & PMF_BLOCK )
+ && ( ( ucmd->buttons & BUTTON_KI_USE ) // BFP - Using Ki
+ || ( client->ps.eFlags & EF_KI_BOOST ) ) // BFP - When "kiusetoggle" is binded, enables/disables
+ && client->ps.weaponstate != WEAPON_STUN ) {
+ client->ps.eFlags |= EF_KI_BOOST; // Handle ki boost status
+ client->ps.eFlags |= EF_AURA;
+ } else {
+ if ( !( client->ps.pm_flags & PMF_BLOCK ) // BFP - Handle block status from this conditional
+ && !( ucmd->buttons & BUTTON_KI_CHARGE ) ) { // BFP - If it's charging while it was using ki boost, don't remove the aura!
+ client->ps.eFlags &= ~EF_AURA;
+ client->ps.eFlags &= ~EF_KI_BOOST; // Handle ki boost status
+ }
+ }
+
+ // BFP - Block, reflect ki attacks and reduce health damage
+ BlockHandling( client, ucmd );
+
+ // BFP - Melee handling
+ MeleeHandling( ent, ucmd, &pm );
+
+ // BFP - Ki Charge
+ if ( ( ucmd->buttons & BUTTON_KI_CHARGE ) && ( client->ps.pm_flags & PMF_KI_CHARGE )
+ && client->ps.pm_time > 0 ) { // still delayed
+ client->ps.eFlags &= ~EF_AURA;
+ }
+ if ( ( client->ps.pm_flags & PMF_KI_CHARGE )
+ && client->ps.pm_time <= 0 ) { // charge ki!
+ client->ps.eFlags |= EF_AURA;
+ }
}
+ // BFP - No flight
+ pm.noFlight = qfalse;
+ if ( g_noFlight.integer > 0 ) {
+ pm.noFlight = qtrue;
+ }
+
+ // BFP - Melee only
+ pm.meleeOnly = qfalse;
+ if ( g_meleeOnly.integer > 0 ) {
+ pm.meleeOnly = qtrue;
+ }
+
+ // BFP - No force gesture anim
+#if 0
if ( ent->flags & FL_FORCE_GESTURE ) {
ent->flags &= ~FL_FORCE_GESTURE;
- ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
+ client->pers.cmd.buttons |= BUTTON_GESTURE;
}
+#endif
pm.ps = &client->ps;
pm.cmd = *ucmd;
@@ -800,23 +1173,21 @@ void ClientThink_real( gentity_t *ent ) {
Pmove (&pm);
// save results of pmove
- if ( ent->client->ps.eventSequence != oldEventSequence ) {
+ if ( client->ps.eventSequence != oldEventSequence ) {
ent->eventTime = level.time;
}
if (g_smoothClients.integer) {
- BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
+ BG_PlayerStateToEntityStateExtraPolate( &client->ps, &ent->s, client->ps.commandTime, qtrue );
}
else {
- BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
+ BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}
- SendPendingPredictableEvents( &ent->client->ps );
+ SendPendingPredictableEvents( &client->ps );
-// BFP - no hook
-#if 0
- if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
+ // BFP - Beam fire hold handling
+ if ( !( client->ps.eFlags & EF_FIRING ) ) {
client->fireHeld = qfalse; // for grapple
}
-#endif
// use the snapped origin for linking so it matches client predicted versions
VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
@@ -832,12 +1203,12 @@ void ClientThink_real( gentity_t *ent ) {
// link entity now, after any personal teleporters have been used
trap_LinkEntity (ent);
- if ( !ent->client->noclip ) {
+ if ( !client->noclip ) {
G_TouchTriggers( ent );
}
// NOTE: now copy the exact origin over otherwise clients can be snapped into solid
- VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
+ VectorCopy( client->ps.origin, ent->r.currentOrigin );
//test for solid areas in the AAS file
BotTestAAS(ent->r.currentOrigin);
@@ -846,7 +1217,7 @@ void ClientThink_real( gentity_t *ent ) {
ClientImpacts( ent, &pm );
// save results of triggers and client events
- if (ent->client->ps.eventSequence != oldEventSequence) {
+ if (client->ps.eventSequence != oldEventSequence) {
ent->eventTime = level.time;
}
@@ -917,13 +1288,13 @@ SpectatorClientEndFrame
==================
*/
void SpectatorClientEndFrame( gentity_t *ent ) {
- gclient_t *cl;
+ gclient_t *cl, *client;
- // if we are doing a chase cam or a remote view, grab the latest info
- if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
- int clientNum, flags;
+ client = ent->client;
- clientNum = ent->client->sess.spectatorClient;
+ // if we are doing a chase cam or a remote view, grab the latest info
+ if ( client->sess.spectatorState == SPECTATOR_FOLLOW ) {
+ int clientNum = client->sess.spectatorClient;
// team follow1 and team follow2 go to whatever clients are playing
if ( clientNum == -1 ) {
@@ -931,32 +1302,32 @@ void SpectatorClientEndFrame( gentity_t *ent ) {
} else if ( clientNum == -2 ) {
clientNum = level.follow2;
}
- if ( clientNum >= 0 ) {
+ if ( (unsigned)clientNum < MAX_CLIENTS ) {
cl = &level.clients[ clientNum ];
if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) {
- flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED));
- ent->client->ps = cl->ps;
- ent->client->ps.pm_flags |= PMF_FOLLOW;
- ent->client->ps.eFlags = flags;
+ int flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED));
+ client->ps = cl->ps;
+ client->ps.pm_flags |= PMF_FOLLOW;
+ client->ps.eFlags = flags;
return;
} else {
// drop them to free spectators unless they are dedicated camera followers
- if ( ent->client->sess.spectatorClient >= 0 ) {
- ent->client->sess.spectatorState = SPECTATOR_FREE;
- ClientBegin( ent->client - level.clients );
+ if ( client->sess.spectatorClient >= 0 ) {
+ client->sess.spectatorState = SPECTATOR_FREE;
+ ClientBegin( client - level.clients );
}
}
}
}
// BFP - PMF_SCOREBOARD is unused
- /*
- if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
- ent->client->ps.pm_flags |= PMF_SCOREBOARD;
+#if 0
+ if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
+ client->ps.pm_flags |= PMF_SCOREBOARD;
} else {
- ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
+ client->ps.pm_flags &= ~PMF_SCOREBOARD;
}
- */
+#endif
}
/*
@@ -970,15 +1341,12 @@ while a slow client may have multiple ClientEndFrame between ClientThink.
*/
void ClientEndFrame( gentity_t *ent ) {
int i;
- clientPersistant_t *pers;
if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
SpectatorClientEndFrame( ent );
return;
}
- pers = &ent->client->pers;
-
// turn off any expired powerups
for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
if ( ent->client->ps.powerups[ i ] < level.time ) {
diff --git a/source/game/g_arenas.c b/source/game/g_arenas.c
index b2951bd..f0f4d8f 100644
--- a/source/game/g_arenas.c
+++ b/source/game/g_arenas.c
@@ -42,7 +42,6 @@ void UpdateTournamentInfo( void ) {
gentity_t *player;
int playerClientNum;
int n, accuracy, perfect, msglen;
- int buflen;
char buf[32];
char msg[MAX_STRING_CHARS];
@@ -77,18 +76,25 @@ void UpdateTournamentInfo( void ) {
}
perfect = ( level.clients[playerClientNum].ps.persistant[PERS_RANK] == 0 && player->client->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;
+ // BFP - No impressive, gauntlet, defend and assist counters
+#if 0
Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy,
player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT],
player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE],
perfect );
+#endif
+ Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy,
+ 0, player->client->ps.persistant[PERS_EXCELLENT_COUNT],
+ 0, player->client->ps.persistant[PERS_SCORE],
+ perfect );
}
msglen = (int)strlen( msg );
for( i = 0; i < level.numNonSpectatorClients; i++ ) {
n = level.sortedClients[i];
Com_sprintf( buf, sizeof(buf), " %i %i %i", n, level.clients[n].ps.persistant[PERS_RANK], level.clients[n].ps.persistant[PERS_SCORE] );
- buflen = (int)strlen( buf );
- if( msglen + buflen + 1 >= sizeof(msg) ) {
+ msglen += (int)strlen( buf );
+ if( msglen >= sizeof(msg)-1 ) {
break;
}
strcat( msg, buf );
diff --git a/source/game/g_bot.c b/source/game/g_bot.c
index 32a48e4..4c32721 100644
--- a/source/game/g_bot.c
+++ b/source/game/g_bot.c
@@ -43,7 +43,6 @@ typedef struct {
int spawnTime;
} botSpawnQueue_t;
-//static int botBeginDelay = 0; // bk001206 - unused, init
static botSpawnQueue_t botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH];
vmCvar_t bot_minplayers;
@@ -52,6 +51,8 @@ extern gentity_t *podium1;
extern gentity_t *podium2;
extern gentity_t *podium3;
+extern char mapname[ MAX_QPATH ];
+
float trap_Cvar_VariableValue( const char *var_name ) {
char buf[128];
@@ -108,7 +109,7 @@ int G_ParseInfos( char *buf, int max, char *infos[] ) {
Info_SetValueForKey( info, key, token );
}
//NOTE: extra space for arena number
- infos[count] = G_Alloc((int)strlen(info) + (int)strlen("\\num\\") + (int)strlen(va("%d", MAX_ARENAS)) + 1);
+ infos[count] = G_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
if (infos[count]) {
strcpy(infos[count], info);
count++;
@@ -117,18 +118,19 @@ int G_ParseInfos( char *buf, int max, char *infos[] ) {
return count;
}
+
/*
===============
G_LoadArenasFromFile
===============
*/
-static void G_LoadArenasFromFile( char *filename ) {
+static void G_LoadArenasFromFile( const char *filename ) {
int len;
fileHandle_t f;
char buf[MAX_ARENAS_TEXT];
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if ( !f ) {
+ if ( f == FS_INVALID_HANDLE ) {
trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
return;
}
@@ -145,6 +147,7 @@ static void G_LoadArenasFromFile( char *filename ) {
g_numArenas += G_ParseInfos( buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas] );
}
+
/*
===============
G_LoadArenas
@@ -170,7 +173,7 @@ static void G_LoadArenas( void ) {
}
// get all arenas from .arena files
- numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 );
+ numdirs = trap_FS_GetFileList( "scripts", ".arena", dirlist, sizeof( dirlist ) );
dirptr = dirlist;
for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
dirlen = (int)strlen(dirptr);
@@ -229,27 +232,28 @@ static void PlayerIntroSound( const char *modelAndSkin ) {
trap_SendConsoleCommand( EXEC_APPEND, va( "play sound/player/announce/%s.wav\n", skin ) );
}
+
/*
===============
G_AddRandomBot
===============
*/
-void G_AddRandomBot( int team ) {
+void G_AddRandomBot( team_t team ) {
int i, n, num;
float skill;
- char *value, netname[36], *teamstr;
+ char *value, netname[36], *teamstr, *skillstr;
gclient_t *cl;
num = 0;
for ( n = 0; n < g_numBots ; n++ ) {
value = Info_ValueForKey( g_botInfos[n], "name" );
//
- for ( i=0 ; i< g_maxclients.integer ; i++ ) {
+ for ( i = 0 ; i < level.maxclients ; i++ ) {
cl = level.clients + i;
if ( cl->pers.connected != CON_CONNECTED ) {
continue;
}
- if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
+ if ( !(g_entities[i].r.svFlags & SVF_BOT) ) {
continue;
}
if ( team >= 0 && cl->sess.sessionTeam != team ) {
@@ -259,20 +263,27 @@ void G_AddRandomBot( int team ) {
break;
}
}
- if (i >= g_maxclients.integer) {
+ if (i >= level.maxclients) {
num++;
}
}
num = random() * num;
for ( n = 0; n < g_numBots ; n++ ) {
- value = Info_ValueForKey( g_botInfos[n], "name" );
- //
- for ( i=0 ; i< g_maxclients.integer ; i++ ) {
+
+ value = Info_ValueForKey( g_botInfos[ n ], "name" );
+
+ skillstr = Info_ValueForKey( g_botInfos[ n ], "skill" );
+ if ( *skillstr )
+ skill = atof( skillstr );
+ else
+ skill = trap_Cvar_VariableValue( "g_spSkill" );
+
+ for ( i = 0 ; i < level.maxclients ; i++ ) {
cl = level.clients + i;
if ( cl->pers.connected != CON_CONNECTED ) {
continue;
}
- if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
+ if ( !(g_entities[i].r.svFlags & SVF_BOT) ) {
continue;
}
if ( team >= 0 && cl->sess.sessionTeam != team ) {
@@ -282,23 +293,22 @@ void G_AddRandomBot( int team ) {
break;
}
}
- if (i >= g_maxclients.integer) {
+ if (i >= level.maxclients) {
num--;
- if (num <= 0) {
- skill = trap_Cvar_VariableValue( "g_spSkill" );
+ if ( num <= 0 ) {
if (team == TEAM_RED) teamstr = "red";
else if (team == TEAM_BLUE) teamstr = "blue";
else teamstr = "";
- strncpy(netname, value, sizeof(netname)-1);
- netname[sizeof(netname)-1] = '\0';
+ Q_strncpyz(netname, value, sizeof(netname));
Q_CleanStr(netname);
- trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) );
+ trap_SendConsoleCommand( EXEC_INSERT, va( "addbot %s %1.2f %s 0\n", netname, skill, teamstr ) );
return;
}
}
}
}
+
/*
===============
G_RemoveRandomBot
@@ -309,12 +319,12 @@ int G_RemoveRandomBot( int team ) {
char netname[36];
gclient_t *cl;
- for ( i=0 ; i< g_maxclients.integer ; i++ ) {
+ for ( i = 0 ; i < level.maxclients ; i++ ) {
cl = level.clients + i;
if ( cl->pers.connected != CON_CONNECTED ) {
continue;
}
- if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
+ if ( !(g_entities[i].r.svFlags & SVF_BOT) ) {
continue;
}
if ( team >= 0 && cl->sess.sessionTeam != team ) {
@@ -328,22 +338,23 @@ int G_RemoveRandomBot( int team ) {
return qfalse;
}
+
/*
===============
G_CountHumanPlayers
===============
*/
-int G_CountHumanPlayers( int team ) {
+static int G_CountHumanPlayers( team_t team ) {
int i, num;
gclient_t *cl;
num = 0;
- for ( i=0 ; i< g_maxclients.integer ; i++ ) {
+ for ( i = 0 ; i < level.maxclients ; i++ ) {
cl = level.clients + i;
if ( cl->pers.connected != CON_CONNECTED ) {
continue;
}
- if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) {
+ if ( g_entities[i].r.svFlags & SVF_BOT ) {
continue;
}
if ( team >= 0 && cl->sess.sessionTeam != team ) {
@@ -354,22 +365,23 @@ int G_CountHumanPlayers( int team ) {
return num;
}
+
/*
===============
G_CountBotPlayers
===============
*/
-int G_CountBotPlayers( int team ) {
+static int G_CountBotPlayers( team_t team ) {
int i, n, num;
gclient_t *cl;
num = 0;
- for ( i=0 ; i< g_maxclients.integer ; i++ ) {
+ for ( i=0 ; i< level.maxclients ; i++ ) {
cl = level.clients + i;
if ( cl->pers.connected != CON_CONNECTED ) {
continue;
}
- if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
+ if ( !(g_entities[i].r.svFlags & SVF_BOT) ) {
continue;
}
if ( team >= 0 && cl->sess.sessionTeam != team ) {
@@ -389,6 +401,7 @@ int G_CountBotPlayers( int team ) {
return num;
}
+
/*
===============
G_CheckMinimumPlayers
@@ -399,19 +412,24 @@ void G_CheckMinimumPlayers( void ) {
int humanplayers, botplayers;
static int checkminimumplayers_time;
- if (level.intermissiontime) return;
+ if ( level.intermissiontime )
+ return;
+
//only check once each 10 seconds
- if (checkminimumplayers_time > level.time - 10000) {
+ if ( checkminimumplayers_time > level.time - 10000 )
return;
- }
+
+ if ( level.time - level.startTime < 2000 )
+ return;
+
checkminimumplayers_time = level.time;
trap_Cvar_Update(&bot_minplayers);
minplayers = bot_minplayers.integer;
if (minplayers <= 0) return;
if (g_gametype.integer >= GT_TEAM) {
- if (minplayers >= g_maxclients.integer / 2) {
- minplayers = (g_maxclients.integer / 2) -1;
+ if (minplayers >= level.maxclients / 2) {
+ minplayers = (level.maxclients / 2) -1;
}
humanplayers = G_CountHumanPlayers( TEAM_RED );
@@ -432,9 +450,10 @@ void G_CheckMinimumPlayers( void ) {
G_RemoveRandomBot( TEAM_BLUE );
}
}
- else if (g_gametype.integer == GT_TOURNAMENT ) {
- if (minplayers >= g_maxclients.integer) {
- minplayers = g_maxclients.integer-1;
+ else if ( g_gametype.integer == GT_TOURNAMENT
+ || g_gametype.integer == GT_SURVIVAL ) { // BFP - Survival
+ if ( minplayers >= level.maxclients ) {
+ minplayers = level.maxclients - 1;
}
humanplayers = G_CountHumanPlayers( -1 );
botplayers = G_CountBotPlayers( -1 );
@@ -450,8 +469,8 @@ void G_CheckMinimumPlayers( void ) {
}
}
else if (g_gametype.integer == GT_FFA) {
- if (minplayers >= g_maxclients.integer) {
- minplayers = g_maxclients.integer-1;
+ if (minplayers >= level.maxclients) {
+ minplayers = level.maxclients-1;
}
humanplayers = G_CountHumanPlayers( TEAM_FREE );
botplayers = G_CountBotPlayers( TEAM_FREE );
@@ -464,6 +483,7 @@ void G_CheckMinimumPlayers( void ) {
}
}
+
/*
===============
G_CheckBotSpawn
@@ -509,7 +529,8 @@ static void AddBotToSpawnQueue( int clientNum, int delay ) {
}
}
- G_Printf( S_COLOR_YELLOW "Unable to delay spawn\n" );
+ G_Printf( S_COLOR_YELLOW "Unable to delay bot spawn\n" );
+
ClientBegin( clientNum );
}
@@ -563,16 +584,17 @@ qboolean G_BotConnect( int clientNum, qboolean restart ) {
G_AddBot
===============
*/
-static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) {
+static void G_AddBot( const char *name, float skill, const char *team, int delay, const char *altname ) {
int clientNum;
char *botinfo;
gentity_t *bot;
char *key;
char *s;
- char *botname;
- char *model;
- char *headmodel;
+ const char *botname;
+ const char *model;
+ const char *headmodel;
char userinfo[MAX_INFO_STRING];
+ char nm[MAX_CVAR_VALUE_STRING];
// get the botinfo from bots.txt
botinfo = G_GetBotInfoByName( name );
@@ -592,11 +614,16 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
if (altname && altname[0]) {
botname = altname;
}
- Info_SetValueForKey( userinfo, "name", botname );
+
+ BG_CleanName( botname, nm, sizeof( nm ), "unnamed bot" );
+ Info_SetValueForKey( userinfo, "name", nm );
+
Info_SetValueForKey( userinfo, "rate", "25000" );
Info_SetValueForKey( userinfo, "snaps", "20" );
Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) );
+ // BFP - No handicap
+#if 0
if ( skill >= 1 && skill < 2 ) {
Info_SetValueForKey( userinfo, "handicap", "50" );
}
@@ -606,6 +633,7 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
else if ( skill >= 3 && skill < 4 ) {
Info_SetValueForKey( userinfo, "handicap", "90" );
}
+#endif
key = "model";
model = Info_ValueForKey( botinfo, key );
@@ -613,8 +641,10 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
model = "visor/default";
}
Info_SetValueForKey( userinfo, key, model );
- key = "team_model";
- Info_SetValueForKey( userinfo, key, model );
+ if ( g_gametype.integer >= GT_TEAM ) {
+ key = "team_model";
+ Info_SetValueForKey( userinfo, key, model );
+ }
key = "headmodel";
headmodel = Info_ValueForKey( botinfo, key );
@@ -622,8 +652,10 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
headmodel = model;
}
Info_SetValueForKey( userinfo, key, headmodel );
- key = "team_headmodel";
- Info_SetValueForKey( userinfo, key, headmodel );
+ if ( g_gametype.integer >= GT_TEAM ) {
+ key = "team_headmodel";
+ Info_SetValueForKey( userinfo, key, headmodel );
+ }
key = "gender";
s = Info_ValueForKey( botinfo, key );
@@ -632,6 +664,8 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
}
Info_SetValueForKey( userinfo, "sex", s );
+ // BFP - No color1 and color2
+#if 0
key = "color1";
s = Info_ValueForKey( botinfo, key );
if ( !*s ) {
@@ -645,6 +679,7 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
s = "5";
}
Info_SetValueForKey( userinfo, key, s );
+#endif
s = Info_ValueForKey(botinfo, "aifile");
if (!*s ) {
@@ -660,22 +695,14 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
return;
}
- // initialize the bot settings
- if( !team || !*team ) {
- if( g_gametype.integer >= GT_TEAM ) {
- if( PickTeam(clientNum) == TEAM_RED) {
- team = "red";
- }
- else {
- team = "blue";
- }
- }
- else {
- team = "red";
- }
+ // cleanup previous data manually
+ // because client may silently (re)connect without ClientDisconnect in case of crash for example
+ if ( level.clients[ clientNum ].pers.connected != CON_DISCONNECTED ) {
+ ClientDisconnect( clientNum );
}
+
Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) );
- Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) );
+ Info_SetValueForKey( userinfo, "skill", va( "%1.2f", skill ) );
Info_SetValueForKey( userinfo, "team", team );
bot = &g_entities[ clientNum ];
@@ -690,7 +717,7 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay
return;
}
- if( delay == 0 ) {
+ if ( delay == 0 ) {
ClientBegin( clientNum );
return;
}
@@ -731,6 +758,10 @@ void Svcmd_AddBot_f( void ) {
}
else {
skill = atof( string );
+ if ( skill < 1 )
+ skill = 1;
+ else if ( skill > 5 )
+ skill = 5;
}
// team
@@ -754,7 +785,7 @@ void Svcmd_AddBot_f( void ) {
// go ahead and load the bot's media immediately
if ( level.time - level.startTime > 1000 &&
trap_Cvar_VariableIntegerValue( "cl_running" ) ) {
- trap_SendServerCommand( -1, "loaddefered\n" ); // FIXME: spelled wrong, but not changing for demo
+ trap_SendServerCommand( -1, "loaddefered\n" );
}
}
@@ -765,30 +796,30 @@ Svcmd_BotList_f
*/
void Svcmd_BotList_f( void ) {
int i;
- char name[MAX_TOKEN_CHARS];
- char funname[MAX_TOKEN_CHARS];
- char model[MAX_TOKEN_CHARS];
- char aifile[MAX_TOKEN_CHARS];
-
- trap_Printf("^1name model aifile funname\n");
- for (i = 0; i < g_numBots; i++) {
- strcpy(name, Info_ValueForKey( g_botInfos[i], "name" ));
+ char name[MAX_NETNAME];
+ char funname[MAX_NETNAME];
+ char model[MAX_QPATH];
+ char aifile[MAX_QPATH];
+
+ trap_Printf( S_COLOR_RED "name model aifile funname\n" );
+ for ( i = 0; i < g_numBots; i++ ) {
+ Q_strncpyz( name, Info_ValueForKey( g_botInfos[i], "name" ), sizeof( name ) );
if ( !*name ) {
strcpy(name, "UnnamedPlayer");
}
- strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" ));
+ Q_strncpyz( funname, Info_ValueForKey( g_botInfos[i], "funname" ), sizeof( funname ) );
if ( !*funname ) {
- strcpy(funname, "");
+ strcpy( funname, "" );
}
- strcpy(model, Info_ValueForKey( g_botInfos[i], "model" ));
+ Q_strncpyz( model, Info_ValueForKey( g_botInfos[i], "model" ), sizeof( model ) );
if ( !*model ) {
- strcpy(model, "visor/default");
+ strcpy( model, "visor/default" );
}
- strcpy(aifile, Info_ValueForKey( g_botInfos[i], "aifile"));
- if (!*aifile ) {
- strcpy(aifile, "bots/default_c.c");
+ Q_strncpyz( aifile, Info_ValueForKey( g_botInfos[i], "aifile" ), sizeof( aifile ) );
+ if ( !*aifile ) {
+ strcpy( aifile, "bots/default_c.c" );
}
- trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, aifile, funname));
+ trap_Printf( va( "%-16s %-16s %-20s %-20s\n", name, model, aifile, funname ) );
}
}
@@ -798,7 +829,7 @@ void Svcmd_BotList_f( void ) {
G_SpawnBots
===============
*/
-static void G_SpawnBots( char *botList, int baseDelay ) {
+static void G_SpawnBots( const char *botList, int baseDelay ) {
char *bot;
char *p;
float skill;
@@ -819,15 +850,15 @@ static void G_SpawnBots( char *botList, int baseDelay ) {
skill = 5;
}
- Q_strncpyz( bots, botList, sizeof(bots) );
+ Q_strncpyz( bots, botList, sizeof( bots ) );
p = &bots[0];
delay = baseDelay;
while( *p ) {
//skip spaces
- while( *p && *p == ' ' ) {
+ while( *p == ' ' ) {
p++;
}
- if( !p ) {
+ if( !*p ) {
break;
}
@@ -839,7 +870,7 @@ static void G_SpawnBots( char *botList, int baseDelay ) {
p++;
}
if( *p ) {
- *p++ = 0;
+ *p++ = '\0';
}
// we must add the bot this way, calling G_AddBot directly at this stage
@@ -856,29 +887,30 @@ static void G_SpawnBots( char *botList, int baseDelay ) {
G_LoadBotsFromFile
===============
*/
-static void G_LoadBotsFromFile( char *filename ) {
+static void G_LoadBotsFromFile( const char *filename ) {
int len;
fileHandle_t f;
char buf[MAX_BOTS_TEXT];
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if ( !f ) {
+ if ( f == FS_INVALID_HANDLE ) {
trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
return;
}
if ( len >= MAX_BOTS_TEXT ) {
- trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) );
+ trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_BOTS_TEXT ) );
trap_FS_FCloseFile( f );
return;
}
trap_FS_Read( buf, len, f );
- buf[len] = 0;
trap_FS_FCloseFile( f );
+ buf[ len ] = '\0';
g_numBots += G_ParseInfos( buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots] );
}
+
/*
===============
G_LoadBots
@@ -899,16 +931,16 @@ static void G_LoadBots( void ) {
g_numBots = 0;
- trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM );
- if( *botsFile.string ) {
- G_LoadBotsFromFile(botsFile.string);
- }
- else {
- G_LoadBotsFromFile("scripts/bots.txt");
+ trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_ARCHIVE | CVAR_LATCH );
+
+ if ( *botsFile.string && g_gametype.integer != GT_SINGLE_PLAYER ) {
+ G_LoadBotsFromFile( botsFile.string );
+ } else {
+ G_LoadBotsFromFile( "scripts/bots.txt" );
}
// get all bots from .bot files
- numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
+ numdirs = trap_FS_GetFileList( "scripts", ".bot", dirlist, sizeof( dirlist ) );
dirptr = dirlist;
for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
dirlen = (int)strlen(dirptr);
@@ -954,6 +986,7 @@ char *G_GetBotInfoByName( const char *name ) {
return NULL;
}
+
/*
===============
G_InitBots
@@ -965,8 +998,6 @@ void G_InitBots( qboolean restart ) {
const char *arenainfo;
char *strValue;
int basedelay;
- char map[MAX_QPATH];
- char serverinfo[MAX_INFO_STRING];
G_LoadBots();
G_LoadArenas();
@@ -974,9 +1005,7 @@ void G_InitBots( qboolean restart ) {
trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO );
if( g_gametype.integer == GT_SINGLE_PLAYER ) {
- trap_GetServerinfo( serverinfo, sizeof(serverinfo) );
- Q_strncpyz( map, Info_ValueForKey( serverinfo, "mapname" ), sizeof(map) );
- arenainfo = G_GetArenaInfoByMap( map );
+ arenainfo = G_GetArenaInfoByMap( mapname );
if ( !arenainfo ) {
return;
}
diff --git a/source/game/g_client.c b/source/game/g_client.c
index cfd017b..5a072d9 100644
--- a/source/game/g_client.c
+++ b/source/game/g_client.c
@@ -28,6 +28,8 @@ static vec3_t playerMins = {-15, -15, -24};
static vec3_t playerMaxs = {15, 15, 32};
#define MAX_SPAWN_POINTS 128
+static char ban_reason[MAX_CVAR_VALUE_STRING];
+
/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) initial
potential spawning position for deathmatch games.
The first time a player enters the game, they will be at an 'initial' spot.
@@ -81,18 +83,18 @@ SpotWouldTelefrag
*/
qboolean SpotWouldTelefrag( gentity_t *spot ) {
int i, num;
- int touch[MAX_GENTITIES];
+ int touch[MAX_CLIENTS];
gentity_t *hit;
vec3_t mins, maxs;
VectorAdd( spot->s.origin, playerMins, mins );
VectorAdd( spot->s.origin, playerMaxs, maxs );
- num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
+ num = G_EntitiesInBox( mins, maxs, touch, level.maxclients );
- for (i=0 ; iclient && hit->client->ps.stats[STAT_HEALTH] > 0 ) {
- if ( hit->client) {
+ if ( hit->client ) {
return qtrue;
}
@@ -264,7 +266,7 @@ static gentity_t *SelectRandomFurthestSpawnPoint( const gentity_t *ent, vec3_t a
VectorCopy( spot->s.angles, angles );
VectorCopy( spot->s.origin, origin );
- origin[2] += 9.0f;
+ origin[2] += SPAWN_HEIGHT;
return spot;
}
@@ -310,7 +312,7 @@ gentity_t *SelectInitialSpawnPoint( gentity_t *ent, vec3_t origin, vec3_t angles
VectorCopy( spot->s.angles, angles );
VectorCopy( spot->s.origin, origin );
- origin[2] += 9.0f;
+ origin[2] += SPAWN_HEIGHT;
return spot;
}
@@ -388,6 +390,11 @@ void CopyToBodyQue( gentity_t *ent ) {
trap_UnlinkEntity (ent);
+ // don't leave a corpse if already gibbed
+ if ( ent->s.eType == ET_INVISIBLE && ent->health <= GIB_HEALTH ) {
+ return;
+ }
+
// if client is in a nodrop area, don't leave the body
contents = trap_PointContents( ent->s.origin, -1 );
if ( contents & CONTENTS_NODROP ) {
@@ -450,12 +457,16 @@ void CopyToBodyQue( gentity_t *ent ) {
body->die = body_die;
+ // BFP - Attention: DLL & SO are prone to crash here, so setting body->takedamage as qfalse avoids that
+ body->takedamage = qfalse;
+#if 0
// don't take more damage if already gibbed
if ( ent->health <= GIB_HEALTH ) {
body->takedamage = qfalse;
} else {
body->takedamage = qtrue;
}
+#endif
VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
@@ -596,89 +607,6 @@ static void ForceClientSkin( gclient_t *client, char *model, const char *skin )
}
*/
-/*
-===========
-ClientCheckName
-============
-*/
-static void ClientCleanName( const char *in, char *out, int outSize ) {
- int len, colorlessLen;
- char ch;
- char *p;
- int spaces;
-
- //save room for trailing null byte
- outSize--;
-
- len = 0;
- colorlessLen = 0;
- p = out;
- *p = 0;
- spaces = 0;
-
- while( 1 ) {
- ch = *in++;
- if( !ch ) {
- break;
- }
-
- // don't allow leading spaces
- if( !*p && ch == ' ' ) {
- continue;
- }
-
- // check colors
- if( ch == Q_COLOR_ESCAPE ) {
- // solo trailing carat is not a color prefix
- if( !*in ) {
- break;
- }
-
- // don't allow black in a name, period
- if( ColorIndex(*in) == 0 ) {
- in++;
- continue;
- }
-
- // make sure room in dest for both chars
- if( len > outSize - 2 ) {
- break;
- }
-
- *out++ = ch;
- *out++ = *in++;
- len += 2;
- continue;
- }
-
- // don't allow too many consecutive spaces
- if( ch == ' ' ) {
- spaces++;
- if( spaces > 3 ) {
- continue;
- }
- }
- else {
- spaces = 0;
- }
-
- if( len > outSize - 1 ) {
- break;
- }
-
- *out++ = ch;
- colorlessLen++;
- len++;
- }
- *out = 0;
-
- // don't allow empty names
- if( *p == 0 || colorlessLen == 0 ) {
- Q_strncpyz( p, "UnnamedPlayer", outSize );
- }
-}
-
-
/*
===========
ClientUserInfoChanged
@@ -690,28 +618,42 @@ The game can override any of the settings and call trap_SetUserinfo
if desired.
============
*/
-void ClientUserinfoChanged( int clientNum ) {
+qboolean ClientUserinfoChanged( int clientNum ) {
gentity_t *ent;
- int teamTask, teamLeader, team, health;
+ team_t team;
+ // BFP - No handicap for health
+ // int health;
char *s;
char model[MAX_QPATH];
- char headModel[MAX_QPATH];
+ // BFP - Check model in list
+ char modelCheck[MAX_QPATH];
char oldname[MAX_STRING_CHARS];
gclient_t *client;
- char c1[MAX_INFO_STRING];
- char c2[MAX_INFO_STRING];
- char redTeam[MAX_INFO_STRING];
- char blueTeam[MAX_INFO_STRING];
+ // BFP - No color1
+ // char c1[MAX_INFO_STRING];
char userinfo[MAX_INFO_STRING];
+ // BFP - Model prefix load
+ char newModelPrefix[MAX_QPATH];
+ char *oldModelDash, *newModelDash;
+
ent = g_entities + clientNum;
client = ent->client;
trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
// check for malformed or illegal info strings
- if ( !Info_Validate(userinfo) ) {
- strcpy (userinfo, "\\name\\badinfo");
+ if ( !Info_Validate( userinfo ) ) {
+ Q_strcpy( ban_reason, "bad userinfo" );
+ if ( client && client->pers.connected != CON_DISCONNECTED )
+ trap_DropClient( clientNum, ban_reason );
+ return qfalse;
+ }
+
+ if ( client->pers.connected == CON_DISCONNECTED ) {
+ // we just checked if connecting player can join server
+ // so quit now as some important data like player team is still not set
+ return qtrue;
}
// check for local client
@@ -731,7 +673,7 @@ void ClientUserinfoChanged( int clientNum ) {
// set name
Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) );
s = Info_ValueForKey (userinfo, "name");
- ClientCleanName( s, client->pers.netname, sizeof(client->pers.netname) );
+ BG_CleanName( s, client->pers.netname, sizeof( client->pers.netname ), "UnnamedPlayer" );
if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
@@ -746,6 +688,8 @@ void ClientUserinfoChanged( int clientNum ) {
}
}
+ // BFP - No handicap
+#if 0
// set max health
health = atoi( Info_ValueForKey( userinfo, "handicap" ) );
client->pers.maxHealth = health;
@@ -753,14 +697,47 @@ void ClientUserinfoChanged( int clientNum ) {
client->pers.maxHealth = 100;
}
client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
+#endif
+
+ client->ps.stats[STAT_MAX_HEALTH] = 1 + client->ps.persistant[PERS_POWERLEVEL];
+ if ( client->ps.stats[STAT_MAX_HEALTH] > 1000 ) {
+ client->ps.stats[STAT_MAX_HEALTH] = 1000;
+ }
+
+ // BFP - Monster gamemode, double max health for player monster
+ if ( g_gametype.integer == GT_MONSTER
+ && level.monsterClientNum == clientNum
+ && ( client->ps.eFlags & EF_MONSTER ) ) {
+ client->ps.stats[STAT_MAX_HEALTH] *= 2;
+ if ( client->ps.stats[STAT_MAX_HEALTH] > 2000 ) {
+ client->ps.stats[STAT_MAX_HEALTH] = 2000;
+ }
+ }
// set model
- if( g_gametype.integer >= GT_TEAM ) {
- Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) );
- Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) );
- } else {
- Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) );
- Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) );
+ // BFP - Resolve player model when loading by prefix
+ G_ResolvePlayerModel( userinfo, model, model, sizeof( model ) );
+
+ // BFP - Kick/force to spectate the player who uses an illegal model which isn't available in the server
+ Q_strncpyz( modelCheck, G_GetPlayerModelName( clientNum, userinfo ), sizeof( modelCheck ) );
+ if ( !G_PlayerModelExistsOnServer( modelCheck )
+ && ( g_gametype.integer != GT_MONSTER
+ || ( g_gametype.integer == GT_MONSTER && g_monster.integer < 1 ) ) ) {
+#if KICK_ILLEGAL_PLAYER_MODEL
+ if ( client && client->pers.connected != CON_DISCONNECTED ) {
+ trap_DropClient( clientNum, "was kicked" );
+ }
+#else
+ gentity_t *tempEnt = G_TempEntity( ent->r.currentOrigin, EV_OBITUARY );
+ tempEnt->s.eventParm = MOD_ILLEGAL_PLAYER_MODEL;
+ tempEnt->r.svFlags = SVF_BROADCAST;
+ client->sess.sessionTeam = TEAM_SPECTATOR;
+ client->sess.spectatorState = SPECTATOR_FREE;
+ client->sess.spectatorClient = 0;
+ client->sess.teamLeader = qfalse;
+ ClientBegin( clientNum );
+#endif
+ return qfalse;
}
// bots set their team a few frames later
@@ -779,27 +756,6 @@ void ClientUserinfoChanged( int clientNum ) {
team = client->sess.sessionTeam;
}
-/* NOTE: all client side now
-
- // team
- switch( team ) {
- case TEAM_RED:
- ForceClientSkin(client, model, "red");
-// ForceClientSkin(client, headModel, "red");
- break;
- case TEAM_BLUE:
- ForceClientSkin(client, model, "blue");
-// ForceClientSkin(client, headModel, "blue");
- break;
- }
- // don't ever use a default skin in teamplay, it would just waste memory
- // however bots will always join a team but they spawn in as spectator
- if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) {
- ForceClientSkin(client, model, "red");
-// ForceClientSkin(client, headModel, "red");
- }
-*/
-
// teamInfo
s = Info_ValueForKey( userinfo, "teamoverlay" );
if ( ! *s || atoi( s ) != 0 ) {
@@ -807,47 +763,160 @@ void ClientUserinfoChanged( int clientNum ) {
} else {
client->pers.teamInfo = qfalse;
}
- /*
- s = Info_ValueForKey( userinfo, "cg_pmove_fixed" );
- if ( !*s || atoi( s ) == 0 ) {
- client->pers.pmoveFixed = qfalse;
- }
- else {
- client->pers.pmoveFixed = qtrue;
- }
- */
-
- // team task (0 = none, 1 = offence, 2 = defence)
- teamTask = atoi(Info_ValueForKey(userinfo, "teamtask"));
- // team Leader (1 = leader, 0 is normal player)
- teamLeader = client->sess.teamLeader;
+ // BFP - No color1
+#if 0
// colors
strcpy(c1, Info_ValueForKey( userinfo, "color1" ));
- strcpy(c2, Info_ValueForKey( userinfo, "color2" ));
-
- strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" ));
- strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" ));
+#endif
// send over a subset of the userinfo keys so other clients can
// print scoreboards, display models, and play custom sounds
if ( ent->r.svFlags & SVF_BOT ) {
- s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d",
- client->pers.netname, team, model, headModel, c1, c2,
- client->pers.maxHealth, client->sess.wins, client->sess.losses,
- Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader );
+ s = va("n\\%s\\t\\%i\\model\\%s\\w\\%i\\l\\%i\\skill\\%s",
+ client->pers.netname, team, model,
+ client->sess.wins, client->sess.losses,
+ Info_ValueForKey( userinfo, "skill" ) );
} else {
- s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\g_redteam\\%s\\g_blueteam\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d",
- client->pers.netname, client->sess.sessionTeam, model, headModel, redTeam, blueTeam, c1, c2,
- client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader);
+ s = va("n\\%s\\t\\%i\\model\\%s\\w\\%i\\l\\%i",
+ client->pers.netname, client->sess.sessionTeam, model,
+ client->sess.wins, client->sess.losses );
}
trap_SetConfigstring( CS_PLAYERS+clientNum, s );
+ // BFP - Model prefix handling
+ {
+ // extract model prefixes safely
+ oldModelDash = strchr(ent->oldModel, '-');
+ if ( oldModelDash ) {
+ Q_strncpyz( ent->oldModelPrefix, ent->oldModel, oldModelDash - ent->oldModel + 1 );
+ } else {
+ Q_strncpyz( ent->oldModelPrefix, ent->oldModel, sizeof( ent->oldModelPrefix ) );
+ }
+
+ newModelDash = strchr(model, '-');
+ if ( newModelDash ) {
+ Q_strncpyz( newModelPrefix, model, newModelDash - model + 1 );
+ } else {
+ Q_strncpyz( newModelPrefix, model, sizeof( newModelPrefix ) );
+ }
+
+ // compare model prefixes
+ if ( Q_stricmp( ent->oldModelPrefix, newModelPrefix )
+ && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { // only when the player is playing
+ // prefixes differ, kill the player
+ ent->flags &= ~FL_GODMODE;
+ ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
+ player_die( ent, ent, NULL, 100000, MOD_UNKNOWN );
+ }
+
+ // save the new model as the old model for the next time this function runs
+ Q_strncpyz( ent->oldModel, model, sizeof( ent->oldModel ) );
+ }
+
+ // BFP - Send powerlevel info to cgame reusing frame from entityState_t struct
+ ent->s.frame = client->ps.persistant[PERS_POWERLEVEL];
+
// this is not the userinfo, more like the configstring actually
G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s );
+
+ return qtrue;
}
+/*
+===========
+ClientGetAveragePowerlevel
+
+Get the average powerlevel when there are another players with
+different powerlevels.
+============
+*/
+static int ClientGetAveragePowerlevel( void ) { // BFP - Average powerlevel
+ int i = 0;
+ int totalPowerLevel = 0;
+ int activeClients = 0;
+
+ while ( i < level.numConnectedClients ) {
+ gentity_t *ent = &g_entities[level.sortedClients[i]];
+
+ if ( ent->inuse && ent->client ) {
+ totalPowerLevel += ent->client->ps.persistant[PERS_POWERLEVEL];
+ ++activeClients;
+ }
+
+ // BFP - Survival
+ if ( g_gametype.integer == GT_SURVIVAL
+ && ent->inuse && ent->client && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
+ // get the same powerlevel of the standing one for the sake of balance
+ // avoid division by zero
+ return ( activeClients > 0 ) ? ent->client->ps.persistant[PERS_POWERLEVEL] : 0;
+ }
+ ++i;
+ }
+
+ // avoid division by zero
+ return ( activeClients > 0 ) ? ( totalPowerLevel / activeClients ) : 0;
+}
+
+/*
+==================
+ClientBecomeMonster
+
+Sets the player monster status and properties.
+==================
+*/
+static void ClientBecomeMonster( gentity_t *ent ) { // BFP - Monster gamemode function to set the player monster status
+ // double health and max health
+ ent->client->ps.stats[STAT_MAX_HEALTH] *= 2;
+ if ( ent->client->ps.stats[STAT_MAX_HEALTH] > 2000 ) {
+ ent->client->ps.stats[STAT_MAX_HEALTH] = 2000;
+ }
+ ent->health = ent->client->ps.stats[STAT_HEALTH] = ent->client->ps.stats[STAT_MAX_HEALTH];
+
+ // double ki and max ki
+ ent->client->ps.stats[STAT_MAX_KI] *= 2;
+ ent->client->ps.stats[STAT_KI] = ent->client->ps.stats[STAT_MAX_KI];
+
+ ent->client->ps.eFlags |= EF_MONSTER;
+
+ trap_SendServerCommand( -1, va("print \"%s is the monster\n\"", ent->client->pers.netname) );
+}
+
+/*
+===========
+ClientCheckMonsterGone
+
+Check if the player monster is gone and set the monster
+to the most waited player.
+============
+*/
+void ClientCheckMonsterGone( gentity_t *ent ) { // BFP - Monster gamemode function check
+ if ( g_gametype.integer == GT_MONSTER
+ && ( ent->client->ps.eFlags & EF_MONSTER )
+ && ent->client->ps.clientNum == level.monsterClientNum ) {
+ qboolean becameMonster = qfalse;
+ int i;
+
+ ent->client->ps.eFlags &= ~EF_MONSTER;
+
+ for ( i = 0 ; i < level.maxclients ; ++i ) {
+ if ( g_entities[i].client->pers.connected == CON_CONNECTED
+ && g_entities[i].client->sess.sessionTeam != TEAM_SPECTATOR
+ && ent->client->ps.clientNum != g_entities[i].client->ps.clientNum ) {
+ level.monsterClientNum = g_entities[i].client->ps.clientNum;
+ g_entities[i].client->ps.eFlags |= EF_MONSTER;
+ becameMonster = qtrue;
+ respawn( &g_entities[i] );
+ break;
+ }
+ }
+ // a check to detect if no one is here, so reset the value, otherwise whoever joins won't become a monster
+ if ( !becameMonster ) {
+ level.monsterClientNum = -1;
+ }
+ }
+}
/*
===========
@@ -869,14 +938,40 @@ to the server machine, but qfalse on map changes and tournement
restarts.
============
*/
-char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) {
+const char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) {
char *value;
// char *areabits;
gclient_t *client;
char userinfo[MAX_INFO_STRING];
gentity_t *ent;
+ qboolean isAdmin = qfalse;
+
+ if ( clientNum >= level.maxclients ) {
+ return "Bad connection slot.";
+ }
ent = &g_entities[ clientNum ];
+ ent->client = level.clients + clientNum;
+
+ if ( firstTime ) {
+ // cleanup previous data manually
+ // because client may silently (re)connect without ClientDisconnect in case of crash for example
+ if ( level.clients[ clientNum ].pers.connected != CON_DISCONNECTED )
+ ClientDisconnect( clientNum );
+
+ // remove old entity from the world
+ trap_UnlinkEntity( ent );
+ ent->r.contents = 0;
+ ent->s.eType = ET_INVISIBLE;
+ ent->s.eFlags = 0;
+ ent->s.modelindex = 0;
+ ent->s.clientNum = clientNum;
+ ent->s.number = clientNum;
+ ent->takedamage = qfalse;
+ }
+
+ ent->r.svFlags &= ~SVF_BOT;
+ ent->inuse = qfalse;
trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
@@ -884,20 +979,24 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) {
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500
// recommanding PB based IP / GUID banning, the builtin system is pretty limited
// check to see if they are on the banned IP list
- value = Info_ValueForKey (userinfo, "ip");
- if ( G_FilterPacket( value ) ) {
+ value = Info_ValueForKey( userinfo, "ip" );
+
+ if ( !strcmp( value, "localhost" ) && !isBot )
+ isAdmin = qtrue;
+
+ if ( !isAdmin && G_FilterPacket( value ) ) {
return "You are banned from this server.";
}
- // we don't check password for bots and local client
- // NOTE: local client <-> "ip" "localhost"
- // this means this client is not running in our current process
- if ( !( ent->r.svFlags & SVF_BOT ) && (strcmp(value, "localhost") != 0)) {
+ // we don't check password for bots and local client
+ // NOTE: local client <-> "ip" "localhost"
+ // this means this client is not running in our current process
+ if ( !isBot && !isAdmin ) {
// check for a password
- value = Info_ValueForKey (userinfo, "password");
- if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) &&
- strcmp( g_password.string, value) != 0) {
- return "Invalid password";
+ if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) ) {
+ value = Info_ValueForKey( userinfo, "password" );
+ if ( strcmp( g_password.string, value ) )
+ return "Invalid password";
}
}
@@ -905,28 +1004,74 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) {
ent->client = level.clients + clientNum;
client = ent->client;
+ // BFP - Monster gamemode
+ if ( level.monsterClientNum == -1 ) {
+ // first player becomes the monster
+ level.monsterClientNum = clientNum;
+ }
+
// areabits = client->areabits;
memset( client, 0, sizeof(*client) );
+
+ // BFP - Monster gamemode
+ if ( clientNum == level.monsterClientNum ) {
+ client->ps.eFlags |= EF_MONSTER;
+ }
- client->pers.connected = CON_CONNECTING;
+ if ( !ClientUserinfoChanged( clientNum ) ) {
+ return ban_reason;
+ }
+
+ // BFP - Survival
+ if ( g_gametype.integer == GT_SURVIVAL ) {
+ // make sure the player isn't playing and must be spectator
+ // when the match is restarted or the map is changed
+ if ( !firstTime && client->sess.sessionTeam != TEAM_SPECTATOR ) {
+ client->sess.sessionTeam = TEAM_SPECTATOR;
+ // output "joined to ..." message
+ BroadcastTeamChange( client, -1 );
+ }
+
+ // output "joined to ..." message
+ if ( level.newSession ) {
+ BroadcastTeamChange( client, -1 );
+ }
+
+ // make sure the player score isn't negative when being connected at that moment
+ if ( client->ps.persistant[PERS_SCORE] < 0 ) {
+ client->ps.persistant[PERS_SCORE] = 0;
+ }
+ }
+
+ // BFP - Team Last Man Standing
+ if ( g_gametype.integer == GT_TLMS ) {
+ client->forceToSpectate = qfalse;
+ }
// read or initialize the session data
if ( firstTime || level.newSession ) {
- G_InitSessionData( client, userinfo );
+ value = Info_ValueForKey( userinfo, "team" );
+ G_InitSessionData( client, value, isBot );
+ G_WriteClientSessionData( client );
}
- G_ReadSessionData( client );
+
+ G_ReadClientSessionData( client );
if( isBot ) {
- ent->r.svFlags |= SVF_BOT;
- ent->inuse = qtrue;
if( !G_BotConnect( clientNum, !firstTime ) ) {
return "BotConnectfailed";
}
+ ent->r.svFlags |= SVF_BOT;
+ client->sess.spectatorClient = clientNum;
}
+ ent->inuse = qtrue;
- // get and distribute relevent paramters
+ // get and distribute relevant paramters
G_LogPrintf( "ClientConnect: %i\n", clientNum );
+
+ client->pers.connected = CON_CONNECTING;
+
ClientUserinfoChanged( clientNum );
// don't do the "xxx connected" messages if they were caried over from previous level
@@ -934,11 +1079,6 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) {
trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname) );
}
- if ( g_gametype.integer >= GT_TEAM &&
- client->sess.sessionTeam != TEAM_SPECTATOR ) {
- BroadcastTeamChange( client, -1 );
- }
-
// count current clients and rank for scoreboard
CalculateRanks();
@@ -964,6 +1104,8 @@ void ClientBegin( int clientNum ) {
gclient_t *client;
gentity_t *tent;
int flags;
+ // BFP - Survival, save scores if the players are spectating
+ int savedScore;
ent = g_entities + clientNum;
@@ -980,6 +1122,8 @@ void ClientBegin( int clientNum ) {
client->pers.connected = CON_CONNECTED;
client->pers.enterTime = level.time;
client->pers.teamState.state = TEAM_BEGIN;
+ // BFP - Survival, save scores if the players are spectating
+ savedScore = client->ps.persistant[PERS_SCORE];
// save eflags around this, because changing teams will
// cause this to happen with a valid entity, and we
@@ -990,15 +1134,50 @@ void ClientBegin( int clientNum ) {
memset( &client->ps, 0, sizeof( client->ps ) );
client->ps.eFlags = flags;
+ // BFP - Monster gamemode
+ if ( level.monsterClientNum == -1 ) {
+ // first player becomes the monster
+ level.monsterClientNum = clientNum;
+ }
+ if ( clientNum == level.monsterClientNum ) {
+ client->ps.eFlags |= EF_MONSTER;
+ }
+
+ // BFP - Survival, save scores if the players are spectating
+ if ( g_gametype.integer == GT_SURVIVAL ) {
+ client->ps.persistant[PERS_SCORE] = savedScore;
+ }
+
// locate ent at a spawn point
ClientSpawn( ent );
+ // BFP - Kick the player who uses an illegal player model which isn't in the server
+#if KICK_ILLEGAL_PLAYER_MODEL
+ {
+ char userinfo[MAX_INFO_STRING];
+ char model[MAX_QPATH];
+
+ trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
+ Q_strncpyz( model, G_GetPlayerModelName( clientNum, userinfo ), sizeof( model ) );
+
+ if ( !G_PlayerModelExistsOnServer( model )
+ && ( g_gametype.integer != GT_MONSTER
+ || ( g_gametype.integer == GT_MONSTER && g_monster.integer < 1 ) ) ) {
+ if ( client && client->pers.connected != CON_DISCONNECTED ) {
+ trap_DropClient( clientNum, "was kicked" );
+ }
+ return;
+ }
+ }
+#endif
+
if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
// send event
tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN );
tent->s.clientNum = ent->s.clientNum;
- if ( g_gametype.integer != GT_TOURNAMENT ) {
+ if ( g_gametype.integer != GT_TOURNAMENT
+ && g_gametype.integer != GT_SURVIVAL ) { // BFP - Survival
trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname) );
}
}
@@ -1028,6 +1207,9 @@ void ClientSpawn(gentity_t *ent) {
gentity_t *spawnPoint;
int flags;
int savedPing;
+ // BFP - Team Last Man Standing, save force to spectate and selected team
+ qboolean savedForcedToSpectate;
+ team_t savedSelectedTeam;
// char *savedAreaBits;
int accuracy_hits, accuracy_shots;
int eventSequence;
@@ -1051,6 +1233,12 @@ void ClientSpawn(gentity_t *ent) {
spawn_origin, spawn_angles);
} else {
do {
+ // BFP - Monster gamemode, the player monster spawns at the spectator spawn point, kinda curious (¬_¬)
+ if ( g_gametype.integer == GT_MONSTER
+ && ( client->ps.eFlags & EF_MONSTER ) ) {
+ spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles );
+ break;
+ }
// the first spawn should be at a good looking spot
if ( !client->pers.initialSpawn && client->pers.localClient ) {
client->pers.initialSpawn = qtrue;
@@ -1079,9 +1267,6 @@ void ClientSpawn(gentity_t *ent) {
}
client->pers.teamState.state = TEAM_ACTIVE;
- // always clear the kamikaze flag
- ent->s.eFlags &= ~EF_KAMIKAZE;
-
// toggle the teleport bit so the client knows to not lerp
// and never clear the voted flag
flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
@@ -1091,6 +1276,10 @@ void ClientSpawn(gentity_t *ent) {
saved = client->pers;
savedSess = client->sess;
+ // BFP - Team Last Man Standing, save force to spectate and selected team
+ savedForcedToSpectate = client->forceToSpectate;
+ savedSelectedTeam = client->selectedTeam;
+
savedPing = client->ps.ping;
// savedAreaBits = client->areabits;
accuracy_hits = client->accuracy_hits;
@@ -1100,10 +1289,14 @@ void ClientSpawn(gentity_t *ent) {
}
eventSequence = client->ps.eventSequence;
- memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset?
+ Com_Memset (client, 0, sizeof(*client));
client->pers = saved;
client->sess = savedSess;
+ // BFP - Team Last Man Standing, keep force to spectate and selected team
+ client->forceToSpectate = savedForcedToSpectate;
+ client->selectedTeam = savedSelectedTeam;
+
client->ps.ping = savedPing;
// client->areabits = savedAreaBits;
client->accuracy_hits = accuracy_hits;
@@ -1118,18 +1311,23 @@ void ClientSpawn(gentity_t *ent) {
client->ps.persistant[PERS_SPAWN_COUNT]++;
client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
- client->airOutTime = level.time + 12000;
+ // BFP - No drowning
+ // client->airOutTime = level.time + 12000;
trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
+ // BFP - No handicap
+#if 0
// set max health
client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) );
if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
client->pers.maxHealth = 100;
}
- // clear entity values
client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
+#endif
+
client->ps.eFlags = flags;
+ // clear entity values
ent->s.groundEntityNum = ENTITYNUM_NONE;
ent->client = &level.clients[index];
ent->takedamage = qtrue;
@@ -1147,48 +1345,105 @@ void ClientSpawn(gentity_t *ent) {
client->ps.clientNum = index;
- // TODO: BFP - list of 5 skills
+ // BFP - TODO: list of 5 skills
- client->ps.stats[STAT_KI] = 1000; // BFP - give ki at start
+ // BFP - Powerlevel start
+ if ( client->ps.persistant[PERS_POWERLEVEL] < g_basePL.integer ) {
+ client->ps.persistant[PERS_POWERLEVEL] = g_basePL.integer;
+ } else {
+ if ( level.numConnectedClients > 1 ) {
+ client->ps.persistant[PERS_POWERLEVEL] = ClientGetAveragePowerlevel();
+ }
+ }
+ if ( client->ps.persistant[PERS_POWERLEVEL] > 1000 || g_basePL.integer > 998 ) {
+ client->ps.persistant[PERS_POWERLEVEL] = 1000;
+ }
- client->ps.stats[STAT_WEAPONS] = ( 1 << WP_MACHINEGUN );
- client->ps.ammo[WP_MACHINEGUN] = 100;
+ // BFP - Max spawn powerlevel, only when g_maxSpawnPL is higher than 0
+ if ( g_maxSpawnPL.integer > 0 && client->ps.persistant[PERS_POWERLEVEL] > g_maxSpawnPL.integer
+ && g_basePL.integer < 999 ) {
+ client->ps.persistant[PERS_POWERLEVEL] = g_maxSpawnPL.integer;
+ }
- client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ROCKET_LAUNCHER );
- client->ps.ammo[WP_ROCKET_LAUNCHER] = 100;
+ // BFP - Team Last Man Standing, the powerlevel always starts at the maximum and reset spectating reason
+ if ( g_gametype.integer == GT_TLMS ) {
+ client->ps.persistant[PERS_POWERLEVEL] = 1000;
+ }
+ if ( client->forceToSpectate <= 0 ) {
+ client->forceToSpectate = qfalse;
+ }
- client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_PLASMAGUN );
- client->ps.ammo[WP_PLASMAGUN] = 100;
-
- client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_RAILGUN );
- client->ps.ammo[WP_RAILGUN] = 100;
+ // BFP - Send powerlevel info to cgame reusing frame from entityState_t struct
+ ent->s.frame = client->ps.persistant[PERS_POWERLEVEL];
- client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BFG );
- client->ps.ammo[WP_BFG] = 100;
+ // BFP - Max health start
+ client->ps.stats[STAT_MAX_HEALTH] = 1 + client->ps.persistant[PERS_POWERLEVEL];
+ if ( client->ps.stats[STAT_MAX_HEALTH] > 1000 ) {
+ client->ps.stats[STAT_MAX_HEALTH] = 1000;
+ }
- // TODO: BFP - Gauntlet must be replaced as a bind key (Alt key),
- // it will use fight animation (using kicks and fists)
+ // BFP - Ki start
+ client->ps.stats[STAT_KI] = 999;
+ // BFP - NOTE: What the heck? Did BFP dev make this multiplying 9.00825 with powerlevel? Strange approximation...
+ client->ps.stats[STAT_KI] = client->ps.stats[STAT_KI] + ( 9.00825 * client->ps.persistant[PERS_POWERLEVEL] );
+ client->ps.stats[STAT_MAX_KI] = client->ps.stats[STAT_KI];
- client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET );
- client->ps.ammo[WP_GAUNTLET] = -1;
- client->ps.ammo[WP_GRAPPLING_HOOK] = -1;
+ if ( client->ps.stats[STAT_MAX_KI] > 10000 ) {
+ client->ps.stats[STAT_MAX_KI] = client->ps.stats[STAT_KI] = 10000;
+ }
+
+ // BFP - Monster gamemode
+ if ( g_gametype.integer == GT_MONSTER && g_monster.integer > 0
+ && client->ps.clientNum == level.monsterClientNum ) {
+ client->ps.stats[STAT_WEAPONS] = ( 1 << WP_GRAPPLING_HOOK );
+ client->ps.ammo[WP_GRAPPLING_HOOK] = 100;
+ } else {
+ client->ps.stats[STAT_WEAPONS] = ( 1 << WP_MACHINEGUN );
+ client->ps.ammo[WP_MACHINEGUN] = 100;
+
+ client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ROCKET_LAUNCHER );
+ client->ps.ammo[WP_ROCKET_LAUNCHER] = 100;
+
+ client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_PLASMAGUN );
+ client->ps.ammo[WP_PLASMAGUN] = 100;
+
+ client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_RAILGUN );
+ client->ps.ammo[WP_RAILGUN] = 100;
+
+ client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BFG );
+ client->ps.ammo[WP_BFG] = 100;
+
+ //client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET );
+ //client->ps.ammo[WP_GAUNTLET] = -1;
+ //client->ps.ammo[WP_GRAPPLING_HOOK] = -1;
+ client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GRAPPLING_HOOK );
+ client->ps.ammo[WP_GRAPPLING_HOOK] = 100;
+ }
// health will count down towards max_health
- ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25;
+ ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH]; // BFP - Before Q3: + 25
- ent->kiamount = client->ps.stats[STAT_KI];
+ // BFP - Monster gamemode
+ if ( g_gametype.integer == GT_MONSTER && client->ps.clientNum == level.monsterClientNum
+ && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
+ ClientBecomeMonster( ent );
+ }
G_SetOrigin( ent, spawn_origin );
VectorCopy( spawn_origin, client->ps.origin );
// the respawned flag will be cleared after the attack and jump keys come up
client->ps.pm_flags |= PMF_RESPAWNED;
+ client->ps.viewheight = DEFAULT_VIEWHEIGHT;
+ client->ps.gravity = g_gravity.integer;
+ client->ps.speed = g_speed.integer;
- trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
+ trap_GetUsercmd( client - level.clients, &client->pers.cmd );
SetClientViewAngle( ent, spawn_angles );
if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
-
+ // BFP - Monster gamemode, if this guy spectated, respawn the other guy who can become a monster
+ ClientCheckMonsterGone( ent );
} else {
G_KillBox( ent );
trap_LinkEntity (ent);
@@ -1298,13 +1553,17 @@ void ClientDisconnect( int clientNum ) {
G_LogPrintf( "ClientDisconnect: %i\n", clientNum );
// if we are playing in tourney mode and losing, give a win to the other player
- if ( (g_gametype.integer == GT_TOURNAMENT )
+ if ( ( g_gametype.integer == GT_TOURNAMENT
+ || g_gametype.integer == GT_SURVIVAL ) // BFP - Survival
&& !level.intermissiontime
&& !level.warmupTime && level.sortedClients[1] == clientNum ) {
level.clients[ level.sortedClients[0] ].sess.wins++;
ClientUserinfoChanged( level.sortedClients[0] );
}
+ // BFP - Monster gamemode, if this guy disconnected, respawn the other guy who can become a monster
+ ClientCheckMonsterGone( ent );
+
trap_UnlinkEntity (ent);
ent->s.modelindex = 0;
ent->inuse = qfalse;
diff --git a/source/game/g_cmds.c b/source/game/g_cmds.c
index a219664..d9006ae 100644
--- a/source/game/g_cmds.c
+++ b/source/game/g_cmds.c
@@ -24,6 +24,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "menudef.h" // for the voice chats
+// BFP - BFP tournament allow chat macro
+#define BFP_TOURNAMENT_CHAT_NOT_ALLOWED 0
+
/*
==================
DeathmatchScoreboardMessage
@@ -64,6 +67,8 @@ void DeathmatchScoreboardMessage( gentity_t *ent ) {
}
perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;
+ // BFP - No impressive, gauntlet, defend, assist and capture counters
+#if 0
Com_sprintf (entry, sizeof(entry),
" %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i],
cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000,
@@ -75,6 +80,18 @@ void DeathmatchScoreboardMessage( gentity_t *ent ) {
cl->ps.persistant[PERS_ASSIST_COUNT],
perfect,
cl->ps.persistant[PERS_CAPTURES]);
+#endif
+ Com_sprintf (entry, sizeof(entry),
+ " %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i],
+ cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000,
+ scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy,
+ 0,
+ cl->ps.persistant[PERS_EXCELLENT_COUNT],
+ 0,
+ 0,
+ 0,
+ perfect,
+ 0);
j = (int)strlen(entry);
if (stringlength + j > 1024)
break;
@@ -264,9 +281,14 @@ void Cmd_Give_f (gentity_t *ent)
if (give_all || Q_stricmp(name, "ammo") == 0)
{
+ // BFP - TODO: Don't give all ammo to all weapons, that part is supposed to be removed in the future
+#if 1
for ( i = 0 ; i < MAX_WEAPONS ; i++ ) {
ent->client->ps.ammo[i] = 999;
}
+#endif
+ ent->client->ps.stats[STAT_KI] = ent->client->ps.stats[STAT_MAX_KI];
+
if (!give_all)
return;
}
@@ -283,6 +305,8 @@ void Cmd_Give_f (gentity_t *ent)
ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
return;
}
+ // BFP - No impressive, gauntlet, defend and assist counters
+#if 0
if (Q_stricmp(name, "impressive") == 0) {
ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
return;
@@ -299,6 +323,7 @@ void Cmd_Give_f (gentity_t *ent)
ent->client->ps.persistant[PERS_ASSIST_COUNT]++;
return;
}
+#endif
// spawn a specific item right on the player
if ( !give_all ) {
@@ -571,7 +596,8 @@ void SetTeam( gentity_t *ent, char *s ) {
}
// override decision if limiting the players
- if ( (g_gametype.integer == GT_TOURNAMENT)
+ if ( ( g_gametype.integer == GT_TOURNAMENT
+ || g_gametype.integer == GT_SURVIVAL ) // BFP - Survival
&& level.numNonSpectatorClients >= 2 ) {
team = TEAM_SPECTATOR;
} else if ( g_maxGameClients.integer > 0 &&
@@ -579,6 +605,13 @@ void SetTeam( gentity_t *ent, char *s ) {
team = TEAM_SPECTATOR;
}
+ // BFP - Monster gamemode, check if the player monster is changing teams
+ if ( g_gametype.integer == GT_MONSTER
+ && ( ent->client->ps.eFlags & EF_MONSTER )
+ && team == TEAM_SPECTATOR ) {
+ ClientCheckMonsterGone( ent );
+ }
+
//
// decide if we will allow the change
//
@@ -644,12 +677,45 @@ to free floating spectator mode
=================
*/
void StopFollowing( gentity_t *ent ) {
- ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
- ent->client->sess.sessionTeam = TEAM_SPECTATOR;
- ent->client->sess.spectatorState = SPECTATOR_FREE;
- ent->client->ps.pm_flags &= ~PMF_FOLLOW;
- ent->r.svFlags &= ~SVF_BOT;
- ent->client->ps.clientNum = ent - g_entities;
+ gclient_t *client;
+
+ if ( ( ent->r.svFlags & SVF_BOT ) || !ent->inuse )
+ return;
+
+ client = ent->client;
+
+ client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
+ client->sess.sessionTeam = TEAM_SPECTATOR;
+ SetClientViewAngle( ent, client->ps.viewangles );
+
+ client->sess.spectatorState = SPECTATOR_FREE;
+ client->ps.pm_flags &= ~PMF_FOLLOW;
+ //ent->r.svFlags &= ~SVF_BOT;
+
+ client->ps.clientNum = ent - g_entities;
+}
+
+
+/*
+=================
+StopAndSpectateFreely
+
+Stops following and spectate freely
+=================
+*/
+static void StopAndSpectateFreely( gentity_t *ent ) { // BFP - Stops following and spectate freely
+ vec3_t currentOrigin, currentAngles;
+ if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
+ // keep the origin and angles after stop following
+ VectorCopy( ent->client->ps.origin, currentOrigin );
+ currentOrigin[2] += 20;
+ VectorCopy( ent->client->ps.viewangles, currentAngles );
+
+ StopFollowing( ent );
+
+ VectorCopy( currentOrigin, ent->client->ps.origin );
+ VectorCopy( currentAngles, ent->client->ps.viewangles );
+ }
}
/*
@@ -680,22 +746,76 @@ void Cmd_Team_f( gentity_t *ent ) {
return;
}
+ // BFP - No switch team time delay
+#if BFP_NO_SWITCH_TEAM_TIME
if ( ent->client->switchTeamTime > level.time ) {
trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" );
return;
}
+#endif
// if they are playing a tournement game, count as a loss
- if ( (g_gametype.integer == GT_TOURNAMENT )
+ if ( ( g_gametype.integer == GT_TOURNAMENT
+ || g_gametype.integer == GT_SURVIVAL ) // BFP - Survival
&& ent->client->sess.sessionTeam == TEAM_FREE ) {
ent->client->sess.losses++;
}
trap_Argv( 1, s, sizeof( s ) );
+ // BFP - Kick/force to spectate the player who uses an illegal model which isn't available in the server
+ {
+ char model[MAX_QPATH];
+ char userinfo[MAX_INFO_STRING];
+
+ trap_GetUserinfo( ent->client->ps.clientNum, userinfo, sizeof( userinfo ) );
+ Q_strncpyz( model, G_GetPlayerModelName( ent->client->ps.clientNum, userinfo ), sizeof( model ) );
+
+ if ( !G_PlayerModelExistsOnServer( model )
+ && ( g_gametype.integer != GT_MONSTER
+ || ( g_gametype.integer == GT_MONSTER && g_monster.integer < 1 ) ) ) {
+#if KICK_ILLEGAL_PLAYER_MODEL
+ if ( ent->client && ent->client->pers.connected != CON_DISCONNECTED ) {
+ trap_DropClient( ent->client->ps.clientNum, "was kicked" );
+ }
+#else
+ gentity_t *tempEnt = G_TempEntity( ent->r.currentOrigin, EV_OBITUARY );
+ tempEnt->s.eventParm = MOD_ILLEGAL_PLAYER_MODEL;
+ tempEnt->r.svFlags = SVF_BROADCAST;
+ ent->client->sess.sessionTeam = TEAM_SPECTATOR;
+ ent->client->sess.spectatorState = SPECTATOR_FREE;
+ ent->client->sess.spectatorClient = 0;
+ ClientBegin( ent->client->ps.clientNum );
+#endif
+ return;
+ }
+ }
+
+ // BFP - Team Last Man Standing, show a centerprint message to switching teams when the player were fragged and forced to spectate
+ if ( g_gametype.integer == GT_TLMS
+ && ent->client->forceToSpectate ) {
+ trap_SendServerCommand( ent->client->ps.clientNum,
+ "cp \"You are eliminated!\nWait until the round ends to respawn with your team!\n\"" );
+ return;
+ }
+
+ // BFP - Team Last Man Standing, show a centerprint message to switching teams when joined
+ if ( g_gametype.integer == GT_TLMS
+ && ( ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE )
+ && ent->client->sess.sessionTeam != TEAM_SPECTATOR
+ && !ent->client->forceToSpectate ) {
+ trap_SendServerCommand( ent->client->ps.clientNum,
+ "cp \"You cannot switch teams during the round!\n\""
+ );
+ return;
+ }
+
SetTeam( ent, s );
+ // BFP - No switch team time delay
+#if BFP_NO_SWITCH_TEAM_TIME
ent->client->switchTeamTime = level.time + 5000;
+#endif
}
@@ -732,7 +852,8 @@ void Cmd_Follow_f( gentity_t *ent ) {
}
// if they are playing a tournement game, count as a loss
- if ( (g_gametype.integer == GT_TOURNAMENT )
+ if ( ( g_gametype.integer == GT_TOURNAMENT
+ || g_gametype.integer == GT_SURVIVAL ) // BFP - Survival
&& ent->client->sess.sessionTeam == TEAM_FREE ) {
ent->client->sess.losses++;
}
@@ -756,7 +877,8 @@ void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
int original;
// if they are playing a tournement game, count as a loss
- if ( (g_gametype.integer == GT_TOURNAMENT )
+ if ( ( g_gametype.integer == GT_TOURNAMENT
+ || g_gametype.integer == GT_SURVIVAL ) // BFP - Survival
&& ent->client->sess.sessionTeam == TEAM_FREE ) {
ent->client->sess.losses++;
}
@@ -769,15 +891,24 @@ void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
}
- clientnum = ent->client->sess.spectatorClient;
+ // BFP - If currently in free spectator mode, start following from the beginning
+ if ( ent->client->sess.spectatorState == SPECTATOR_FREE ) {
+ clientnum = ( dir == 1 ) ? -1 : level.maxclients;
+ } else {
+ clientnum = ent->client->sess.spectatorClient;
+ }
original = clientnum;
do {
clientnum += dir;
if ( clientnum >= level.maxclients ) {
- clientnum = 0;
+ // BFP - Reached the end going forward - stop following
+ StopAndSpectateFreely( ent );
+ return;
}
if ( clientnum < 0 ) {
- clientnum = level.maxclients - 1;
+ // BFP - Reached the beginning going backward - stop following
+ StopAndSpectateFreely( ent );
+ return;
}
// can only follow connected clients
@@ -822,12 +953,15 @@ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, cons
if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) {
return;
}
+ // BFP - Chatting is allowed on BFP tournaments
+#if BFP_TOURNAMENT_CHAT_NOT_ALLOWED
// no chatting to players in tournements
if ( (g_gametype.integer == GT_TOURNAMENT )
&& other->client->sess.sessionTeam == TEAM_FREE
&& ent->client->sess.sessionTeam != TEAM_FREE ) {
return;
}
+#endif
trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"",
mode == SAY_TEAM ? "tchat" : "chat",
@@ -845,6 +979,12 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )
char text[MAX_SAY_TEXT];
char location[64];
+ // BFP - Allow spectator chat
+ if ( ent->client->ps.pm_type == PM_SPECTATOR
+ && g_allowSpectatorChat.integer <= 0 ) {
+ return;
+ }
+
if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
mode = SAY_ALL;
}
@@ -975,10 +1115,13 @@ static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *i
if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) {
return;
}
+ // BFP - Chatting is allowed on BFP tournaments
+#if BFP_TOURNAMENT_CHAT_NOT_ALLOWED
// no chatting to players in tournements
if ( (g_gametype.integer == GT_TOURNAMENT )) {
return;
}
+#endif
if (mode == SAY_TEAM) {
color = COLOR_CYAN;
@@ -1200,11 +1343,11 @@ static const char *gameNames[] = {
"Free For All",
"Tournament",
"Single Player",
+ "Survival",
+ "Monster",
"Team Deathmatch",
- "Capture the Flag",
- "One Flag CTF",
- "Overload",
- "Harvester"
+ "Team Last Man Standing",
+ "Capture the Flag"
};
/*
@@ -1253,9 +1396,14 @@ void Cmd_CallVote_f( gentity_t *ent ) {
} else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {
} else if ( !Q_stricmp( arg1, "timelimit" ) ) {
} else if ( !Q_stricmp( arg1, "fraglimit" ) ) {
+ } else if ( !Q_stricmp( arg1, "g_basePL" ) ) { // BFP - Base powerlevel
+ } else if ( !Q_stricmp( arg1, "g_hitStun" ) ) { // BFP - Hit stun
+ } else if ( !Q_stricmp( arg1, "g_allowSpectatorChat" ) ) { // BFP - Allow spectator chat
+ } else if ( !Q_stricmp( arg1, "g_meleeOnly" ) ) { // BFP - Melee only
+ } else if ( !Q_stricmp( arg1, "g_noFlight" ) ) { // BFP - No flight
} else {
trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
- trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , g_gametype , kick , clientkick , g_doWarmup, timelimit , fraglimit .\n\"" );
+ trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , g_gametype , kick , clientkick , g_doWarmup, timelimit , fraglimit , g_basePL , g_hitStun <0/1>, g_allowSpectatorChat <0/1>, g_meleeOnly <0/1>, g_noFlight <0/1>.\n\"" );
return;
}
@@ -1597,14 +1745,54 @@ Cmd_BFP_Fly_f
*/
void Cmd_BFP_Fly_f( gentity_t* ent ) { // BFP - Flight
+ // BFP - No flight
+ if ( g_noFlight.integer > 0 ) {
+ return;
+ }
+
if ( ent->client->ps.pm_type != PM_DEAD ) {
- if ( !( ent->client->ps.pm_flags & PMF_FLYING ) ) {
- G_AddEvent( ent, EV_JUMP, 0 ); // play jump sound
+ // do not play the sound in the charging status
+ if ( !( ent->client->ps.eFlags & EF_FLIGHT ) && !( ent->client->ps.pm_flags & PMF_KI_CHARGE ) ) {
+ BG_AddPredictableEventToPlayerstate( EV_ENABLE_FLIGHT, 0, &ent->client->ps, -1 ); // play the sound
}
- ent->client->ps.pm_flags ^= PMF_FLYING;
+ ent->client->ps.eFlags ^= EF_FLIGHT;
}
}
+/*
+=====================
+Cmd_BFP_SetKiCharge_f
+=====================
+*/
+void Cmd_BFP_SetKiCharge_f( gentity_t* ent ) { // BFP - Set Ki charge
+ // BFP - NOTE: Charging only when ki use has been toggled,
+ // but it's a charging pose animation only,
+ // what were they (original BFP devs) thinking?
+
+ // Nah, that implementation doesn't make sense,
+ // if you want silly and useless stuff like that, play it in original BFP
+ if ( ent->client->ps.pm_type != PM_DEAD
+ && ( ent->client->ps.eFlags & EF_AURA ) ) {
+ ent->client->ps.torsoTimer = ent->client->ps.legsTimer = 0;
+ ent->client->ps.legsAnim = LEGS_CHARGE;
+ ent->client->ps.torsoAnim = TORSO_CHARGE;
+ }
+}
+
+/*
+=====================
+Cmd_BFP_SetKiUse_f
+=====================
+*/
+void Cmd_BFP_SetKiUse_f( gentity_t* ent ) { // BFP - Set Ki use
+ // BFP - NOTE: Originally, an unfinished command...
+#if 0
+ if ( ent->client->ps.pm_type != PM_DEAD ) {
+ ent->client->ps.eFlags |= EF_KI_BOOST;
+ }
+#endif
+}
+
/*
=====================
Cmd_BFP_KiUseToggle_f
@@ -1613,32 +1801,62 @@ Cmd_BFP_KiUseToggle_f
void Cmd_BFP_KiUseToggle_f( gentity_t* ent ) { // BFP - Ki use toggle
if ( ent->client->ps.pm_type != PM_DEAD ) {
- ent->client->ps.pm_flags ^= PMF_KI_BOOST;
+ ent->client->ps.eFlags ^= EF_KI_BOOST;
}
}
-void Cmd_BFP_SetKiIdle_f( gentity_t* ent ) { // BFP - TODO: Set Ki idle
- Com_Printf( "Cmd_BFP_SetKiIdle_f\n" );
-}
+/*
+=====================
+Cmd_BFP_SetKiIdle_f
+=====================
+*/
+void Cmd_BFP_SetKiIdle_f( gentity_t* ent ) { // BFP - Set Ki idle
-void Cmd_BFP_SelectCharacter_f( gentity_t* ent ) { // BFP - TODO: Select character
- Com_Printf( "Cmd_BFP_SelectCharacter_f\n" );
+ // BFP - NOTE: originally... Ki idling means disabling the ki? Sounds like it isn't operating...
+ if ( ent->client->ps.pm_type != PM_DEAD ) {
+ ent->client->ps.eFlags &= ~EF_KI_BOOST;
+ }
}
-void Cmd_BFP_StartBlock_f( gentity_t* ent ) { // BFP - TODO: Start block
- Com_Printf( "Cmd_BFP_StartBlock_f\n" );
+/*
+=====================
+Cmd_BFP_SelectCharacter_f
+=====================
+*/
+void Cmd_BFP_SelectCharacter_f( gentity_t* ent ) { // BFP - Select character
+ char characterselected[MAX_TOKEN_CHARS];
+
+ // BFP - NOTE: That command was left without finishing the implementation to change the character of this way
+ // What could it be?
+
+ if ( trap_Argc() != 2 ) {
+ return;
+ }
+ trap_Argv( 1, characterselected, sizeof( characterselected ) );
+
+ // that prints the info in the server
+ G_Printf( "Character: %s\n", characterselected );
}
-void Cmd_BFP_StopBlock_f( gentity_t* ent ) { // BFP - TODO: Stop block
- Com_Printf( "Cmd_BFP_StopBlock_f\n" );
+/*
+=================
+Cmd_BFP_Block_f
+=================
+*/
+void Cmd_BFP_Block_f( gentity_t* ent ) { // BFP - Block
+
+ if ( ent->client->ps.pm_type != PM_DEAD ) {
+ ent->client->ps.pm_flags |= PMF_BLOCK;
+ ent->client->blockTime = level.time + (g_blockLength.integer * 1000);
+ }
}
-void Cmd_BFP_StartMelee_f( gentity_t* ent ) { // BFP - TODO: Start melee
- Com_Printf( "Cmd_BFP_StartMelee_f\n" );
+void Cmd_BFP_StartMelee_f( gentity_t* ent ) { // BFP - Start melee
+ // BFP - NOTE: That command was left without finishing the implementation to start melee
}
-void Cmd_BFP_StopMelee_f( gentity_t* ent ) { // BFP - TODO: Stop melee
- Com_Printf( "Cmd_BFP_StopMelee_f\n" );
+void Cmd_BFP_StopMelee_f( gentity_t* ent ) { // BFP - Stop melee
+ // BFP - NOTE: That command was left without finishing the implementation to stop melee
}
@@ -1751,19 +1969,21 @@ void ClientCommand( int clientNum ) {
Cmd_Stats_f( ent );
else if (Q_stricmp (cmd, "fly") == 0) // BFP - Flight
Cmd_BFP_Fly_f( ent );
+ else if (Q_stricmp (cmd, "set_ki_charge") == 0) // BFP - Set Ki charge
+ Cmd_BFP_SetKiCharge_f( ent );
+ else if (Q_stricmp (cmd, "set_ki_use") == 0) // BFP - Set Ki use
+ Cmd_BFP_SetKiUse_f( ent );
else if (Q_stricmp (cmd, "kiusetoggle") == 0) // BFP - Ki use toggle
Cmd_BFP_KiUseToggle_f( ent );
- else if (Q_stricmp (cmd, "set_ki_idle") == 0) // BFP - TODO: Set Ki idle
+ else if (Q_stricmp (cmd, "set_ki_idle") == 0) // BFP - Set Ki idle
Cmd_BFP_SetKiIdle_f( ent );
- else if (Q_stricmp (cmd, "selectcharacter") == 0) // BFP - TODO: Select character
+ else if (Q_stricmp (cmd, "selectcharacter") == 0) // BFP - Select character
Cmd_BFP_SelectCharacter_f( ent );
- else if (Q_stricmp (cmd, "start_block") == 0) // BFP - TODO: Start Block
- Cmd_BFP_StartBlock_f( ent );
- else if (Q_stricmp (cmd, "stop_block") == 0) // BFP - TODO: Stop block
- Cmd_BFP_StopBlock_f( ent );
- else if (Q_stricmp (cmd, "start_melee") == 0) // BFP - TODO: Start melee
+ else if (Q_stricmp (cmd, "block") == 0) // BFP - Block
+ Cmd_BFP_Block_f( ent );
+ else if (Q_stricmp (cmd, "start_melee") == 0) // BFP - Start melee
Cmd_BFP_StartMelee_f( ent );
- else if (Q_stricmp (cmd, "stop_melee") == 0) // BFP - TODO: Stop melee
+ else if (Q_stricmp (cmd, "stop_melee") == 0) // BFP - Stop melee
Cmd_BFP_StopMelee_f( ent );
else
trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
diff --git a/source/game/g_combat.c b/source/game/g_combat.c
index cc999a7..9dd559c 100644
--- a/source/game/g_combat.c
+++ b/source/game/g_combat.c
@@ -61,7 +61,8 @@ void AddScore( gentity_t *ent, vec3_t origin, int score ) {
ScorePlum(ent, origin, score);
//
ent->client->ps.persistant[PERS_SCORE] += score;
- if ( g_gametype.integer == GT_TEAM )
+ if ( g_gametype.integer == GT_TEAM
+ || g_gametype.integer == GT_TLMS ) // BFP - Team Last Man Standing
level.teamScores[ ent->client->ps.persistant[PERS_TEAM] ] += score;
CalculateRanks();
}
@@ -75,11 +76,14 @@ Toss the weapon and powerups for the killed player
*/
void TossClientItems( gentity_t *self ) {
gitem_t *item;
- int weapon;
float angle;
int i;
gentity_t *drop;
+ // BFP - No weapon drop
+#if 0
+ int weapon;
+
// drop the weapon if not a gauntlet or machinegun
weapon = self->s.weapon;
@@ -104,6 +108,7 @@ void TossClientItems( gentity_t *self ) {
// spawn the item
Drop_Item( self, item, 0 );
}
+#endif
// drop all the powerups if not in teamplay
if ( g_gametype.integer != GT_TEAM ) {
@@ -133,22 +138,31 @@ LookAtKiller
*/
void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker ) {
vec3_t dir;
- vec3_t angles;
+ float killerYaw = self->s.angles[YAW];
if ( attacker && attacker != self ) {
VectorSubtract (attacker->s.pos.trBase, self->s.pos.trBase, dir);
} else if ( inflictor && inflictor != self ) {
VectorSubtract (inflictor->s.pos.trBase, self->s.pos.trBase, dir);
} else {
- self->client->ps.stats[STAT_DEAD_YAW] = self->s.angles[YAW];
+ if ( killerYaw > 255 ) {
+ self->client->ps.damageYaw = 255;
+ self->client->ps.damagePitch = (int)( killerYaw - 255 );
+ } else {
+ self->client->ps.damageYaw = (int)killerYaw;
+ self->client->ps.damagePitch = 0;
+ }
return;
}
- self->client->ps.stats[STAT_DEAD_YAW] = vectoyaw ( dir );
-
- angles[YAW] = vectoyaw ( dir );
- angles[PITCH] = 0;
- angles[ROLL] = 0;
+ killerYaw = vectoyaw( dir );
+ if ( killerYaw > 255 ) {
+ self->client->ps.damageYaw = 255;
+ self->client->ps.damagePitch = (int)( killerYaw - 255 );
+ } else {
+ self->client->ps.damageYaw = (int)killerYaw;
+ self->client->ps.damagePitch = 0;
+ }
}
/*
@@ -157,24 +171,6 @@ GibEntity
==================
*/
void GibEntity( gentity_t *self, int killer ) {
- gentity_t *ent;
- int i;
-
- //if this entity still has kamikaze
- if (self->s.eFlags & EF_KAMIKAZE) {
- // check if there is a kamikaze timer around for this owner
- for (i = 0; i < MAX_GENTITIES; i++) {
- ent = &g_entities[i];
- if (!ent->inuse)
- continue;
- if (ent->activator != self)
- continue;
- if (strcmp(ent->classname, "kamikaze timer"))
- continue;
- G_FreeEntity(ent);
- break;
- }
- }
G_AddEvent( self, EV_GIB_PLAYER, killer );
self->takedamage = qfalse;
self->s.eType = ET_INVISIBLE;
@@ -187,6 +183,8 @@ body_die
==================
*/
void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
+ int killer;
+
if ( self->health > GIB_HEALTH ) {
return;
}
@@ -195,36 +193,23 @@ void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int d
return;
}
- GibEntity( self, 0 );
+ if ( attacker && attacker->client ) {
+ killer = attacker->s.number;
+ } else {
+ killer = ENTITYNUM_WORLD;
+ }
+
+ GibEntity( self, killer );
}
// these are just for logging, the client prints its own messages
-char *modNames[] = {
- "MOD_UNKNOWN",
- "MOD_SHOTGUN",
- "MOD_GAUNTLET",
- "MOD_MACHINEGUN",
- "MOD_GRENADE",
- "MOD_GRENADE_SPLASH",
- "MOD_ROCKET",
- "MOD_ROCKET_SPLASH",
- "MOD_PLASMA",
- "MOD_PLASMA_SPLASH",
- "MOD_RAILGUN",
- "MOD_LIGHTNING",
- "MOD_BFG",
- "MOD_BFG_SPLASH",
- "MOD_WATER",
- "MOD_SLIME",
- "MOD_LAVA",
- "MOD_CRUSH",
- "MOD_TELEFRAG",
- "MOD_FALLING",
- "MOD_SUICIDE",
- "MOD_TARGET_LASER",
- "MOD_TRIGGER_HURT",
- "MOD_GRAPPLE"
+char *modNames[MOD_MAX] = {
+ // BFP - Means of death are declared in bg_meansofdeath.h file
+#define MOD_STRINGS
+ #include "bg_meansofdeath.h"
+#undef MOD_STRINGS
+ "NULL" // avoid -Wpedantic warnings
};
/*
@@ -239,8 +224,7 @@ void CheckAlmostCapture( gentity_t *self, gentity_t *attacker ) {
// if this player was carrying a flag
if ( self->client->ps.powerups[PW_REDFLAG] ||
- self->client->ps.powerups[PW_BLUEFLAG] ||
- self->client->ps.powerups[PW_NEUTRALFLAG] ) {
+ self->client->ps.powerups[PW_BLUEFLAG] ) {
// get the goal flag this player should have been going for
if ( g_gametype.integer == GT_CTF ) {
if ( self->client->sess.sessionTeam == TEAM_BLUE ) {
@@ -277,36 +261,427 @@ void CheckAlmostCapture( gentity_t *self, gentity_t *attacker ) {
}
}
+/*
+==============
+SendTierEvent
+==============
+*/
+static void SendTierEvent( const entity_event_t EVENT, gentity_t *ent ) { // BFP - Send tier event
+ gentity_t *t = G_TempEntity( ent->client->ps.origin, EVENT );
+ t->s.otherEntityNum = ent->client->ps.clientNum;
+}
+
+/*
+======================
+GainPowerlevelKiHealth
+======================
+*/
+static void GainPowerlevelKiHealth( gentity_t *self, gentity_t *attacker ) { // BFP - Gain powerlevel, ki and health
+ float currentKiPercentage;
+ qboolean alreadyTier1 = qfalse;
+ qboolean alreadyTier2 = qfalse;
+ qboolean alreadyTier3 = qfalse;
+ qboolean alreadyTransformed = qfalse;
+ // BFP - Monster gamemode handling the maximum ki calculation
+ int monsterKi = 1;
+
+ if ( attacker->client->ps.persistant[PERS_POWERLEVEL] > 99 && attacker->client->ps.persistant[PERS_POWERLEVEL] < 250 ) {
+ alreadyTier1 = qtrue;
+ }
+ if ( attacker->client->ps.persistant[PERS_POWERLEVEL] > 249 && attacker->client->ps.persistant[PERS_POWERLEVEL] < 500 ) {
+ alreadyTier2 = qtrue;
+ }
+ if ( attacker->client->ps.persistant[PERS_POWERLEVEL] > 499 && attacker->client->ps.persistant[PERS_POWERLEVEL] < 1000 ) {
+ alreadyTier3 = qtrue;
+ }
+ if ( attacker->client->ps.persistant[PERS_POWERLEVEL] >= 1000 ) {
+ alreadyTransformed = qtrue;
+ }
+
+ // BFP - Attacker gains powerlevel from the opponent
+ // Formula: attackerPowerlevel += 1 + ( opponentPowerlevel * g_plKillBonusPct.value )
+ attacker->client->ps.persistant[PERS_POWERLEVEL] += 1 + ( self->client->ps.persistant[PERS_POWERLEVEL] * g_plKillBonusPct.value );
+ if ( !alreadyTier1 && attacker->client->ps.persistant[PERS_POWERLEVEL] > 99 && attacker->client->ps.persistant[PERS_POWERLEVEL] < 250 ) {
+ attacker->client->ps.eFlags |= EF_AURA_TIER_UP;
+ attacker->client->tierUnlockedTime = level.time + 2000;
+ SendTierEvent( EV_TIER_1, attacker );
+ } else if ( !alreadyTier2 && attacker->client->ps.persistant[PERS_POWERLEVEL] > 249 && attacker->client->ps.persistant[PERS_POWERLEVEL] < 500 ) {
+ attacker->client->ps.eFlags |= EF_AURA_TIER_UP;
+ attacker->client->tierUnlockedTime = level.time + 2000;
+ SendTierEvent( EV_TIER_2, attacker );
+ } else if ( !alreadyTier3 && attacker->client->ps.persistant[PERS_POWERLEVEL] > 499 && attacker->client->ps.persistant[PERS_POWERLEVEL] < 1000 ) {
+ attacker->client->ps.eFlags |= EF_AURA_TIER_UP;
+ attacker->client->tierUnlockedTime = level.time + 2000;
+ SendTierEvent( EV_TIER_3, attacker );
+ } else if ( !alreadyTransformed && attacker->client->ps.persistant[PERS_POWERLEVEL] > 999 ) {
+ attacker->client->ps.eFlags |= EF_AURA_TIER_UP;
+ attacker->client->ps.pm_flags |= PMF_ULTIMATE_TIER;
+ attacker->client->tierUnlockedTime = level.time + 5000;
+ SendTierEvent( EV_TIER_4, attacker );
+ }
+ if ( attacker->client->ps.persistant[PERS_POWERLEVEL] > 1000 ) { // if higher, clamp to 1000
+ attacker->client->ps.persistant[PERS_POWERLEVEL] = 1000;
+ }
+
+ // BFP - Send powerlevel info to cgame reusing frame from entityState_t struct
+ attacker->s.frame = attacker->client->ps.persistant[PERS_POWERLEVEL];
+
+ // BFP - Add more maximum ki
+ currentKiPercentage = ( (float)attacker->client->ps.stats[STAT_KI] / (float)attacker->client->ps.stats[STAT_MAX_KI] );
+ // BFP - Monster gamemode, handle attacker ki monster calculation
+ if ( attacker->client->ps.eFlags & EF_MONSTER ) {
+ monsterKi = 2;
+ }
+ // BFP - NOTE: What the heck? Did BFP dev make this multiplying 9.00825 with powerlevel? Strange approximation...
+ attacker->client->ps.stats[STAT_MAX_KI] = monsterKi * ( 999 + ( 9.00825 * attacker->client->ps.persistant[PERS_POWERLEVEL] ) );
+
+ // BFP - Avoid exceeding maximum ki
+ if ( attacker->client->ps.stats[STAT_MAX_KI] > ( 10000 * monsterKi ) ) {
+ attacker->client->ps.stats[STAT_MAX_KI] = 10000 * monsterKi;
+ }
+
+ // BFP - Add and balance ki
+ if ( attacker->client->ps.persistant[PERS_POWERLEVEL] < 1000 ) {
+ if ( currentKiPercentage < 1.0f ) {
+ currentKiPercentage *= (float)attacker->client->ps.stats[STAT_MAX_KI];
+ attacker->client->ps.stats[STAT_KI] = currentKiPercentage;
+ } else {
+ attacker->client->ps.stats[STAT_KI] = attacker->client->ps.stats[STAT_MAX_KI];
+ }
+ if ( attacker->client->ps.stats[STAT_KI] > attacker->client->ps.stats[STAT_MAX_KI] ) {
+ attacker->client->ps.stats[STAT_KI] = attacker->client->ps.stats[STAT_MAX_KI];
+ }
+ }
+
+ // BFP - Add max health and balance health
+ if ( attacker->client->ps.stats[STAT_MAX_HEALTH] < 1000 ) {
+ float currentHealthPercentage = ( (float)attacker->client->ps.stats[STAT_HEALTH] / (float)attacker->client->ps.stats[STAT_MAX_HEALTH] ) * 10.0f;
+
+ attacker->client->ps.stats[STAT_MAX_HEALTH] = 1 + attacker->client->ps.persistant[PERS_POWERLEVEL];
+ if ( attacker->client->ps.stats[STAT_MAX_HEALTH] > 1000 ) {
+ attacker->client->ps.stats[STAT_MAX_HEALTH] = 1000;
+ }
+
+ // keep and balance health
+ if ( currentHealthPercentage < 10 ) {
+ attacker->health = attacker->client->ps.stats[STAT_HEALTH] = attacker->client->ps.stats[STAT_HEALTH] + (short)currentHealthPercentage;
+ } else {
+ attacker->health = attacker->client->ps.stats[STAT_HEALTH] = attacker->client->ps.stats[STAT_MAX_HEALTH];
+ }
+ if ( attacker->client->ps.stats[STAT_HEALTH] > attacker->client->ps.stats[STAT_MAX_HEALTH] ) {
+ attacker->health = attacker->client->ps.stats[STAT_HEALTH] = attacker->client->ps.stats[STAT_MAX_HEALTH];
+ }
+ }
+
+ if ( attacker->client->ps.stats[STAT_KI] > attacker->client->ps.stats[STAT_MAX_KI] ) {
+ attacker->client->ps.stats[STAT_KI] = attacker->client->ps.stats[STAT_MAX_KI];
+ }
+
+ // BFP - When unlocking a tier, give the player maximum health and ki
+ if ( attacker->client->ps.eFlags & EF_AURA_TIER_UP ) {
+ attacker->health = attacker->client->ps.stats[STAT_HEALTH] = attacker->client->ps.stats[STAT_MAX_HEALTH];
+ attacker->client->ps.stats[STAT_KI] = attacker->client->ps.stats[STAT_MAX_KI];
+ }
+}
+
+/*
+==================================================
+Survival_ForceToSpectateAndRespawnAnotherPlayer
+==================================================
+*/
+static void Survival_ForceToSpectateAndRespawnAnotherPlayer( gentity_t *self ) { // BFP - Function for survival gamemode
+ int i, oldestTime = level.time, oldestClient = -1;
+
+ // force to spectate the dead player
+ if ( level.numPlayingClients == 2 && level.numConnectedClients > 2 ) {
+ SetTeam( self, "s" );
+ self->client->sess.sessionTeam = TEAM_SPECTATOR;
+ }
+
+ for ( i = 0; i < level.maxclients; ++i ) {
+ gclient_t *cl = &level.clients[i];
+ if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam == TEAM_SPECTATOR ) {
+ if ( cl->pers.enterTime < oldestTime ) {
+ oldestTime = cl->pers.enterTime;
+ oldestClient = i;
+ }
+ }
+ }
+
+ if ( oldestClient != -1 && level.numConnectedClients > 2 ) {
+ gentity_t *spectator = &g_entities[oldestClient];
+ SetTeam( spectator, "f" );
+ ClientSpawn( spectator );
+ }
+}
+
+/*
+====================
+CheckSurvivalWarmup
+====================
+*/
+static void CheckSurvivalWarmup( void ) { // BFP - Function for survival gamemode
+ // if the warmup is changed at the console, restart it
+ if ( g_warmup.modificationCount != level.warmupModificationCount ) {
+ level.warmupModificationCount = g_warmup.modificationCount;
+ level.warmupTime = -1;
+ }
+
+ // if all players have arrived, start the countdown
+ if ( level.numPlayingClients == 2 ) {
+ // fudge by -1 to account for extra delays
+ level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
+ trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
+ }
+}
+
/*
==================
-CheckAlmostScored
+CheckSurvivalRules
==================
*/
-void CheckAlmostScored( gentity_t *self, gentity_t *attacker ) {
- gentity_t *ent;
- vec3_t dir;
- char *classname;
+static void CheckSurvivalRules( gentity_t *self, int meansOfDeath ) { // BFP - Survival rules
+ int i;
+
+ if ( g_gametype.integer != GT_SURVIVAL ) {
+ return;
+ }
- // if the player was carrying cubes
- if ( self->client->ps.generic1 ) {
- if ( self->client->sess.sessionTeam == TEAM_BLUE ) {
- classname = "team_redobelisk";
+ // don't make players not see the scoreboard when the match timelimit ends
+ if ( g_timelimit.integer && !level.warmupTime
+ && level.time - level.startTime >= g_timelimit.integer*60000 ) {
+ return;
+ }
+
+ // don't make players blind to the scoreboard when the winner reached the fraglimit and the match ended
+ for ( i = 0 ; i < level.maxclients; ++i ) {
+ gclient_t *cl;
+ cl = level.clients + i;
+ if ( cl->ps.persistant[PERS_SCORE] >= g_fraglimit.integer ) {
+ return;
}
- else {
- classname = "team_blueobelisk";
+ }
+
+ // BFP - NOTE: This is the best conditional when changing a character model.
+ // Originally on BFP, when a player changed the model, the warmup time was being restarted.
+ // So, that could lead trolling cases to break the game balance.
+
+ if ( level.warmupTime > 0 && level.numPlayingClients == 2 ) {
+ // force to spectate the player who is touching something mortal
+ if ( meansOfDeath == MOD_TRIGGER_HURT || meansOfDeath == MOD_CRUSH ) {
+ // if the score points didn't decrease, decrease it for balance sake!
+ ScorePlum( self, self->r.currentOrigin, -1 );
+ --self->client->ps.persistant[PERS_SCORE];
+ CalculateRanks();
+
+ Survival_ForceToSpectateAndRespawnAnotherPlayer( self );
+
+ CheckSurvivalWarmup();
+
+ if ( level.numConnectedClients == 2 ) {
+ respawn( self );
+ }
+
+ return;
}
- ent = G_Find(NULL, FOFS(classname), classname);
- // if we found the destination obelisk
- if ( ent ) {
- // if the player was *very* close
- VectorSubtract( self->client->ps.origin, ent->s.origin, dir );
- if ( VectorLength(dir) < 200 ) {
- self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT;
- if ( attacker->client ) {
- attacker->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT;
- }
+ // respawn if changing, trying to spectate or commiting a suicide
+ else if ( meansOfDeath != MOD_TRIGGER_HURT && meansOfDeath != MOD_CRUSH ) {
+ respawn( self );
+ return;
+ }
+ }
+
+ // if there are only 2 players in the game
+ if ( level.warmupTime <= 0
+ && level.numConnectedClients == 2 && level.numPlayingClients == 2 ) {
+ // respawn quickly instead watching the scoreboard while being dead
+ respawn( self );
+
+ CheckSurvivalWarmup();
+
+ return;
+ }
+
+ Survival_ForceToSpectateAndRespawnAnotherPlayer( self );
+
+ CheckSurvivalWarmup();
+}
+
+/*
+==========================
+CheckMonsterGamemodeRules
+==========================
+*/
+static void CheckMonsterGamemodeRules( gentity_t *self, gentity_t *attacker, int meansOfDeath ) { // BFP - Monster gamemode rules
+ qboolean selfMonster = ( self && self->client && self->client->ps.clientNum == level.monsterClientNum );
+
+ if ( g_gametype.integer != GT_MONSTER ) {
+ return;
+ }
+
+ // if it's going to spectate
+ if ( selfMonster && self->client->pers.teamState.state == TEAM_BEGIN ) {
+ return;
+ }
+
+ // if changed the character, just respawn
+ if ( selfMonster && meansOfDeath == MOD_UNKNOWN ) {
+ respawn( self );
+ return;
+ }
+
+ if ( selfMonster
+ && ( meansOfDeath == MOD_TRIGGER_HURT || meansOfDeath == MOD_CRUSH || meansOfDeath == MOD_FALLING
+ || meansOfDeath == MOD_LAVA || meansOfDeath == MOD_SLIME ) ) {
+ trap_SendServerCommand( -1, va("print \"The monster died without a killer.\n\"") );
+ respawn( self );
+ return;
+ }
+
+ if ( attacker && attacker->client && selfMonster ) {
+ if ( attacker == self ) {
+ trap_SendServerCommand( -1, va("print \"The monster killed himself!\n\"") );
+ respawn( attacker );
+ return;
+ }
+ trap_SendServerCommand( -1, va("print \"%s killed the monster!\n\"", attacker->client->pers.netname) );
+ level.monsterClientNum = attacker->client->ps.clientNum;
+ self->client->ps.eFlags &= ~EF_MONSTER;
+ attacker->client->ps.eFlags |= EF_MONSTER;
+
+ respawn( attacker );
+ }
+}
+
+/*
+=================================
+TeamLastManStanding_CheckDeadTeam
+=================================
+*/
+static qboolean TeamLastManStanding_CheckDeadTeam( team_t team ) { // BFP - Function for Team Last Man Standing gamemode
+ int i;
+
+ for ( i = 0; i < level.numConnectedClients; ++i ) {
+ gclient_t *cl = &level.clients[i];
+ if ( cl->sess.sessionTeam == team && cl->ps.pm_type != PM_DEAD ) {
+ return qfalse;
+ }
+ }
+ return qtrue;
+}
+
+/*
+=======================================
+TeamLastManStanding_RespawnAllDeadTeams
+=======================================
+*/
+static void TeamLastManStanding_RespawnAllDeadTeams( void ) { // BFP - Function for Team Last Man Standing gamemode
+ int i;
+
+ for ( i = 0; i < level.numConnectedClients; ++i ) {
+ gentity_t *ent = &g_entities[i];
+ if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR
+ && ent->client->forceToSpectate ) {
+ ent->client->forceToSpectate = qfalse;
+ if ( ent->client->selectedTeam == TEAM_RED ) {
+ SetTeam( ent, "r" );
+ ClientSpawn( ent );
+ } else if ( ent->client->selectedTeam == TEAM_BLUE ) {
+ SetTeam( ent, "b" );
+ ClientSpawn( ent );
}
+ ent->client->sess.sessionTeam = ent->client->selectedTeam;
+ }
+ }
+}
+
+/*
+==============================
+CheckTeamLastManStandingWarmup
+==============================
+*/
+static void CheckTeamLastManStandingWarmup( void ) { // BFP - Function for Team Last Man Standing gamemode
+ if ( g_warmup.integer <= 0 ) {
+ return;
+ }
+
+ // if the warmup is changed at the console, restart it
+ if ( g_warmup.modificationCount != level.warmupModificationCount ) {
+ level.warmupModificationCount = g_warmup.modificationCount;
+ level.warmupTime = -1;
+ }
+
+ // fudge by -1 to account for extra delays
+ level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
+ trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
+}
+
+/*
+==============================
+CheckTeamLastManStandingRules
+==============================
+*/
+static void CheckTeamLastManStandingRules( gentity_t *self, gentity_t *attacker, int meansOfDeath ) { // BFP - Team Last Man Standing rules
+ qboolean selfClient = ( self && self->client );
+ qboolean attackerClient = ( attacker && attacker->client );
+
+ // BFP - NOTE: Force to spectate the current dead players in the match,
+ // the dead player spectators can't join, the playing players can't switch teams and going to spectate.
+ // Once, the match gets all dead players (these who are marked and forced to spectate),
+ // respawns all dead players, the game finishes until hits the fraglimit/timelimit.
+ // The players who were beginning to connect and are news in the match, they can join if they wish,
+ // but after there's no turn back (that applies the explanation from before).
+ // Originally on BFP, the dead players can join in the match, so that's invalid and it's against the rules.
+
+ if ( g_gametype.integer != GT_TLMS ) {
+ return;
+ }
+
+ // force to spectate the player who is touching something mortal during the warmup
+ if ( level.warmupTime > 0
+ && selfClient
+ && ( meansOfDeath == MOD_TRIGGER_HURT || meansOfDeath == MOD_CRUSH ) ) {
+ // if the score points didn't decrease, decrease it for balance sake!
+ ScorePlum( self, self->r.currentOrigin, -1 );
+ --self->client->ps.persistant[PERS_SCORE];
+ CalculateRanks();
+ }
+
+ // check dead teams
+ if ( TeamLastManStanding_CheckDeadTeam( TEAM_RED ) ) {
+ CheckTeamLastManStandingWarmup();
+ TeamLastManStanding_RespawnAllDeadTeams();
+ // respawn the 2 last players in the match: the victim and the attacker
+ if ( selfClient ) {
+ respawn( self );
+ }
+ if ( attackerClient ) {
+ respawn( attacker );
}
+
+ return;
+ }
+
+ if ( TeamLastManStanding_CheckDeadTeam( TEAM_BLUE ) ) {
+ CheckTeamLastManStandingWarmup();
+ TeamLastManStanding_RespawnAllDeadTeams();
+ // respawn the 2 last players in the match: the victim and the attacker
+ if ( selfClient ) {
+ respawn( self );
+ }
+ if ( attackerClient ) {
+ respawn( attacker );
+ }
+
+ return;
+ }
+
+ if ( selfClient ) {
+ self->client->forceToSpectate = qtrue;
+ // keep the selected team, otherwise won't spawn correctly
+ self->client->selectedTeam = self->client->sess.sessionTeam;
+ SetTeam( self, "s" );
+ self->client->sess.sessionTeam = TEAM_SPECTATOR;
}
}
@@ -331,17 +706,26 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
return;
}
- // check for an almost capture
- CheckAlmostCapture( self, attacker );
- // check for a player that almost brought in cubes
- CheckAlmostScored( self, attacker );
+ // BFP - For compilation safety from shared objects (.so) and dll
+ if ( attacker != NULL ) {
+ // check for an almost capture
+ CheckAlmostCapture( self, attacker );
+ }
-// BFP - no hook
-#if 0
+ // BFP - BFP Beam handling
if (self->client && self->client->hook) {
- Weapon_HookFree(self->client->hook);
+ // Weapon_HookFree(self->client->hook);
+ Weapon_BFPBeamFree( self->client->hook );
}
-#endif
+
+ // BFP - Monster gamemode, if the player monster fell, respawn and don't score
+ if ( self && self->client && self->client->ps.clientNum == level.monsterClientNum
+ && ( self->client->ps.eFlags & EF_MONSTER )
+ && meansOfDeath == MOD_TRIGGER_HURT ) {
+ respawn( self );
+ return;
+ }
+
self->client->ps.pm_type = PM_DEAD;
if ( attacker ) {
@@ -392,12 +776,14 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
if( meansOfDeath == MOD_GAUNTLET ) {
+ // BFP - No gauntlet counter
// play humiliation on player
- attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
+ // attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
// add the sprite over the player's head
- attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ attacker->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
// also play humiliation on target
@@ -411,7 +797,8 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
// add the sprite over the player's head
- attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ attacker->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
}
@@ -422,16 +809,21 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
AddScore( self, self->r.currentOrigin, -1 );
}
- // Add team bonuses
- Team_FragBonuses(self, inflictor, attacker);
+ // BFP - Gain powerlevel, ki and health
+ if ( attacker && attacker->client
+ && attacker != self && !OnSameTeam (self, attacker ) ) {
+ GainPowerlevelKiHealth( self, attacker );
+ }
+
+ // BFP - For compilation safety from shared objects (.so) and dll
+ if ( attacker != NULL ) {
+ // Add team bonuses
+ Team_FragBonuses(self, inflictor, attacker);
+ }
// if I committed suicide, the flag does not fall, it returns.
if (meansOfDeath == MOD_SUICIDE) {
- if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF
- Team_ReturnFlag( TEAM_FREE );
- self->client->ps.powerups[PW_NEUTRALFLAG] = 0;
- }
- else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF
+ if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF
Team_ReturnFlag( TEAM_RED );
self->client->ps.powerups[PW_REDFLAG] = 0;
}
@@ -447,10 +839,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
TossClientItems( self );
}
else {
- if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF
- Team_ReturnFlag( TEAM_FREE );
- }
- else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF
+ if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF
Team_ReturnFlag( TEAM_RED );
}
else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF
@@ -500,10 +889,14 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );
// never gib in a nodrop
- if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) {
+ // BFP - No gibs when being attacked and entering to death phase, just stay as corpse
+#if 0
+ if ((self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) {
// gib death
GibEntity( self, killer );
- } else {
+ } else
+#endif
+ {
// normal death
static int i;
@@ -543,6 +936,14 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
trap_LinkEntity (self);
+ // BFP - Survival rules
+ CheckSurvivalRules( self, meansOfDeath );
+
+ // BFP - Monster gamemode rules
+ CheckMonsterGamemodeRules( self, attacker, meansOfDeath );
+
+ // BFP - Team Last Man Standing rules
+ CheckTeamLastManStandingRules( self, attacker, meansOfDeath );
}
@@ -647,12 +1048,26 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
vec3_t dir, vec3_t point, int damage, int dflags, int mod ) {
gclient_t *client;
int take;
- int save;
int asave;
- int knockback;
+ float knockback; // BFP - Before Q3: int
int max;
+ // BFP - Melee knockback
+ int meleeKnockback = 0;
+ // BFP - Damage force for knockback
+ float damageForce = (float)damage * 0.1;
+
+ // BFP - Ultimate tier status is invulnerable!
+ if ( targ && targ->client // BFP - NOTE: Avoid DLL/SO crashing when impacting a door or any map entity (ET_MOVER), this is important for implementations like that!
+ && ( targ->client->ps.pm_flags & PMF_ULTIMATE_TIER ) ) {
+ return;
+ }
+
+ if ( !targ || ( targ && !targ->takedamage ) ) {
+ return;
+ }
- if (!targ->takedamage) {
+ // BFP - Don't deal damage on warmup
+ if ( level.time < level.warmupTime ) {
return;
}
@@ -673,13 +1088,36 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
if ( targ->use && targ->moverState == MOVER_POS1 ) {
targ->use( targ, inflictor, attacker );
}
+ // BFP - For breakable map entities
+ if ( targ->takedamage && targ->health > 0 ) {
+ if ( damage < 1 ) {
+ damage = 1;
+ }
+ take = damage;
+ targ->health = targ->health - take;
+ }
return;
}
+
+ // BFP - Melee knockback
+ if ( mod == MOD_MELEE ) {
+ float meleeFactor = 15.0f;
+ if ( damage <= 10 ) {
+ meleeFactor = 7.5f;
+ } else if ( damage <= 20 ) {
+ meleeFactor = 7.5f + ( damage - 10 ) * 0.45f;
+ } else if ( damage <= 50 ) {
+ meleeFactor = 12.0f + ( damage - 20 ) * 0.1f;
+ }
+ meleeKnockback = damage * meleeFactor;
+ }
+
// reduce damage by the attacker's handicap value
// unless they are rocket jumping
if ( attacker->client && attacker != targ ) {
- max = attacker->client->ps.stats[STAT_MAX_HEALTH];
- damage = damage * max / 100;
+ // BFP - Apply attacker powerlevel calculation, no maximum health calculation
+ max = attacker->client->ps.persistant[PERS_POWERLEVEL] + 1; // BFP - before Q3: max = attacker->client->ps.stats[STAT_MAX_HEALTH];
+ damage = damage * max * 0.01; // BFP - before Q3: damage = damage * max / 100;
}
client = targ->client;
@@ -694,11 +1132,45 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
dflags |= DAMAGE_NO_KNOCKBACK;
} else {
VectorNormalize(dir);
+
+ // BFP - Lose altitude while flying/floating underwater
+ if ( targ->client && !( ( targ->client->ps.pm_flags & PMF_JUMP_HELD )
+ || targ->client->ps.groundEntityNum != ENTITYNUM_NONE )
+ // BFP - Don't apply for rocket jumping
+ && dir[2] <= 0.5f ) {
+ dir[2] = -1;
+ }
}
- knockback = damage;
- if ( knockback > 200 ) {
- knockback = 200;
+ // BFP - Apply knockback multiplied with damage force
+ knockback = damage * damageForce;
+ // BFP - extraKnockback from weapon/projectile
+ if ( inflictor && inflictor->extraKnockback != 0 ) {
+ knockback += inflictor->extraKnockback;
+ }
+
+ // BFP - Knockback can't be negative
+ if ( knockback < 0 ) {
+ knockback = 0;
+ }
+
+ // BFP - Before Q3:
+ // if ( knockback > 200 ) {
+ // knockback = 200;
+ // }
+
+ // BFP - Add enough knockback to push the targets while receiving explosion/projectile impacts
+ if ( mod != MOD_MELEE ) {
+ // BFP - Rocket jumping
+ if ( dir && dir[2] > 0.5f ) {
+ knockback = 50;
+ }
+ } else { // BFP - Melee knockback
+ knockback = meleeKnockback;
+ }
+
+ if ( knockback > 1800 ) { // BFP - Knockback cannot be more than 1800
+ knockback = 1800;
}
if ( targ->flags & FL_NO_KNOCKBACK ) {
knockback = 0;
@@ -707,14 +1179,39 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
knockback = 0;
}
+ // BFP - Blinds the opponent and it can be blinded again after 4 seconds (look inside cg_draw.c in CG_DrawBlindEffect for more details)
+ if ( inflictor->blinding
+ && ( !targ->blindedTime || level.time - targ->blindedTime >= 2000 )
+ && targ->client && targ->client->ps.pm_type != PM_DEAD ) {
+ targ->blindedTime = level.time;
+ BG_AddPredictableEventToPlayerstate( EV_BLINDING, 0, &targ->client->ps, -1 );
+ }
+
// figure momentum add, even if the damage won't be taken
if ( knockback && targ->client ) {
vec3_t kvel;
float mass;
mass = 200;
+ // BFP - Using hitscan weapons, the mass is different
+ if ( targ->client != attacker->client
+ && inflictor->attackType == ATK_HITSCAN ) {
+ mass = 50;
+ }
VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel);
+ // BFP - Rocket jumping
+ if ( ( ( targ->client->ps.pm_flags & PMF_JUMP_HELD )
+ || targ->waterlevel > 1 )
+ && dir && dir[2] > 0.5f ) {
+ // increase vertical impulse by 25%
+ kvel[2] *= 1.25f;
+ if ( targ->waterlevel > 1 ) { // if underwater double the impulse and a little push
+ VectorScale( kvel, 2, kvel );
+ kvel[0] *= 1.5f;
+ kvel[1] *= 1.5f;
+ }
+ }
VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity);
// set the timer so that the other client can't cancel
@@ -751,6 +1248,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
}
+ // BFP - No battlesuit powerup
+#if 0
// battlesuit protects from all radius damage (but takes knockback)
// and protects 50% against all damage
if ( client && client->ps.powerups[PW_BATTLESUIT] ) {
@@ -760,19 +1259,14 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
damage *= 0.5;
}
-
-// BFP - TODO: Make hit stun received from melee attack with PMF_KI_BOOST
-#if 0
- if ( g_hitStun.integer >= 1 && ( attacker->client->ps.pm_flags & PMF_KI_BOOST )
- && targ != attacker && !OnSameTeam (targ, attacker) ) {
- attacker->client->ps.hitStunTime = -3; // just an idea, enable the hit stun with a conditional of == -3
- }
#endif
// add to the attacker's hit counter (if the target isn't a general entity like a prox mine)
if ( attacker->client && targ != attacker && targ->health > 0
&& targ->s.eType != ET_MISSILE
- && targ->s.eType != ET_GENERAL) {
+ && targ->s.eType != ET_GENERAL
+ && !( client->ps.pm_flags & PMF_BLOCK ) // BFP - When blocking, don't receive any hit
+ && damage > 0 ) { // BFP - Don't apply hits persistance if the damage is lesser than 1
if ( OnSameTeam( targ, attacker ) ) {
attacker->client->ps.persistant[PERS_HITS]--;
} else {
@@ -787,16 +1281,23 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
damage *= 0.5;
}
+ // BFP - No damage < 1 conditional
+#if 0
if ( damage < 1 ) {
damage = 1;
}
+#endif
take = damage;
- save = 0;
// save some from armor
asave = CheckArmor (targ, take, dflags);
take -= asave;
+ // BFP - When blocking, don't receive damage
+ if ( client && ( client->ps.pm_flags & PMF_BLOCK ) ) {
+ take = asave = 0;
+ }
+
if ( g_debugDamage.integer ) {
G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number,
targ->health, take, asave );
@@ -805,12 +1306,9 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
// add to the damage inflicted on a player this frame
// the total will be turned into screen blends and view angle kicks
// at the end of the frame
- if ( client ) {
- if ( attacker ) {
- client->ps.persistant[PERS_ATTACKER] = attacker->s.number;
- } else {
- client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD;
- }
+ if ( client
+ && !( client->ps.pm_flags & PMF_BLOCK ) ) { // BFP - When blocking, don't receive screams of pain
+ client->ps.persistant[PERS_ATTACKER] = attacker->s.number;
client->damage_armor += asave;
client->damage_blood += take;
client->damage_knockback += knockback;
@@ -849,7 +1347,9 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
targ->health = -999;
targ->enemy = attacker;
- targ->die (targ, inflictor, attacker, take, mod);
+ // `body_die` doesn't use any of its arguments (except `targ` and 'attacker'),
+ // so it's fine if they're `NULL`.
+ targ->die( targ, inflictor, attacker, take, mod );
return;
} else if ( targ->pain ) {
targ->pain (targ, attacker, take);
@@ -922,7 +1422,7 @@ qboolean CanDamage (gentity_t *targ, vec3_t origin) {
G_RadiusDamage
============
*/
-qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, float radius,
+qboolean G_RadiusDamage ( gentity_t *self, vec3_t origin, gentity_t *attacker, float damage, float radius,
gentity_t *ignore, int mod) {
float points, dist;
gentity_t *ent;
@@ -931,7 +1431,7 @@ qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, floa
vec3_t mins, maxs;
vec3_t v;
vec3_t dir;
- int i, e;
+ int i, e, swapInd;
qboolean hitClient = qfalse;
if ( radius < 1 ) {
@@ -945,6 +1445,41 @@ qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, floa
numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+ // Put the attacker and their team members at the beginning of the list
+ // so that we damage them first, to make for more deterministic behavior.
+ // Otherwise if 1 frag left for the attacker and they both frag and suicide
+ // with this missile then they may or may not win,
+ // depending on the order returned from `trap_EntitiesInBox`,
+ // which is not defined (or at least is not relevant to us here).
+ //
+ // Note, however, that during sudden death this situation will result
+ // in the attacker's team losing.
+ //
+ // An alternative would be to sort by distance,
+ // which is what e.g. ETLegacy does:
+ // https://github.com/etlegacy/etlegacy/blob/764ffc00a953e59aaf435272d004c49a89710309/src/game/g_combat.c#L2373
+ for ( e = 0, swapInd = 0 ; e < numListedEntities ; e++ ) {
+ ent = &g_entities[entityList[ e ]];
+ if ( ent == attacker || OnSameTeam( ent, attacker ) ) {
+ const int temp = entityList[ e ];
+ entityList[ e ] = entityList[ swapInd ];
+ entityList[ swapInd ] = temp;
+
+ swapInd++;
+ }
+ }
+ // For extra determinism, damage self before other team members.
+ for ( e = 0 ; e < numListedEntities ; e++ ) {
+ ent = &g_entities[entityList[ e ]];
+ if ( ent == attacker ) {
+ const int temp = entityList[ e ];
+ entityList[ e ] = entityList[ 0 ];
+ entityList[ 0 ] = temp;
+
+ break;
+ }
+ }
+
for ( e = 0 ; e < numListedEntities ; e++ ) {
ent = &g_entities[entityList[ e ]];
@@ -979,7 +1514,7 @@ qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, floa
// push the center of mass higher than the origin so players
// get knocked into the air more
dir[2] += 24;
- G_Damage (ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod);
+ G_Damage (ent, self, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod);
}
}
diff --git a/source/game/g_cvar.h b/source/game/g_cvar.h
index fad0c45..cecaeea 100644
--- a/source/game/g_cvar.h
+++ b/source/game/g_cvar.h
@@ -62,29 +62,36 @@ G_CVAR( g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse )
G_CVAR( g_motd, "g_motd", "", 0, 0, qfalse )
G_CVAR( g_blood, "com_blood", "1", 0, 0, qfalse )
-G_CVAR( g_basePL, "g_basePL", "150", 0, 0, qtrue ) // BFP - Base powerlevel
G_CVAR( g_allowSpectatorChat, "g_allowSpectatorChat", "", 0, 0, qtrue ) // BFP - Allow spectator chat
-G_CVAR( g_meleeDamage, "g_meleeDamage", "10", 0, 0, qtrue ) // BFP - Melee damage
-G_CVAR( g_meleeDiveRange, "g_meleeDiveRange", "700", 0, 0, qtrue ) // BFP - Melee dive range
-G_CVAR( g_meleeRange, "g_meleeRange", "32", 0, 0, qtrue ) // BFP - Melee range
-G_CVAR( g_chargeDelay, "g_chargeDelay", "750", 0, 0, qtrue ) // BFP - Charge delay
+G_CVAR( g_meleeDamage, "g_meleeDamage", "10", CVAR_ARCHIVE, 0, qtrue ) // BFP - Melee damage
+G_CVAR( g_meleeDiveRange, "g_meleeDiveRange", "700", CVAR_ARCHIVE, 0, qtrue ) // BFP - Melee dive range
+G_CVAR( g_meleeRange, "g_meleeRange", "32", CVAR_ARCHIVE, 0, qtrue ) // BFP - Melee range
+G_CVAR( g_chargeDelay, "g_chargeDelay", "750", CVAR_ARCHIVE, 0, qtrue ) // BFP - Charge delay
G_CVAR( g_hitStun, "g_hitStun", "", 0, 0, qtrue ) // BFP - Hit stun
-G_CVAR( g_meleeOnly, "g_meleeOnly", "", 0, 0, qtrue ) // BFP - Melee only
-G_CVAR( g_noFlight, "g_noFlight", "", 0, 0, qtrue ) // BFP - No flight
-G_CVAR( g_plKillBonusPct, "g_plKillBonusPct", ".1", 0, 0, qtrue ) // BFP - Kill bonus percentage
-G_CVAR( g_maxSpawnPL, "g_maxSpawnPL", "999", 0, 0, qtrue ) // BFP - Max spawn powerlevel
+G_CVAR( g_meleeOnly, "g_meleeOnly", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qtrue ) // BFP - Melee only
+G_CVAR( g_noFlight, "g_noFlight", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qtrue ) // BFP - No flight
+
+G_CVAR( g_basePL, "g_basePL", "150", CVAR_ARCHIVE | CVAR_LATCH, 0, qtrue ) // BFP - Base powerlevel
+G_CVAR( g_plKillBonusPct, "g_plKillBonusPct", ".1", CVAR_ARCHIVE, 0, qtrue ) // BFP - Kill bonus percentage
+G_CVAR( g_maxSpawnPL, "g_maxSpawnPL", "0", CVAR_ARCHIVE, 0, qtrue ) // BFP - Max spawn powerlevel
+
G_CVAR( g_flightCost, "g_flightCost", "50", 0, 0, qtrue ) // BFP - Flight cost
G_CVAR( g_flightCostPct, "g_flightCostPct", "0", 0, 0, qtrue ) // BFP - Flight cost percentage
+
G_CVAR( g_boostCost, "g_boostCost", "350", 0, 0, qtrue ) // BFP - Boost cost
-G_CVAR( g_boostCostPct, "g_boostCostPct", "3", 0, 0, qtrue ) // BFP - Boost cost percentage
-G_CVAR( g_blockCost, "g_blockCost", "2", 0, 0, qtrue ) // BFP - Block cost
-G_CVAR( g_blockCostPct, "g_blockCostPct", "3", 0, 0, qtrue ) // BFP - Block cost percentage
+G_CVAR( g_boostCostPct, "g_boostCostPct", "0", 0, 0, qtrue ) // BFP - Boost cost percentage
+
+G_CVAR( g_blockCost, "g_blockCost", "2", CVAR_ARCHIVE, 0, qtrue ) // BFP - Block cost
+G_CVAR( g_blockCostPct, "g_blockCostPct", "0", CVAR_ARCHIVE, 0, qtrue ) // BFP - Block cost percentage
+G_CVAR( g_blockDelay, "g_blockDelay", "2", CVAR_ARCHIVE, 0, qtrue ) // BFP - Block delay
+G_CVAR( g_blockLength, "g_blockLength", "2", CVAR_ARCHIVE, 0, qtrue ) // BFP - Block length
+
G_CVAR( g_kiRegen, "g_kiRegen", "0", 0, 0, qtrue ) // BFP - Ki regeneration
G_CVAR( g_kiRegenPct, "g_kiRegenPct", "0.6", 0, 0, qtrue ) // BFP - Ki regeneration percentage
G_CVAR( g_kiCharge, "g_kiCharge", "0", 0, 0, qtrue ) // BFP - Ki charge
G_CVAR( g_kiChargePct, "g_kiChargePct", "15", 0, 0, qtrue ) // BFP - Ki charge percentage
-G_CVAR( g_blockDelay, "g_blockDelay", "2", 0, 0, qtrue ) // BFP - Block delay
-G_CVAR( g_blockLength, "g_blockLength", "3", 0, 0, qtrue ) // BFP - Block length
+
+G_CVAR( g_monster, "g_monster", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qtrue ) // BFP - Monster feature option for Monster gamemode
G_CVAR( g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse )
G_CVAR( g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse )
diff --git a/source/game/g_items.c b/source/game/g_items.c
index 0dc67ba..9e83fcd 100644
--- a/source/game/g_items.c
+++ b/source/game/g_items.c
@@ -120,10 +120,6 @@ int Pickup_Holdable( gentity_t *ent, gentity_t *other ) {
other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist;
- if( ent->item->giTag == HI_KAMIKAZE ) {
- other->client->ps.eFlags |= EF_KAMIKAZE;
- }
-
return RESPAWN_HOLDABLE;
}
@@ -200,6 +196,8 @@ int Pickup_Weapon (gentity_t *ent, gentity_t *other) {
//======================================================================
int Pickup_Health (gentity_t *ent, gentity_t *other) {
+ // BFP - No health calculation at pickup
+#if 0
int max;
int quantity;
@@ -222,6 +220,24 @@ int Pickup_Health (gentity_t *ent, gentity_t *other) {
other->health = max;
}
other->client->ps.stats[STAT_HEALTH] = other->health;
+#endif
+
+ // BFP - Using "give health" does that
+ if ( ent->item->quantity != 5 && ent->item->quantity != 100 ) {
+ other->health = other->client->ps.stats[STAT_MAX_HEALTH];
+ other->client->ps.stats[STAT_HEALTH] = other->health;
+ }
+
+ // BFP - Using "give mega health" does that
+ if ( ent->item->quantity == 100 ) {
+ other->health += other->client->ps.stats[STAT_MAX_HEALTH];
+ if ( other->health > other->client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
+ other->health = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
+ }
+ other->client->ps.stats[STAT_HEALTH] = other->health;
+ // BFP - Give full ki too
+ other->client->ps.stats[STAT_KI] = other->client->ps.stats[STAT_MAX_KI];
+ }
if ( ent->item->quantity == 100 ) { // mega health respawns slow
return RESPAWN_MEGAHEALTH;
@@ -288,7 +304,7 @@ void RespawnItem( gentity_t *ent ) {
te->r.svFlags |= SVF_BROADCAST;
}
- if ( ent->item->giType == IT_HOLDABLE && ent->item->giTag == HI_KAMIKAZE ) {
+ if ( ent->item->giType == IT_HOLDABLE ) {
// play powerup spawn sound to all clients
gentity_t *te;
@@ -477,7 +493,8 @@ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
dropped->s.pos.trTime = level.time;
VectorCopy( velocity, dropped->s.pos.trDelta );
- dropped->s.eFlags |= EF_BOUNCE_HALF;
+ // BFP - Replaced to bounces instead using EF_BOUNCE_HALF eFlag
+ dropped->bounces = qtrue;
if (g_gametype.integer == GT_CTF && item->giType == IT_TEAM) { // Special case for CTF flags
dropped->think = Team_DroppedFlagThink;
dropped->nextthink = level.time + 30000;
@@ -632,9 +649,12 @@ ClearRegisteredItems
void ClearRegisteredItems( void ) {
memset( itemRegistered, 0, sizeof( itemRegistered ) );
+ // BFP - No weapon items
+#if 0
// players always start with the base weapon
RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) );
RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) );
+#endif
}
/*
diff --git a/source/game/g_local.h b/source/game/g_local.h
index 35da2d0..4f35bbb 100644
--- a/source/game/g_local.h
+++ b/source/game/g_local.h
@@ -50,7 +50,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define FL_DROPPED_ITEM 0x00001000
#define FL_NO_BOTS 0x00002000 // spawn point not for bot use
#define FL_NO_HUMANS 0x00004000 // spawn point just for bots
-#define FL_FORCE_GESTURE 0x00008000 // force gesture on client
+// BFP - No force gesture flag
+// #define FL_FORCE_GESTURE 0x00008000 // force gesture on client
// movers are things like doors, plats, buttons, etc
typedef enum {
@@ -90,6 +91,10 @@ struct gentity_s {
char *model;
char *model2;
int freetime; // level.time when the object was freed
+
+ // BFP - Model prefix handling
+ char oldModel[MAX_QPATH];
+ char oldModelPrefix[MAX_QPATH];
int eventTime; // events will be cleared EVENT_VALID_MSEC after set
qboolean freeAfterEvent;
@@ -143,7 +148,6 @@ struct gentity_s {
int last_move_time;
int health;
- int kiamount; // BFP - ki amount
qboolean takedamage;
@@ -174,8 +178,65 @@ struct gentity_s {
team_t fteam;
gitem_t *item; // for bonus items
+
+ // BFP - Ki charge points
+ int kiChargePoints;
+
+ // BFP - Attack type
+ int attackType;
+
+ float deltaTime; // BFP - For beam struggle
+ float distance; // BFP - For beam struggle
+
+ // BFP - Parts of weapon config logic
+ int missileDuration; // BFP - Missile duration
+ int missileGravity; // BFP - Missile gravity
+ float missileAcceleration; // BFP - Missile acceleration
+
+ qboolean splitKiBall; // BFP - Projectile state for splitting ki ball
+
+ qboolean blinding; // BFP - Blinding
+ int blindedTime; // BFP - Blind time
+
+ // BFP - NOTE: That weapon property is unused in original BFP, so it won't make any difference
+ // just use missileGravity
+ // qboolean usesGravity; // BFP - Projectile uses gravity
+
+ int multishot; // BFP - Multishot
+
+ float radius; // BFP - Collision radius
+ float maxRadius; // BFP - Collision radius cap when charged
+ int maxDamage; // BFP - Damage cap when charged
+ float maxExpRadius; // BFP - Explosion radius cap when charged
+ float chargeRadiusMult; // BFP - Collision radius added per charge level
+ int chargeDamageMult; // BFP - Damage added per charge level
+ float chargeExpRadiusMult; // BFP - Explosion radius added per charge level
+
+ qboolean bounces; // BFP - Bounces
+ float bounceFriction; // BFP - Bounce friction
+ qboolean noZBounce; // BFP - No Z bounce
+
+ qboolean piercing; // BFP - Piercing
+ qboolean piercingFade; // BFP - Piercing enter fade
+ int piercingTouch; // BFP - Piercing touch detected
+ int piercingTime; // BFP - Piercing time
+ int piercingHitTime; // BFP - Piercing hit time
+ vec3_t piercingOrigin; // BFP - Piercing origin
+
+ float homing; // BFP - Homing heading correction intensity (0.0 - 1.0)
+ float homingRange; // BFP - Homing range
+ float homingAcceleration; // BFP - Homing acceleration
+
+ int forcefieldTime; // BFP - Forcefield time
+
+ qboolean reflective; // BFP - Reflective
+
+ int priority; // BFP - Priority
+
+ int extraKnockback; // BFP - Extra knockback
};
+#define SPAWN_HEIGHT 0.0f
typedef enum {
CON_DISCONNECTED,
@@ -245,7 +306,8 @@ typedef struct {
qboolean predictItemPickup; // based on cg_predictItems userinfo
qboolean pmoveFixed; //
char netname[MAX_NETNAME];
- int maxHealth; // for handicapping
+ // BFP - No max health handicap
+ // int maxHealth; // for handicapping
int enterTime; // level.time the client entered the game
playerTeamState_t teamState; // status in teamplay games
int voteCount; // to prevent people from constantly calling votes
@@ -253,6 +315,8 @@ typedef struct {
qboolean teamInfo; // send team overlay updates?
} clientPersistant_t;
+// BFP - A macro to enable/disable switch team time delay
+#define BFP_NO_SWITCH_TEAM_TIME 0
// this structure is cleared on each ClientSpawn(),
// except for 'client->pers' and 'client->sess'
@@ -301,19 +365,53 @@ struct gclient_s {
qboolean inactivityWarning; // qtrue if the five seoond warning has been given
int rewardTime; // clear the EF_AWARD_IMPRESSIVE, etc when time > this
- int airOutTime;
+ // BFP - No drowning
+ // int airOutTime;
int lastKillTime; // for multiple kill rewards
- // BFP - no hook
- //qboolean fireHeld; // used for hook
- //gentity_t *hook; // grapple hook if out
+ // BFP - Reuse as BFP Beam attack and force field
+ qboolean fireHeld; // used for hook
+ gentity_t *hook; // grapple hook if out
+
+ // BFP - No switch team time delay
+#if BFP_NO_SWITCH_TEAM_TIME
int switchTeamTime; // time the player switched teams
+#endif
+
+ // BFP - Team Last Man Standing, force to spectate and selected team
+ qboolean forceToSpectate;
+ team_t selectedTeam;
// timeResidual is used to handle events that happen every second
// like health / armor countdowns and regeneration
int timeResidual;
+ // BFP - To handle every msec for ki boost consumption
+ float kiResidual;
+
+ // BFP - Hit stun melee delay time
+ int hitStunMeleeDelayTime;
+
+ // BFP - Block timers
+ int blockTime;
+ int blockDelayTime;
+ int blockKnockbackTime;
+
+ // BFP - Tier unlocked effect timer
+ int tierUnlockedTime;
+
+ // BFP - Zanzoken handlers
+ int zanzokenPressTime;
+ qboolean zanzokenNow;
+ qboolean zanzokenLeft, zanzokenRight;
+ int zanzokenNumberTimesAllowed;
+ int zanzokenDelay;
+ int zanzokenLastUsed;
+
+ // BFP - For alternatingXOffset
+ qboolean alternatingOffsetSide;
+
char *areabits;
};
@@ -324,8 +422,8 @@ struct gclient_s {
#define MAX_SPAWN_VARS 64
#define MAX_SPAWN_VARS_CHARS 4096
-#define NUM_SPAWN_SPOTS MAX_GENTITIES
-#define SPAWN_SPOT_INTERMISSION NUM_SPAWN_SPOTS-1
+#define NUM_SPAWN_SPOTS (MAX_GENTITIES - MAX_CLIENTS)
+#define SPAWN_SPOT_INTERMISSION (NUM_SPAWN_SPOTS - 1)
typedef struct {
struct gclient_s *clients; // [maxclients]
@@ -379,7 +477,7 @@ typedef struct {
int teamVoteTime[2]; // level.time vote was called
int teamVoteYes[2];
int teamVoteNo[2];
- int numteamVotingClients[2];// set by CalculateRanks
+ int numteamVotingClients[TEAM_NUM_TEAMS];// set by CalculateRanks
// spawn variables
qboolean spawning; // the G_Spawn*() functions are valid
@@ -388,6 +486,12 @@ typedef struct {
int numSpawnVarChars;
char spawnVarChars[MAX_SPAWN_VARS_CHARS];
+#ifndef OLD_CHECK_EXIT_RULES
+ // `CheckExitRulesLater()` has been called, and we'll need to
+ // `CheckExitRules()` when we're done doing stuff.
+ qboolean needToCheckExitRules;
+#endif
+
// intermission state
int intermissionQueued; // intermission was qualified, but
// wait INTERMISSION_DELAY_TIME before
@@ -411,6 +515,9 @@ typedef struct {
int numSpawnSpots;
int numSpawnSpotsTeam;
int numSpawnSpotsFFA;
+
+ // BFP - Monster gamemode clientNum handling
+ int monsterClientNum;
} level_locals_t;
@@ -460,15 +567,23 @@ void SaveRegisteredItems( void );
//
// g_utils.c
//
-int G_ModelIndex( char *name );
-int G_SoundIndex( char *name );
-void G_TeamCommand( team_t team, char *cmd );
+int G_ModelIndex( const char *name );
+int G_SoundIndex( const char *name );
+const char *G_GetPlayerModelName( int clientNum, const char userinfo[MAX_INFO_STRING] ); // BFP - Get player model name from a client
+void G_InitPlayerModelList( void ); // BFP - Initialize player model list
+qboolean G_PlayerModelExistsOnServer( const char *modelName ); // BFP - Check player model existence in the server
+void G_ResolvePlayerModel( const char userinfo[MAX_INFO_STRING], const char *input, char *output, int outSize ); // BFP - Resolve player model when loading by prefix
+int G_EntitiesInBox( vec3_t mins, vec3_t maxs, int *touch, int maxents );
+void G_TeamCommand( team_t team, const char *cmd );
void G_KillBox (gentity_t *ent);
gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match);
-gentity_t *G_PickTarget (char *targetname);
+gentity_t *G_PickTarget ( const char *targetname );
void G_UseTargets (gentity_t *ent, gentity_t *activator);
void G_SetMovedir ( vec3_t angles, vec3_t movedir);
+gentity_t *FindRadius ( gentity_t *from, vec3_t org, float rad ); // BFP - FindRadius function
+qboolean IsValidTargetRadius( gentity_t *ent, gentity_t *rad ); // BFP - Helper function that validates target within the radius
+
void G_InitGentity( gentity_t *e );
gentity_t *G_Spawn (void);
gentity_t *G_TempEntity( vec3_t origin, int event );
@@ -484,22 +599,23 @@ char *vtos( const vec3_t v );
float vectoyaw( const vec3_t vec );
-void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm );
+void G_AddPredictableEvent( gentity_t *ent, entity_event_t event, int eventParm );
void G_AddEvent( gentity_t *ent, int event, int eventParm );
void G_SetOrigin( gentity_t *ent, vec3_t origin );
void AddRemap(const char *oldShader, const char *newShader, float timeOffset);
-const char *BuildShaderStateConfig();
+const char *BuildShaderStateConfig( void );
//
// g_combat.c
//
qboolean CanDamage (gentity_t *targ, vec3_t origin);
void G_Damage (gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod);
-qboolean G_RadiusDamage (vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod);
+qboolean G_RadiusDamage (gentity_t *self, vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod);
int G_InvulnerabilityEffect( gentity_t *targ, vec3_t dir, vec3_t point, vec3_t impactpoint, vec3_t bouncedir );
void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath );
void TossClientItems( gentity_t *self );
-void TossClientCubes( gentity_t *self );
+
+extern char *modNames[MOD_MAX];
// damage flags
#define DAMAGE_RADIUS 0x00000001 // damage was indirect
@@ -517,8 +633,29 @@ gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t aimdir);
gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t aimdir);
gentity_t *fire_rocket (gentity_t *self, vec3_t start, vec3_t dir);
gentity_t *fire_bfg (gentity_t *self, vec3_t start, vec3_t dir);
-gentity_t *fire_grapple (gentity_t *self, vec3_t start, vec3_t dir);
+// BFP - no hook
+// gentity_t *fire_grapple (gentity_t *self, vec3_t start, vec3_t dir);
+
+// BFP - Beam fire
+gentity_t *fire_bfpbeam (gentity_t *self, vec3_t start, vec3_t dir);
+
+// BFP - Homing disk
+gentity_t *fire_disk (gentity_t *self, vec3_t start, vec3_t dir);
+
+// BFP - Forcefield
+gentity_t *fire_forcefield (gentity_t *self, vec3_t start);
+// BFP - sbeam (Super Beam?)
+gentity_t *fire_sbeam (gentity_t *self, vec3_t start, vec3_t dir);
+
+// BFP - rdmissile (Splitting ki ball)
+void G_RDMissile( gentity_t *ent, gclient_t *client );
+// BFP - Break and split the ki ball
+qboolean G_BreakRDMissile( gentity_t* ent );
+// BFP - Reflective
+void G_Reflective( gentity_t *ent, qboolean useViewAngles, const vec3_t start );
+// BFP - Use G_MissileImpact into another files to handle the weapons
+void G_MissileImpact( gentity_t *ent, trace_t *trace );
//
// g_mover.c
@@ -544,10 +681,22 @@ void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles );
qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker );
void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint );
void SnapVectorTowards( vec3_t v, vec3_t to );
-qboolean CheckGauntletAttack( gentity_t *ent );
+qboolean CheckKiShockwavePushAttack( gentity_t *ent ); // BFP - Ki shockwave push attack, before Q3: CheckGauntletAttack
+qboolean CheckMeleeAttack( gentity_t *ent ); // BFP - Melee
void Weapon_HookFree (gentity_t *ent);
void Weapon_HookThink (gentity_t *ent);
+// BFP - BFP Beam attack
+void Weapon_BFPBeam_Fire ( gentity_t *ent );
+void Weapon_BFPBeamFree ( gentity_t *ent );
+void Weapon_BFPBeamRun ( gentity_t *ent );
+
+// BFP - Forcefield
+void Weapon_Forcefield_Think ( gentity_t *ent );
+
+// BFP - sbeam (Super Beam?)
+void Weapon_SBeam_Run ( gentity_t *ent );
+
//
// g_client.c
@@ -608,12 +757,14 @@ void QDECL G_LogPrintf( const char *fmt, ... );
void SendScoreboardMessageToAllClients( void );
void QDECL G_Printf( const char *fmt, ... );
void QDECL G_Error( const char *fmt, ... );
+void G_BroadcastServerCommand( int ignoreClient, const char *command );
//
// g_client.c
//
-char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot );
-void ClientUserinfoChanged( int clientNum );
+void ClientCheckMonsterGone( gentity_t *ent );
+const char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot );
+qboolean ClientUserinfoChanged( int clientNum );
void ClientDisconnect( int clientNum );
void ClientBegin( int clientNum );
void ClientCommand( int clientNum );
@@ -631,23 +782,23 @@ void Cmd_BFP_Fly_f( gentity_t *ent ); // BFP - Flight
//
qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 );
void Team_CheckDroppedItem( gentity_t *dropped );
-qboolean CheckObeliskAttack( gentity_t *obelisk, gentity_t *attacker );
//
// g_mem.c
//
-void *G_Alloc( int size );
+void *G_Alloc( size_t size );
void G_InitMemory( void );
void Svcmd_GameMem_f( void );
//
// g_session.c
//
-void G_ReadSessionData( gclient_t *client );
-void G_InitSessionData( gclient_t *client, char *userinfo );
+void G_ReadClientSessionData( gclient_t *client );
+void G_InitSessionData( gclient_t *client, const char *team, qboolean isBot );
void G_InitWorldSession( void );
void G_WriteSessionData( void );
+void G_WriteClientSessionData( gclient_t *client );
//
// g_arenas.c
@@ -683,7 +834,7 @@ typedef struct bot_settings_s
int BotAISetup( int restart );
int BotAIShutdown( int restart );
int BotAILoadMap( int restart );
-int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart);
+qboolean BotAISetupClient( int client, struct bot_settings_s *settings, qboolean restart );
int BotAIShutdownClient( int client, qboolean restart );
int BotAIStartFrame( int time );
void BotTestAAS(vec3_t origin);
@@ -712,7 +863,7 @@ void trap_FS_Read( void *buffer, int len, fileHandle_t f );
void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
void trap_FS_FCloseFile( fileHandle_t f );
int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize );
-int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t
+int trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin );
void trap_SendConsoleCommand( int exec_when, const char *text );
void trap_Cvar_Register( vmCvar_t *cvar, const char *var_name, const char *value, int flags );
void trap_Cvar_Update( vmCvar_t *cvar );
diff --git a/source/game/g_main.c b/source/game/g_main.c
index 7af4748..2c07712 100644
--- a/source/game/g_main.c
+++ b/source/game/g_main.c
@@ -32,7 +32,6 @@ typedef struct {
int cvarFlags;
int modificationCount; // for tracking changes
qboolean trackChange; // track this variable, and announce if changed
- qboolean teamShader; // track and if changed, update shader state
} cvarTable_t;
gentity_t g_entities[MAX_GENTITIES];
@@ -63,6 +62,9 @@ void G_InitGame( int levelTime, int randomSeed, int restart );
void G_RunFrame( int levelTime );
void G_ShutdownGame( int restart );
void CheckExitRules( void );
+#ifndef OLD_CHECK_EXIT_RULES
+static void CheckExitRulesLater( void );
+#endif
/*
@@ -73,41 +75,93 @@ This is the only way control passes into the module.
This must be the very first function compiled into the .q3vm file
================
*/
-intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) {
+DLLEXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2 ) {
+ int ret;
+
switch ( command ) {
case GAME_INIT:
G_InitGame( arg0, arg1, arg2 );
- return 0;
+ ret = 0;
+ break;
case GAME_SHUTDOWN:
G_ShutdownGame( arg0 );
- return 0;
+ ret = 0;
+ break;
case GAME_CLIENT_CONNECT:
- return (intptr_t)ClientConnect( arg0, arg1, arg2 );
+ ret = (intptr_t)ClientConnect( arg0, arg1, arg2 );
+ break;
case GAME_CLIENT_THINK:
ClientThink( arg0 );
- return 0;
+ ret = 0;
+ break;
case GAME_CLIENT_USERINFO_CHANGED:
ClientUserinfoChanged( arg0 );
- return 0;
+ ret = 0;
+ break;
case GAME_CLIENT_DISCONNECT:
ClientDisconnect( arg0 );
- return 0;
+ ret = 0;
+ break;
case GAME_CLIENT_BEGIN:
ClientBegin( arg0 );
- return 0;
+ ret = 0;
+ break;
case GAME_CLIENT_COMMAND:
ClientCommand( arg0 );
- return 0;
+ ret = 0;
+ break;
case GAME_RUN_FRAME:
G_RunFrame( arg0 );
- return 0;
+ ret = 0;
+ break;
case GAME_CONSOLE_COMMAND:
- return ConsoleCommand();
+ ret = ConsoleCommand();
+ break;
case BOTAI_START_FRAME:
- return BotAIStartFrame( arg0 );
+ ret = BotAIStartFrame( arg0 );
+ break;
+ default:
+ ret = -1;
+ }
+
+#ifndef OLD_CHECK_EXIT_RULES
+ // In vanilla we did `CheckExitRules()` after each frag,
+ // which there can be multiple of when dealing with e.g. splash damage.
+ // And the order in which players get killed is not well-defined:
+ // e.g. for `G_RadiusDamage` and telefrag, kills get performed
+ // in the order returned from `trap_EntitiesInBox`;
+ // for the shotgun each pellet deals damage by itself,
+ // and the order is random;
+ // for the railgun the closest enemy gets damaged first.
+ // That resulted in inconsistent rules when there is one frag left
+ // and someone frags both a enemy and themself (or a teammate):
+ // if the enemy is processed first then the attacker wins.
+ // If a teammate is processed first then the game continues.
+ // Let's fix this by only doing `CheckExitRules()`
+ // when we're done processing the command.
+ //
+ // Note that this new behavior can result in e.g. two players
+ // hitting the frag limit at the same time.
+ // This can happen with missiles, which we run in `G_RunFrame()`.
+ // In such a case `ScoreIsTied()` will kick in, and the game will continue
+ // until the score gets untied, so this new behavior doesn't introduce
+ // a "multiple winners" situation.
+ //
+ // One might argue that in case of two missiles exploding on the same frame,
+ // the old behavoir is more fair, because `G_RunMissile()` is run
+ // in the order in which the missiles were created,
+ // so whoever shot first should win.
+ // However, this isn't entirely obviously fair,
+ // because two missiles can explode on the same frame
+ // even if they were shot a long time apart.
+ // For example, grenade launcher vs rocket launcher.
+ // In this case let's just tie the score.
+ if ( level.needToCheckExitRules ) {
+ CheckExitRules();
}
+#endif
- return -1;
+ return ret;
}
@@ -116,7 +170,7 @@ void QDECL G_Printf( const char *fmt, ... ) {
char text[1024];
va_start (argptr, fmt);
- vsprintf (text, fmt, argptr);
+ Q_vsprintf (text, fmt, argptr);
va_end (argptr);
trap_Printf( text );
@@ -127,12 +181,23 @@ void QDECL G_Error( const char *fmt, ... ) {
char text[1024];
va_start (argptr, fmt);
- vsprintf (text, fmt, argptr);
+ Q_vsprintf (text, fmt, argptr);
va_end (argptr);
trap_Error( text );
}
+void G_BroadcastServerCommand( int ignoreClient, const char *command ) {
+ int i;
+ for ( i = 0; i < level.maxclients; ++i ) {
+ if ( i == ignoreClient )
+ continue;
+ if ( level.clients[ i ].pers.connected == CON_CONNECTED ) {
+ trap_SendServerCommand( i, command );
+ }
+ }
+}
+
/*
================
G_FindTeams
@@ -189,9 +254,6 @@ void G_FindTeams( void ) {
G_Printf ("%i teams with %i entities\n", c, c2);
}
-void G_RemapTeamShaders() {
-}
-
/*
=================
@@ -201,21 +263,12 @@ G_RegisterCvars
void G_RegisterCvars( void ) {
int i;
cvarTable_t *cv;
- qboolean remapped = qfalse;
for ( i = 0, cv = gameCvarTable ; i < ARRAY_LEN( gameCvarTable ) ; i++, cv++ ) {
trap_Cvar_Register( cv->vmCvar, cv->cvarName,
cv->defaultString, cv->cvarFlags );
if ( cv->vmCvar )
cv->modificationCount = cv->vmCvar->modificationCount;
-
- if (cv->teamShader) {
- remapped = qtrue;
- }
- }
-
- if (remapped) {
- G_RemapTeamShaders();
}
// check some things
@@ -235,7 +288,6 @@ G_UpdateCvars
void G_UpdateCvars( void ) {
int i;
cvarTable_t *cv;
- qboolean remapped = qfalse;
for ( i = 0, cv = gameCvarTable ; i < ARRAY_LEN( gameCvarTable ) ; i++, cv++ ) {
if ( cv->vmCvar ) {
@@ -248,30 +300,26 @@ void G_UpdateCvars( void ) {
trap_SendServerCommand( -1, va("print \"Server: %s changed to %s\n\"",
cv->cvarName, cv->vmCvar->string ) );
}
-
- if (cv->teamShader) {
- remapped = qtrue;
- }
}
}
}
-
- if (remapped) {
- G_RemapTeamShaders();
- }
}
static void G_LocateSpawnSpots( void )
{
gentity_t *ent;
- int i, n;
+ static const vec3_t playerMins = { -12, -12, -24 };
+ static const vec3_t playerMaxs = { 12, 12, 32 };
+ vec3_t spawn_origin, spawn_ground;
+ trace_t tr;
+ int i, n;
level.spawnSpots[ SPAWN_SPOT_INTERMISSION ] = NULL;
// locate all spawn spots
n = 0;
ent = g_entities + MAX_CLIENTS;
- for ( i = MAX_CLIENTS; i < MAX_GENTITIES; i++, ent++ ) {
+ for ( i = MAX_CLIENTS; i < MAX_GENTITIES - 1; i++, ent++ ) {
if ( !ent->inuse || !ent->classname )
continue;
@@ -329,6 +377,125 @@ static void G_LocateSpawnSpots( void )
}
}
level.numSpawnSpots = n;
+
+ // try to lower spawn spots if possible to minimize nasty bouncing
+ for ( i = 0; i < level.numSpawnSpots; i++ ) {
+ ent = level.spawnSpots[i];
+
+ n = trap_PointContents( ent->s.origin, ent->s.number ) & ~(CONTENTS_FOG | CONTENTS_TRANSLUCENT);
+ if ( n != 0 ) {
+ continue;
+ }
+
+ VectorCopy( ent->s.origin, spawn_origin );
+ VectorCopy( ent->s.origin, spawn_ground );
+ spawn_ground[2] -= 32.0f;
+ trap_Trace( &tr, spawn_origin, playerMins, playerMaxs, spawn_ground, ent->s.number, CONTENTS_SOLID | CONTENTS_PLAYERCLIP );
+ if ( tr.fraction != 1.0f && tr.entityNum == ENTITYNUM_WORLD ) {
+ ent->s.origin[2] = (int) (tr.endpos[2] + 0.999f);
+ ent->s.groundEntityNum = tr.entityNum;
+ } else {
+ ent->s.groundEntityNum = ENTITYNUM_NONE;
+ }
+ }
+}
+
+
+/*
+======================
+G_LoadBFPServerConfig
+
+Loads and executes the BFP mod server config file (bfp_server.cfg)
+======================
+*/
+static void G_LoadBFPServerConfig( void ) { // BFP - Load bfp_server.cfg
+ fileHandle_t f;
+ int len;
+ char buf[BFP_CFG_BUFFER_SIZE];
+ char *ptr;
+
+ len = trap_FS_FOpenFile( "bfp_server.cfg", &f, FS_READ );
+ if ( !f ) {
+ // Com_Printf( S_COLOR_YELLOW "WARNING: BFP server config file not found, using defaults\n" );
+ return;
+ }
+
+ if ( len >= sizeof(buf) ) {
+ trap_FS_FCloseFile( f );
+ Com_Printf( "BFP server config file too long\n" );
+ return;
+ }
+
+ // read the config file
+ trap_FS_Read( buf, len, f );
+ buf[len] = 0;
+ trap_FS_FCloseFile( f );
+
+ Com_Printf( "reading bfp_server.cfg\n" );
+
+ // parse the config file line by line
+ ptr = buf;
+
+ while ( *ptr ) {
+ // skip whitespace at line start
+ while ( *ptr && (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') ) {
+ ptr++;
+ }
+
+ if ( !*ptr ) {
+ break;
+ }
+
+ if ( !Q_strncmp( ptr, "end", 3 ) ) { // "end" marks the end of file
+ break;
+ }
+
+ // check command type
+ if ( *ptr == 'b' && *(ptr + 1) == ' ' ) { // b
+ char cvar[256], value[512];
+ int i;
+
+ ptr += 2; // skip "b "
+
+ // skip any additional whitespace after "b "
+ while ( *ptr && (*ptr == ' ' || *ptr == '\t') ) {
+ ptr++;
+ }
+
+ // parse cvar name
+ i = 0;
+ while ( *ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\n' && *ptr != '\r' && i < sizeof(cvar) - 1 ) {
+ cvar[i++] = *ptr++;
+ }
+ cvar[i] = 0;
+
+ // skip space between cvar and value
+ while ( *ptr && (*ptr == ' ' || *ptr == '\t') ) {
+ ptr++;
+ }
+
+ // parse value
+ i = 0;
+ while ( *ptr && *ptr != '\n' && *ptr != '\r' && i < sizeof(value) - 1 ) {
+ value[i++] = *ptr++;
+ }
+ value[i] = 0;
+
+ if ( cvar[0] ) { // set the cvar
+ trap_Cvar_Set( cvar, value );
+ }
+ } else {
+ // skip unrecognized lines
+ while ( *ptr && *ptr != '\n' && *ptr != '\r' ) {
+ ptr++;
+ }
+ }
+
+ // skip to next line (handle both \n and \r\n)
+ while ( *ptr && (*ptr == '\n' || *ptr == '\r') ) {
+ ptr++;
+ }
+ }
}
@@ -340,6 +507,8 @@ G_InitGame
*/
void G_InitGame( int levelTime, int randomSeed, int restart ) {
int i;
+ // BFP - Initialize monster client number
+ level.monsterClientNum = -1;
G_Printf ("------- Game Initialization -------\n");
G_Printf ("gamename: %s\n", GAMEVERSION);
@@ -413,6 +582,9 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) {
// parse the key/value pairs and spawn gentities
G_SpawnEntitiesFromString();
+ // BFP - Load bfp_server.cfg
+ G_LoadBFPServerConfig();
+
// general initialization
G_FindTeams();
@@ -423,6 +595,9 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) {
SaveRegisteredItems();
+ // BFP - Initialize player model list
+ G_InitPlayerModelList();
+
G_LocateSpawnSpots();
G_Printf ("-----------------------------------\n");
@@ -439,8 +614,6 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) {
G_InitBots( restart );
}
- G_RemapTeamShaders();
-
}
@@ -479,7 +652,7 @@ void QDECL Com_Error ( int level, const char *error, ... ) {
char text[1024];
va_start (argptr, error);
- vsprintf (text, error, argptr);
+ Q_vsprintf (text, error, argptr);
va_end (argptr);
G_Error( "%s", text);
@@ -490,7 +663,7 @@ void QDECL Com_Printf( const char *msg, ... ) {
char text[1024];
va_start (argptr, msg);
- vsprintf (text, msg, argptr);
+ Q_vsprintf (text, msg, argptr);
va_end (argptr);
G_Printf ("%s", text);
@@ -794,7 +967,11 @@ void CalculateRanks( void ) {
}
// see if it is time to end the level
+#ifndef OLD_CHECK_EXIT_RULES
+ CheckExitRulesLater();
+#else
CheckExitRules();
+#endif
// if we are at the intermission, send the new info to everyone
if ( level.intermissiontime ) {
@@ -1017,7 +1194,7 @@ void QDECL G_LogPrintf( const char *fmt, ... ) {
Com_sprintf( string, sizeof(string), "%3i:%i%i ", min, tens, sec );
va_start( argptr, fmt );
- vsprintf( string +7 , fmt,argptr );
+ Q_vsprintf( string +7 , fmt,argptr );
va_end( argptr );
if ( g_dedicated.integer ) {
@@ -1066,7 +1243,8 @@ void LogExit( const char *string ) {
cl = &level.clients[level.sortedClients[i]];
- if ( cl->sess.sessionTeam == TEAM_SPECTATOR ) {
+ if ( cl->sess.sessionTeam == TEAM_SPECTATOR
+ && g_gametype.integer != GT_SURVIVAL ) { // BFP - Survival
continue;
}
if ( cl->pers.connected == CON_CONNECTING ) {
@@ -1199,6 +1377,11 @@ can see the last frag.
void CheckExitRules( void ) {
int i;
gclient_t *cl;
+
+#ifndef OLD_CHECK_EXIT_RULES
+ level.needToCheckExitRules = qfalse;
+#endif
+
// if at the intermission, wait for all non-bots to
// signal ready, then go to next level
if ( level.intermissiontime ) {
@@ -1250,7 +1433,8 @@ void CheckExitRules( void ) {
if ( cl->pers.connected != CON_CONNECTED ) {
continue;
}
- if ( cl->sess.sessionTeam != TEAM_FREE ) {
+ if ( cl->sess.sessionTeam != TEAM_FREE
+ && g_gametype.integer != GT_SURVIVAL ) { // BFP - Survival
continue;
}
@@ -1278,6 +1462,11 @@ void CheckExitRules( void ) {
}
}
}
+#ifndef OLD_CHECK_EXIT_RULES
+static void CheckExitRulesLater( void ) {
+ level.needToCheckExitRules = qtrue;
+}
+#endif
@@ -1303,6 +1492,11 @@ void CheckTournament( void ) {
if ( level.numPlayingClients == 0 ) {
return;
}
+
+ if ( g_gametype.integer == GT_SURVIVAL // BFP - Survival
+ || g_gametype.integer == GT_TLMS ) { // BFP - Team Last Man Standing
+ return;
+ }
if ( g_gametype.integer == GT_TOURNAMENT ) {
@@ -1321,6 +1515,12 @@ void CheckTournament( void ) {
return;
}
+ if ( level.warmupTime > 0 && level.time > level.warmupTime ) {
+ // warmup period has ended
+ level.warmupTime = 0;
+ trap_SetConfigstring( CS_WARMUP, "0" );
+ }
+
if ( level.warmupTime == 0 ) {
return;
}
@@ -1332,15 +1532,15 @@ void CheckTournament( void ) {
}
// if all players have arrived, start the countdown
- if ( level.warmupTime < 0 ) {
- if ( level.numPlayingClients == 2 ) {
- // fudge by -1 to account for extra delays
- level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
- trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
- }
+ if ( level.warmupTime < 0 && level.numPlayingClients == 2 ) {
+ // fudge by -1 to account for extra delays
+ level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
+ trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
return;
}
+ // BFP - Don't restart the map on BFP tournaments if the warmup time has counted down
+#if 0
// if the warmup time has counted down, restart
if ( level.time > level.warmupTime ) {
level.warmupTime += 10000;
@@ -1349,6 +1549,7 @@ void CheckTournament( void ) {
level.restarted = qtrue;
return;
}
+#endif
} else if ( g_gametype.integer != GT_SINGLE_PLAYER && level.warmupTime != 0 ) {
int counts[TEAM_NUM_TEAMS];
qboolean notEnough = qfalse;
@@ -1402,6 +1603,124 @@ void CheckTournament( void ) {
}
}
+/*
+=============
+CheckSurvival
+
+Once a frame, check for changes in survival player state
+=============
+*/
+void CheckSurvival( void ) {
+ if ( level.numPlayingClients == 0 ) {
+ return;
+ }
+
+ if ( g_gametype.integer != GT_SURVIVAL ) {
+ return;
+ }
+
+ // pull in a spectator if needed
+ if ( level.numPlayingClients > 0 ) {
+ AddTournamentPlayer();
+ }
+
+ // if we don't have two players, go back to "waiting for players"
+ if ( level.numPlayingClients != 2 ) {
+ if ( level.warmupTime != -1 ) {
+ level.warmupTime = -1;
+ trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
+ G_LogPrintf( "Warmup:\n" );
+ }
+ return;
+ }
+
+ if ( level.warmupTime > 0 && level.time > level.warmupTime ) {
+ // warmup period has ended
+ level.warmupTime = 0;
+ trap_SetConfigstring( CS_WARMUP, "0" );
+ }
+
+ if ( level.warmupTime == 0 ) {
+ return;
+ }
+
+ // if the warmup is changed at the console, restart it
+ if ( g_warmup.modificationCount != level.warmupModificationCount ) {
+ level.warmupModificationCount = g_warmup.modificationCount;
+ level.warmupTime = -1;
+ }
+
+ // if all players have arrived, start the countdown
+ if ( level.warmupTime < 0 && level.numPlayingClients == 2 ) {
+ // fudge by -1 to account for extra delays
+ level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
+ trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
+ }
+}
+
+/*
+=============
+CheckTeamLastManStanding
+
+Once a frame, check for changes in Team Last Man Standing player state
+=============
+*/
+void CheckTeamLastManStanding( void ) { // BFP - Team Last Man Standing
+ int redPlayers = TeamCount( -1, TEAM_RED );
+ int bluePlayers = TeamCount( -1, TEAM_BLUE );
+
+ if ( g_gametype.integer != GT_TLMS ) {
+ return;
+ }
+
+ // if we don't have both team players, go back to "waiting for players"
+ if ( redPlayers < 1 || bluePlayers < 1 ) {
+ if ( level.warmupTime != -1 ) {
+ level.warmupTime = -1;
+ trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
+ G_LogPrintf( "Warmup:\n" );
+ }
+ return;
+ }
+
+ // hit the fraglimit/timelimit
+ if ( level.teamScores[TEAM_BLUE] >= g_fraglimit.integer
+ || level.teamScores[TEAM_RED] >= g_fraglimit.integer ) {
+ level.warmupTime = -1;
+ trap_SetConfigstring( CS_WARMUP, "0" );
+ return;
+ }
+
+ if ( ( g_timelimit.integer && !level.warmupTime )
+ && ( level.time - level.startTime >= g_timelimit.integer*60000 ) ) {
+ level.warmupTime = 0;
+ return;
+ }
+
+ if ( level.warmupTime > 0 && level.time > level.warmupTime ) {
+ // warmup period has ended
+ level.warmupTime = 0;
+ trap_SetConfigstring( CS_WARMUP, "0" );
+ }
+
+ if ( level.warmupTime == 0 ) {
+ return;
+ }
+
+ // if the warmup is changed at the console, restart it
+ if ( g_warmup.modificationCount != level.warmupModificationCount ) {
+ level.warmupModificationCount = g_warmup.modificationCount;
+ level.warmupTime = -1;
+ }
+
+ // if both team players have arrived, start the countdown
+ if ( level.warmupTime < 0 && level.numPlayingClients >= 2 ) {
+ // fudge by -1 to account for extra delays
+ level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
+ trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
+ }
+}
+
/*
==================
@@ -1612,8 +1931,6 @@ Advances the non-player objects in the world
void G_RunFrame( int levelTime ) {
int i;
gentity_t *ent;
- int msec;
-int start, end;
// if we are waiting for the level to restart, do nothing
if ( level.restarted ) {
@@ -1623,7 +1940,6 @@ int start, end;
level.framenum++;
level.previousTime = level.time;
level.time = levelTime;
- msec = level.time - level.previousTime;
// get any cvar changes
G_UpdateCvars();
@@ -1631,7 +1947,6 @@ int start, end;
//
// go through all allocated objects
//
- start = trap_Milliseconds();
ent = &g_entities[0];
for (i=0 ; iinuse ) {
@@ -1691,9 +2006,7 @@ int start, end;
G_RunThink( ent );
}
-end = trap_Milliseconds();
-start = trap_Milliseconds();
// perform final fixups on the players
ent = &g_entities[0];
for (i=0 ; i < level.maxclients ; i++, ent++ ) {
@@ -1701,11 +2014,16 @@ start = trap_Milliseconds();
ClientEndFrame( ent );
}
}
-end = trap_Milliseconds();
// see if it is time to do a tournement restart
CheckTournament();
+ // BFP - Survival
+ CheckSurvival();
+
+ // BFP - Team Last Man Standing
+ CheckTeamLastManStanding();
+
// see if it is time to end the level
CheckExitRules();
diff --git a/source/game/g_mem.c b/source/game/g_mem.c
index 8f88663..5a76064 100644
--- a/source/game/g_mem.c
+++ b/source/game/g_mem.c
@@ -28,12 +28,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "g_local.h"
-#define POOLSIZE (256 * 1024)
+#define POOLSIZE (768 * 1024)
static char memoryPool[POOLSIZE];
static int allocPoint;
-void *G_Alloc( int size ) {
+void *G_Alloc( size_t size ) {
char *p;
if ( g_debugAlloc.integer ) {
@@ -41,7 +41,7 @@ void *G_Alloc( int size ) {
}
if ( allocPoint + size > POOLSIZE ) {
- G_Error( "G_Alloc: failed on allocation of %i bytes\n", size ); // bk010103 - was %u, but is signed
+ G_Error( "G_Alloc: failed on allocation of %i bytes", size );
return NULL;
}
diff --git a/source/game/g_misc.c b/source/game/g_misc.c
index 4dc79aa..7ffddfb 100644
--- a/source/game/g_misc.c
+++ b/source/game/g_misc.c
@@ -90,26 +90,43 @@ void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
}
// unlink to make sure it can't possibly interfere with G_KillBox
- trap_UnlinkEntity (player);
+ trap_UnlinkEntity( player );
- VectorCopy ( origin, player->client->ps.origin );
- player->client->ps.origin[2] += 1;
+ VectorCopy( origin, player->client->ps.origin );
+ // BFP - NOTE: Just to avoid player monster being stuck, but that doesn't work on most maps
+ // no matter if applying += 1.0f would make a difference, so that would work on a few maps
+ if ( player->client->ps.eFlags & EF_MONSTER ) {
+ player->client->ps.origin[2] += -player->r.mins[2] + 2.0f;
+ } else {
+ player->client->ps.origin[2] += 1.0f;
+ }
// spit the player out
- AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
- VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
- player->client->ps.pm_time = 160; // hold time
+ if ( angles )
+ AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
+
+ if ( player->client->sess.sessionTeam == TEAM_SPECTATOR ) {
+ VectorScale( player->client->ps.velocity, 1.25f, player->client->ps.velocity );
+ } else {
+ VectorScale( player->client->ps.velocity, g_speed.value * 1.25f, player->client->ps.velocity );
+ }
+
+ player->client->ps.pm_time = 160; // hold time
player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
// toggle the teleport bit so the client knows to not lerp
player->client->ps.eFlags ^= EF_TELEPORT_BIT;
// set angles
- SetClientViewAngle( player, angles );
+ if ( angles )
+ SetClientViewAngle( player, angles );
+
+ // unlagged
+ //G_ResetHistory( player );
// kill anything at the destination
if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
- G_KillBox (player);
+ G_KillBox( player );
}
// save results of pmove
@@ -119,7 +136,7 @@ void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
VectorCopy( player->client->ps.origin, player->r.currentOrigin );
if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
- trap_LinkEntity (player);
+ trap_LinkEntity( player );
}
}
@@ -245,7 +262,7 @@ void SP_misc_portal_camera(gentity_t *ent) {
======================================================================
*/
-void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
+static void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
vec3_t dir;
float deg;
vec3_t up, right;
@@ -292,11 +309,14 @@ static void InitShooter_Finish( gentity_t *ent ) {
ent->nextthink = 0;
}
-void InitShooter( gentity_t *ent, int weapon ) {
+static void InitShooter( gentity_t *ent, int weapon ) {
ent->use = Use_Shooter;
ent->s.weapon = weapon;
+ // BFP - No weapon items
+#if 0
RegisterItem( BG_FindItemForWeapon( weapon ) );
+#endif
G_SetMovedir( ent->s.angles, ent->movedir );
@@ -325,7 +345,7 @@ Fires at either the target or the current direction.
"random" is the number of degrees of deviance from the taget. (1.0 default)
*/
void SP_shooter_plasma( gentity_t *ent ) {
- InitShooter( ent, WP_PLASMAGUN);
+ InitShooter( ent, WP_PLASMAGUN );
}
/*QUAKED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16)
@@ -333,5 +353,5 @@ Fires at either the target or the current direction.
"random" is the number of degrees of deviance from the taget. (1.0 default)
*/
void SP_shooter_grenade( gentity_t *ent ) {
- InitShooter( ent, WP_GRENADE_LAUNCHER);
+ InitShooter( ent, WP_GRENADE_LAUNCHER );
}
diff --git a/source/game/g_missile.c b/source/game/g_missile.c
index d56e45b..b2f818d 100644
--- a/source/game/g_missile.c
+++ b/source/game/g_missile.c
@@ -24,6 +24,189 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MISSILE_PRESTEP_TIME 50
+/*
+================
+G_SplitProjectile_Fire
+================
+*/
+static void G_SplitProjectile_Fire( gentity_t *ent, vec3_t start, vec3_t dir ) {
+ gentity_t *m;
+
+ // BFP - TODO: IMPORTANT, look (homing_special) and (homing_special_spawn),
+ // (homing_special) has explosionSpawn set to split the projectiles, then if that happens,
+ // goes to (homing_special_spawn) and fires there x number of splitted projectiles
+ // explosionSpawn is a weaponNum of the attack to set
+
+ m = fire_plasma( ent, start, dir );
+ m->splitKiBall = qtrue; // handle splitted ki ball, otherwise crashes (in DLL/SO)
+
+ // example of how would be the special spawn
+ m->classname = "missile";
+ m->homing = 0.9;
+ m->homingRange = 800;
+
+ // that part doesn't have -10 in the down minimum, because needs to be like a sphere collision
+ VectorSet( m->r.mins, -m->radius, -m->radius, -m->radius );
+ VectorSet( m->r.maxs, m->radius, m->radius, m->radius );
+
+// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
+}
+
+/*
+================
+G_HandleRDMissile
+================
+*/
+static void G_HandleRDMissile( gentity_t *ent, gclient_t *client ) { // BFP - WP_PLASMAGUN would be that splitting ki ball, when pressing the attack key again, splits by the number of balls depending on the ki attack charge points had
+ vec3_t dir, angles;
+ int i;
+ // BFP - TODO: Apply minCharge and maxCharge from bfp_weapon.cfg
+ int minCharge = 2;
+ int maxCharge = 6;
+ int projectiles_to_spawn = 3;
+ // BFP - Adjust ki charge points to split
+ int splitKiChargePoints = ent->kiChargePoints - minCharge;
+ if ( maxCharge > minCharge && ent->kiChargePoints >= maxCharge ) {
+ --splitKiChargePoints;
+ }
+
+ // BFP - NOTE: That makes us to understand how the projectile is being split by x projectiles
+ /*
+ int yawAdjustments[6] = {
+ // if charge attack is 2:
+ // horizontal:
+ // ← →
+ // ↓
+ -270, -360, -90,
+ // if charge attack is 3:
+ // horizontal:
+ // ↑
+ // ← →
+ // ↓
+ -180,
+ 0, 0
+ };
+ int pitchAdjustments[6] = {
+ 0, 0, 0, 0,
+ // if charge attack is 4:
+ // horizontal:
+ // ↑
+ // ← →
+ // ↓
+ // vertical: ↑
+ -90,
+ // if charge attack is 5 and above:
+ // horizontal:
+ // ↑
+ // ← →
+ // ↓
+ // vertical: ↑ and ↓
+ 90
+ };
+ */
+
+ // determine the number of projectiles to spawn based on the ki attack charge points
+ switch( splitKiChargePoints ) {
+ case 0:
+ projectiles_to_spawn = 3;
+ break;
+ case 1:
+ projectiles_to_spawn = 4;
+ break;
+ case 2:
+ projectiles_to_spawn = 5;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ projectiles_to_spawn = 6;
+ break;
+ default:
+ return;
+ }
+
+ {
+ // this is for the new spawning projectiles,
+ // so, the owner is protected from projectile collisions and
+ // only can be damaged by explosion
+ vec3_t forward, right, up;
+ gentity_t *owner = NULL;
+ if ( ent->r.ownerNum < MAX_CLIENTS && ent->r.ownerNum >= 0 ) {
+ owner = &g_entities[ent->r.ownerNum];
+ }
+
+ if ( !owner || !owner->client ) {
+ owner = &g_entities[client->ps.clientNum];
+ }
+
+ VectorCopy( ent->s.angles, angles );
+ AngleVectors( angles, forward, right, up );
+ // correct up vector
+ up[0] = up[1] = 0;
+ up[2] = 1;
+
+ for ( i = 0; i < projectiles_to_spawn; ++i ) {
+ vec3_t origin;
+ trace_t tr;
+ VectorCopy( ent->r.currentOrigin, origin );
+
+ // a trace to correct the projectile that goes up
+ VectorCopy( origin, tr.endpos );
+ tr.endpos[2] -= 128;
+ trap_Trace( &tr, origin, ent->r.mins, ent->r.maxs, tr.endpos, ent->r.ownerNum, MASK_SOLID );
+
+ switch ( i ) {
+ case 0: // forward
+ VectorCopy( forward, dir );
+ break;
+ case 1: // left
+ VectorNegate( right, dir );
+ break;
+ case 2: // right
+ VectorCopy( right, dir );
+ break;
+ case 3: // backward
+ VectorNegate( forward, dir );
+ break;
+ case 4: // up
+ if ( tr.fraction < 1.0f && tr.entityNum != ENTITYNUM_NONE ) {
+ origin[2] = tr.endpos[2] + ent->radius + 1;
+ }
+ VectorCopy( up, dir );
+ break;
+ case 5: // down
+ VectorNegate( up, dir );
+ }
+ VectorNormalize( dir );
+ G_SplitProjectile_Fire( owner, origin, dir );
+ }
+ }
+}
+
+/*
+================
+G_RDMissile
+================
+*/
+void G_RDMissile( gentity_t *ent, gclient_t *client ) { // BFP - rdmissile (Splitting ki ball)
+ G_HandleRDMissile( ent, client );
+ ent->splitKiBall = qtrue;
+ G_FreeEntity( ent );
+}
+
+/*
+===================
+G_BreakRDMissile
+===================
+*/
+qboolean G_BreakRDMissile( gentity_t* ent ) { // BFP - Break and split the ki ball
+ if ( ent->attackType == ATK_RDMISSILE && !ent->splitKiBall ) {
+ G_RDMissile( ent, ent->parent->client );
+ return qtrue;
+ }
+ return qfalse;
+}
+
/*
================
G_BounceMissile
@@ -41,8 +224,18 @@ void G_BounceMissile( gentity_t *ent, trace_t *trace ) {
dot = DotProduct( velocity, trace->plane.normal );
VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
- if ( ent->s.eFlags & EF_BOUNCE_HALF ) {
- VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
+ // BFP - Replaced to bounces instead using EF_BOUNCE_HALF eFlag
+ if ( ent->bounces ) {
+ if ( ent && !ent->client ) {
+ VectorScale( ent->s.pos.trDelta, ent->bounceFriction, ent->s.pos.trDelta );
+ }
+
+ // BFP - noZBounce: fix the vertical component to a deterministic bounce height
+ // but... what the hell BFP dev was thinking for this behavior here? That isn't a float...
+ if ( ent->noZBounce && trace->plane.normal[2] > 0.2 ) {
+ ent->s.pos.trDelta[2] = 325.0f;
+ }
+
// check for stop
if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) {
G_SetOrigin( ent, trace->endpos );
@@ -82,7 +275,7 @@ void G_ExplodeMissile( gentity_t *ent ) {
// splash damage
if ( ent->splashDamage ) {
- if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent
+ if( G_RadiusDamage( ent, ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent
, ent->splashMethodOfDeath ) ) {
g_entities[ent->r.ownerNum].client->accuracy_hits++;
}
@@ -91,6 +284,591 @@ void G_ExplodeMissile( gentity_t *ent ) {
trap_LinkEntity( ent );
}
+
+/*
+================
+G_DetonateMissile
+
+Detonate a missile
+================
+*/
+void G_DetonateMissile( gentity_t *ent ) { // BFP - Detonate a missile, for nextthink
+ gentity_t *tempEnt = G_TempEntity( ent->r.currentOrigin, EV_MISSILE_DETONATE );
+ tempEnt->s.otherEntityNum = ent->s.number;
+
+ // BFP - That's for ki charge points, make it lesser than 0
+ ent->s.generic1 = 0;
+
+ // BFP - Splitting ki ball
+ if ( ent->parent && ent->parent->client
+ && ent->parent->client->ps.weapon == WP_PLASMAGUN
+ && ent->s.weapon == WP_PLASMAGUN && !ent->splitKiBall ) {
+ ent->parent->client->ps.weaponstate = WEAPON_READY;
+ G_RDMissile( ent, ent->parent->client );
+ }
+
+ // splash damage
+ if ( ent->splashDamage
+ && G_RadiusDamage( ent, ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent
+ , ent->splashMethodOfDeath ) ) {
+ g_entities[ent->r.ownerNum].client->accuracy_hits++;
+ }
+}
+
+
+/*
+================
+G_CollideDetonationCheck
+
+Checks the projectile collision radius and detonation point
+================
+*/
+static void G_CollideDetonationCheck( gentity_t *ent, trace_t *trace ) { // BFP - Detonation check
+ vec3_t impactPoint;
+ // BFP - NOTE: This setup solves the issue of real impact crack mark for collision radius, but original BFP didn't that (uses -1)
+ float distToPlane = DotProduct( trace->endpos, trace->plane.normal ) - trace->plane.dist;
+
+ // if there's a mover or a corpse going to be gibbed, change the impact direction, otherwise the explosion will appear very down or under the target
+ gentity_t *target = &g_entities[ trace->entityNum ];
+ if ( target
+ && ( ( target->client && target->client->ps.pm_type == PM_DEAD )
+ || target->s.eType == ET_MOVER ) ) {
+ distToPlane = 1;
+ }
+
+ VectorMA( trace->endpos, -distToPlane, trace->plane.normal, impactPoint );
+
+ if ( trace->contents & MASK_PLAYERSOLID ) {
+ gentity_t *te = G_TempEntity( impactPoint, EV_MISSILE_MISS );
+ te->s.eventParm = DirToByte( trace->plane.normal );
+ } else {
+ G_AddEvent( ent, EV_MISSILE_DETONATE, DirToByte( trace->plane.normal ) );
+ }
+}
+
+/*
+================
+G_BFPBeamImpact
+================
+*/
+static void G_BFPBeamImpact( gentity_t *ent, gentity_t *other, trace_t *trace ) { // BFP - Beam impact
+ gentity_t *nent;
+ vec3_t v;
+
+ nent = G_Spawn();
+ if ( other->takedamage && other->client ) {
+
+ G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
+ nent->s.otherEntityNum = other->s.number;
+
+ ent->enemy = other;
+
+ v[0] = other->r.currentOrigin[0] + ( other->r.mins[0] + other->r.maxs[0] ) * 0.5;
+ v[1] = other->r.currentOrigin[1] + ( other->r.mins[1] + other->r.maxs[1] ) * 0.5;
+ v[2] = other->r.currentOrigin[2] + ( other->r.mins[2] + other->r.maxs[2] ) * 0.5;
+ } else {
+ VectorCopy( trace->endpos, v );
+ // BFP - Detonation check
+ G_CollideDetonationCheck( nent, trace );
+ ent->enemy = NULL;
+ }
+
+ SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth
+
+ nent->freeAfterEvent = qtrue;
+ // change over to a normal entity right at the point of impact
+ nent->s.eType = ET_GENERAL;
+
+ G_SetOrigin( ent, v );
+ G_SetOrigin( nent, v );
+
+ if ( ent->splashDamage ) {
+ G_RadiusDamage( ent, v, ent->parent, ent->splashDamage, ent->splashRadius,
+ other, ent->splashMethodOfDeath );
+ }
+
+ trap_LinkEntity( ent );
+ trap_LinkEntity( nent );
+ // BFP - Free trails too
+ // if ( ent->s.weapon == WP_GRAPPLING_HOOK ) {
+ Weapon_BFPBeamFree( ent );
+}
+
+
+/*
+================
+G_Homing
+================
+*/
+static void G_Homing( gentity_t *ent ) { // BFP - Homing
+ if ( ent && ent->homing > 0 && ent->homingRange > 0 ) {
+ gentity_t *target = NULL, *rad = NULL;
+ vec3_t dir, raddir;
+
+ // BFP - Don't apply gravity on this part, otherwise causes stucking or out of bounds glitches
+ if ( ent->missileGravity > 0 ) {
+ // prevents the projectile from getting stuck
+ BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
+ }
+
+ while ( ( rad = FindRadius(rad, ent->r.currentOrigin, ent->homingRange) ) != NULL ) {
+ if ( IsValidTargetRadius( ent, rad ) ) {
+ VectorSubtract( rad->r.currentOrigin, ent->r.currentOrigin, raddir );
+ raddir[2] += 16;
+ if ( target == NULL ) {
+ target = rad;
+ VectorCopy( raddir, dir );
+ }
+ }
+ }
+
+ if ( target != NULL ) {
+ float homingAcceleration = ( ent->homingAcceleration <= 0 ) ? 1 : ent->homingAcceleration;
+ VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
+ VectorNormalize( dir );
+ VectorScale( dir, ent->homing, dir );
+ VectorAdd( dir, ent->r.currentAngles, dir );
+ VectorNormalize( dir );
+ VectorCopy( dir, ent->r.currentAngles );
+ VectorScale( dir, ent->speed * homingAcceleration, ent->s.pos.trDelta );
+ ent->s.pos.trTime = level.time;
+ }
+ }
+}
+
+
+/*
+================
+G_PiercingDamage
+================
+*/
+static void G_PiercingDamage( gentity_t *ent, gentity_t *target, int damage ) { // BFP - Piercing helper function
+ vec3_t velocity;
+
+ // stops homing
+ ent->homing = 0;
+ ent->homingRange = 0;
+
+ BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
+
+ ++ent->piercingTouch;
+ G_Damage( target, ent, ent->parent, velocity,
+ ent->s.origin, damage, 0, ent->methodOfDeath );
+ {
+ gentity_t *effect = G_TempEntity( target->r.currentOrigin, EV_SPARK );
+ effect->s.otherEntityNum = ent->s.number;
+ effect->s.otherEntityNum2 = target->s.number;
+ }
+ ent->piercingTime = level.time + 1000;
+ ent->piercingHitTime = level.time + 200;
+}
+
+
+/*
+================
+G_PiercingFade
+================
+*/
+static void G_PiercingFade( gentity_t *ent ) { // BFP - Piercing helper function
+ if ( ent->nextthink > level.time + 1000 ) {
+ ent->nextthink = level.time + 1000;
+ }
+ ent->piercingFade = qtrue;
+
+ // stops homing
+ ent->homing = 0;
+ ent->homingRange = 0;
+}
+
+
+/*
+================
+G_Piercing
+================
+*/
+static void G_Piercing( gentity_t *ent, trace_t *trace ) { // BFP - Piercing
+ if ( ent && ent->piercing ) {
+ gentity_t *rad = NULL, *other = &g_entities[trace->entityNum];
+ int damage = ( ent->splashDamage ) ? ent->splashDamage : ent->damage;
+ const int MAX_PIERCING_HITS = 4;
+
+ // corrects the projectile from colliding
+ BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
+
+ if ( ent->piercingFade ) {
+ return;
+ }
+
+ // disappear in about 1 second after hitting something solid (no living entities) or going out of boundaries
+ if ( ( ( trace->fraction != 1 && trace->entityNum != ENTITYNUM_NONE )
+ || ( trace->surfaceFlags & SURF_NOIMPACT ) )
+ && !other->client
+ && !ent->piercingFade && !ent->piercingTime ) {
+ G_PiercingFade( ent );
+ return;
+ }
+
+ // disappear in about 1 second after receiving the maximum number of hits
+ if ( ent->piercingTouch >= MAX_PIERCING_HITS ) {
+ G_PiercingFade( ent );
+ return;
+ }
+
+ // can still be gibbed
+ if ( other && other->client
+ && ( other->health <= 0 || other->client->ps.pm_type == PM_DEAD )
+ && other->client->pers.connected != CON_DISCONNECTED
+ && other->client->sess.sessionTeam != TEAM_SPECTATOR
+ && !OnSameTeam( other, ent->parent ) ) {
+ G_PiercingDamage( ent, other, ent->damage );
+ VectorCopy( ent->r.currentOrigin, ent->piercingOrigin );
+ }
+
+ while ( ( rad = FindRadius(rad, ent->r.currentOrigin, ent->radius) ) != NULL ) {
+ // BFP - If it's a splitting ki ball, break and split!
+ if ( rad && rad->s.eType == ET_MISSILE
+ && G_BreakRDMissile( rad ) ) {
+ continue;
+ }
+
+ if ( rad && rad->s.eType == ET_MISSILE && !rad->piercing ) { // pierce that projectile, let it explode
+ G_MissileImpact( rad, trace );
+ continue;
+ }
+
+ if ( !IsValidTargetRadius( ent, rad ) ) {
+ continue;
+ }
+
+ // around 200 msec of piercing hit
+ if ( ent->piercingHitTime && level.time >= ent->piercingHitTime ) {
+ ent->piercingHitTime = 0;
+ }
+ if ( ent->piercingHitTime && level.time < ent->piercingHitTime ) {
+ continue;
+ }
+
+ // around 1 second of piercing
+ if ( ent->piercingTime && level.time >= ent->piercingTime ) {
+ ent->piercingTime = 0;
+ }
+ if ( ent->piercingTime && level.time < ent->piercingTime ) {
+ continue;
+ }
+
+ // avoid damaging someone alive behind
+ // if the first didn't die and received damage with splash origin
+ if ( rad && rad->client && !ent->piercingHitTime ) {
+ ent->target_ent = rad;
+ if ( other && other->client && other->takedamage
+ && rad->client == other->client ) {
+ ent->target_ent = other;
+ }
+ G_PiercingDamage( ent, ent->target_ent, damage );
+ VectorCopy( ent->r.currentOrigin, ent->piercingOrigin );
+ if ( ent->target_ent->health <= 0 || ent->target_ent->client->ps.pm_type == PM_DEAD ) {
+ ent->target_ent = NULL;
+ ent->piercingHitTime = 0;
+ ent->piercingTime = 0;
+ }
+ continue;
+ }
+ }
+
+ // piercing radius
+ rad = NULL;
+ while ( ( rad = FindRadius(rad, ent->piercingOrigin, ent->radius) ) != NULL ) {
+ if ( ent->target_ent && rad == ent->target_ent
+ && ent->piercingHitTime && level.time < ent->piercingHitTime ) {
+ continue;
+ }
+
+ if ( !IsValidTargetRadius( ent, rad ) ) {
+ continue;
+ }
+
+ if ( !ent->target_ent
+ || ent->target_ent->health <= 0 || ent->target_ent->client->ps.pm_type == PM_DEAD ) {
+ ent->target_ent = rad;
+ ent->piercingHitTime = 0;
+ ent->piercingTime = 0;
+ }
+
+ if ( rad != ent->target_ent ) {
+ continue;
+ }
+
+ G_PiercingDamage( ent, ent->target_ent, damage );
+
+ if ( ent->target_ent->health <= 0 || ent->target_ent->client->ps.pm_type == PM_DEAD ) {
+ // trace along the trajectory the projectile already travelled to find
+ // the next living enemy that was crossed after the one that just died
+ vec3_t dir, scanOrigin, end;
+ trace_t tr;
+ float scanned;
+ int passent;
+ const float SCAN_DIST = 9999999;
+ gentity_t *next = NULL, *dead = ent->target_ent;
+
+ VectorCopy( ent->s.pos.trDelta, dir );
+ VectorNormalize( dir );
+ // start the scan from where the dead target was standing, not from
+ // the projectile's current position, so we pick up enemies the projectile
+ // has already physically crossed along its path
+ VectorCopy( ent->piercingOrigin, scanOrigin );
+ passent = ent->r.ownerNum;
+ scanned = 0.0f;
+
+ while ( scanned < SCAN_DIST ) {
+ float segLen;
+ gentity_t *candidate;
+
+ VectorMA( scanOrigin, SCAN_DIST - scanned, dir, end );
+ trap_Trace( &tr, scanOrigin, NULL, NULL, end, passent, MASK_PLAYERSOLID );
+ segLen = tr.fraction * ( SCAN_DIST - scanned );
+
+ if ( tr.startsolid || tr.allsolid ) {
+ scanned += 1.0f;
+ VectorMA( scanOrigin, 1.0f, dir, scanOrigin );
+ continue;
+ }
+ if ( tr.entityNum == ENTITYNUM_NONE ) {
+ break;
+ }
+
+ // step past something solid and keep scanning
+ if ( tr.entityNum != ENTITYNUM_NONE ) {
+ scanned += segLen + 1.0f;
+ VectorMA( tr.endpos, 1.0f, dir, scanOrigin );
+ passent = ent->r.ownerNum;
+ continue;
+ }
+
+ candidate = &g_entities[ tr.entityNum ];
+ if ( !candidate->client ) {
+ scanned += segLen + 1.0f;
+ VectorMA( tr.endpos, 1.0f, dir, scanOrigin );
+ passent = tr.entityNum;
+ continue;
+ }
+
+ // skip the dead target and anyone invalid
+ if ( candidate == dead
+ || candidate == ent->parent || candidate == ent
+ || candidate->r.ownerNum == ent->r.ownerNum
+ || !candidate->takedamage
+ || candidate->health <= 0 || candidate->client->ps.pm_type == PM_DEAD
+ || candidate->client->pers.connected == CON_DISCONNECTED
+ || candidate->client->sess.sessionTeam == TEAM_SPECTATOR
+ || OnSameTeam( candidate, ent->parent ) ) {
+ scanned += segLen + 1.0f;
+ VectorMA( tr.endpos, 1.0f, dir, scanOrigin );
+ passent = tr.entityNum;
+ continue;
+ }
+ next = candidate;
+ break;
+ }
+
+ ent->target_ent = NULL;
+ ent->piercingHitTime = 0;
+ ent->piercingTime = 0;
+ // snap piercing origin to the next target so the radius loop finds them
+ if ( next ) {
+ VectorCopy( next->r.currentOrigin, ent->piercingOrigin );
+ } else {
+ VectorCopy( ent->r.currentOrigin, ent->piercingOrigin );
+ }
+ rad = NULL;
+ }
+ }
+ }
+}
+
+
+/*
+================
+G_Reflective
+================
+*/
+void G_Reflective( gentity_t *ent, qboolean useViewAngles, const vec3_t start ) { // BFP - Reflective
+ if ( ent && ent->reflective ) {
+ gentity_t *rad = NULL;
+ vec3_t forward;
+ // BFP - TODO: For weapon config: range, ...
+ float range = 500;
+ int i;
+
+ // BFP - For hitscan weapons: reflect toward where the player aims
+ if ( useViewAngles ) {
+ AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );
+ }
+
+ for ( i = 0; i < level.num_entities; ++i ) {
+ rad = &g_entities[i];
+ if ( !rad->inuse || rad->s.eType != ET_MISSILE ) {
+ continue;
+ }
+ if ( rad->parent == ent || rad->r.ownerNum == ent->s.number ) {
+ continue;
+ }
+ if ( rad->freeAfterEvent || rad->nextthink <= level.time ) {
+ continue;
+ }
+ if ( rad->attackType == ATK_BEAM || rad->attackType == ATK_SBEAM ) { // cannot defend from beam & sbeam attack types
+ continue;
+ }
+ if ( rad->piercing ) { // cannot defend from piercing attacks
+ continue;
+ }
+
+ BG_EvaluateTrajectory( &rad->s.pos, level.time, rad->r.currentOrigin );
+
+ if ( Distance( rad->r.currentOrigin, start ) > range ) {
+ continue;
+ }
+
+ // BFP - Projectile weapons: reflect using the missile's own travel direction
+ if ( !useViewAngles ) {
+ VectorNormalize2( rad->s.pos.trDelta, forward );
+ }
+
+ VectorScale( forward, rad->speed, rad->s.pos.trDelta );
+ VectorCopy( rad->r.currentOrigin, rad->s.pos.trBase );
+ rad->s.pos.trTime = level.time;
+
+ rad->r.ownerNum = ent->s.number;
+ rad->parent = ent;
+
+ rad->homingRange = 0;
+ rad->homing = 0;
+ }
+ }
+}
+
+
+/*
+================
+G_MissileGravity
+================
+*/
+static void G_MissileGravity( gentity_t *ent ) { // BFP - Missile gravity
+ if ( ent && ent->missileGravity > 0 && !ent->noZBounce ) {
+ ent->s.pos.trDelta[2] -= ent->missileGravity;
+ VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
+ ent->s.pos.trTime = level.time;
+ }
+}
+
+
+/*
+=====================
+G_MissileAcceleration
+=====================
+*/
+static void G_MissileAcceleration( gentity_t *ent ) { // BFP - Missile acceleration
+ if ( ent && ent->missileAcceleration > 0 ) {
+ VectorScale( ent->s.pos.trDelta, ent->missileAcceleration, ent->s.pos.trDelta );
+ VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
+ ent->s.pos.trTime = level.time;
+ }
+}
+
+
+/*
+=====================
+G_ChargeDamageScaling
+=====================
+*/
+static void G_ChargeDamageScaling( gentity_t *ent, int minCharge ) { // BFP - Charge damage scaling
+ int chargeLevel = ent->kiChargePoints - minCharge;
+ float r = ent->radius + chargeLevel * ent->chargeRadiusMult;
+ float rdown;
+ float er = ent->splashRadius + chargeLevel * ent->chargeExpRadiusMult;
+
+ if ( ent->kiChargePoints <= 0 || ent->kiChargePoints < minCharge ) {
+ return;
+ }
+
+ // direct damage
+ ent->damage += chargeLevel * ent->chargeDamageMult;
+ if ( ent->maxDamage > 0 && ent->damage > ent->maxDamage ) {
+ ent->damage = ent->maxDamage;
+ }
+
+ // collision radius (hitbox)
+ if ( ent->maxRadius > 0 && r > ent->maxRadius ) {
+ r = ent->maxRadius;
+ }
+ ent->radius = r;
+ rdown = r;
+ if ( ent->attackType != ATK_BEAM && ent->attackType != ATK_SBEAM ) {
+ rdown = 10;
+ }
+ VectorSet( ent->r.mins, -r, -r, -rdown );
+ VectorSet( ent->r.maxs, r, r, r );
+
+ // explosionRadius (splashRadius)
+ if ( ent->maxExpRadius > 0 && er > ent->maxExpRadius ) {
+ er = ent->maxExpRadius;
+ }
+ ent->splashRadius = (int)er;
+}
+
+
+/*
+===========
+G_Priority
+===========
+*/
+static void G_Priority( gentity_t *ent, trace_t *trace ) { // BFP - Priority
+ int entityList[MAX_GENTITIES];
+ int numEntities, i;
+ vec3_t mins, maxs;
+ gentity_t *other;
+
+ if ( !ent ) {
+ return;
+ }
+
+ VectorAdd( ent->r.mins, ent->r.currentOrigin, mins );
+ VectorAdd( ent->r.maxs, ent->r.currentOrigin, maxs );
+
+ numEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+ for ( i = 0; i < numEntities; ++i ) {
+ other = &g_entities[entityList[i]];
+
+ if ( !other->inuse || other == ent ) {
+ continue;
+ }
+ if ( other->s.eType != ET_MISSILE ) { // projectiles only
+ continue;
+ }
+ if ( other->parent == ent->parent ) {
+ continue;
+ }
+
+ if ( other->attackType == ATK_BEAM
+ || other->attackType == ATK_SBEAM
+ || other->attackType == ATK_RDMISSILE
+ || other->attackType == ATK_FORCEFIELD ) {
+ continue;
+ }
+
+ if ( ent->priority > other->priority ) {
+ G_MissileImpact( other, trace );
+ } else if ( ent->priority < other->priority ) {
+ G_MissileImpact( ent, trace );
+ return;
+ } else {
+ G_MissileImpact( other, trace );
+ G_MissileImpact( ent, trace );
+ return;
+ }
+ }
+}
+
+
/*
================
G_MissileImpact
@@ -100,11 +878,20 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
gentity_t *other;
qboolean hitClient = qfalse;
+ // BFP - Don't explode if going to impact outside map boundaries
+ if ( trace->surfaceFlags & SURF_NOIMPACT ) {
+ return;
+ }
+
other = &g_entities[trace->entityNum];
+ // BFP - That's for ki charge points, make it lesser than 0
+ ent->s.generic1 = 0;
+
// check for bounce
if ( !other->takedamage &&
- ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
+ // BFP - Replaced to bounces instead using EF_BOUNCE and EF_BOUNCE_HALF eFlags
+ ent->bounces ) {
G_BounceMissile( ent, trace );
G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
return;
@@ -116,20 +903,56 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
if ( ent->damage ) {
vec3_t velocity;
+ // BFP - When blocking... Deflect the projectile!
+ if ( other->client && ( other->client->ps.pm_flags & PMF_BLOCK ) ) {
+ // PUSH!
+ BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
+ // BFP - No vector length check
+#if 0
+ if ( VectorLength( velocity ) == 0 ) {
+ velocity[2] = 1;
+ }
+#endif
+
+ // BFP - Consume 10% of ki when deflecting the projectile and apply knockback
+ if ( other->client->blockKnockbackTime <= 0 ) {
+ other->client->ps.stats[STAT_KI] -= other->client->ps.stats[STAT_MAX_KI] * 0.1;
+ other->client->blockKnockbackTime = level.time + 250;
+ }
+
+ G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
+ ent->s.origin, ent->damage,
+ 0, ent->methodOfDeath);
+
+ // DEFLECT!
+ G_BounceMissile( ent, trace );
+ return;
+ }
+
if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
g_entities[ent->r.ownerNum].client->accuracy_hits++;
hitClient = qtrue;
}
BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
+ // BFP - No vector length check
+#if 0
if ( VectorLength( velocity ) == 0 ) {
velocity[2] = 1; // stepped on a grenade
}
+#endif
+
G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
ent->s.origin, ent->damage,
0, ent->methodOfDeath);
}
}
+ // BFP - Changed "hook" to "beam" classname
+ if ( ent->attackType == ATK_BEAM || ent->attackType == ATK_SBEAM ) {
+ G_BFPBeamImpact( ent, other, trace );
+ return;
+ }
+
// BFP - no hook
#if 0
if (!strcmp(ent->classname, "hook")) {
@@ -187,7 +1010,8 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
} else if( trace->surfaceFlags & SURF_METALSTEPS ) {
G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
} else {
- G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
+ // BFP - Detonation check
+ G_CollideDetonationCheck( ent, trace );
}
ent->freeAfterEvent = qtrue;
@@ -201,7 +1025,7 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
// splash damage (doesn't apply to person directly hit)
if ( ent->splashDamage ) {
- if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
+ if( G_RadiusDamage( ent, trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
other, ent->splashMethodOfDeath ) ) {
if( !hitClient ) {
g_entities[ent->r.ownerNum].client->accuracy_hits++;
@@ -221,6 +1045,8 @@ void G_RunMissile( gentity_t *ent ) {
vec3_t origin;
trace_t tr;
int passent;
+ // BFP - Shortcut variable to make sure who is using this projectile
+ gclient_t *client = ent->parent->client;
// get current position
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
@@ -245,9 +1071,97 @@ void G_RunMissile( gentity_t *ent ) {
VectorCopy( tr.endpos, ent->r.currentOrigin );
}
+ // BFP - Ignore sinking corpses: their collision bbox lags behind their actual sinking position
+ if ( tr.fraction != 1
+ && g_entities[tr.entityNum].s.eType == ET_PLAYER
+ && g_entities[tr.entityNum].physicsObject ) {
+ trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, tr.entityNum, ent->clipmask );
+ VectorCopy( tr.endpos, ent->r.currentOrigin );
+ }
+
trap_LinkEntity( ent );
+ // BFP - When the player stopped shooting the charged beam/projectile by pressing the attack key
+ // These are sample weapons used as examples for BFP:
+
+ // WP_PLASMAGUN would be that splitting ki ball, when pressing the attack key again, splits by the number of balls depending on the ki attack charge points had
+ if ( client
+ && client->ps.weaponstate != WEAPON_FIRING
+ && client->ps.weapon == WP_PLASMAGUN
+ && ent->s.weapon == WP_PLASMAGUN && !ent->splitKiBall ) {
+ client->ps.weaponTime = 0;
+ }
+
+ // BFP - Priority
+ G_Priority( ent, &tr );
+
+ // BFP - Reflective
+ G_Reflective( ent, qfalse, ent->r.currentOrigin );
+
+ // BFP - Missile gravity
+ G_MissileGravity( ent );
+
+ // BFP - Missile acceleration
+ G_MissileAcceleration( ent );
+
+ if ( client
+ && ( client->pers.cmd.buttons & BUTTON_ATTACK )
+ && client->ps.weapon == WP_PLASMAGUN
+ && ent->s.weapon == WP_PLASMAGUN && !ent->splitKiBall ) {
+ G_RDMissile( ent, client );
+ client->ps.weaponstate = WEAPON_READY;
+ return;
+ }
+
+ // BFP - Piercing weapons
+ G_Piercing( ent, &tr );
+
+ // BFP - Homing weapons
+ G_Homing( ent );
+
+ // WP_GRAPPLING_HOOK would be the beam
+ // BFP - Beam handling
+ if ( client
+ && ent->s.weapon == WP_GRAPPLING_HOOK ) {
+ if ( client->ps.weaponstate != WEAPON_ACTIVE
+ && client->ps.weaponstate != WEAPON_BEAMSTRUGGLE ) {
+ G_MissileImpact( ent, &tr );
+ client->ps.weaponTime = 0;
+ }
+ if ( tr.fraction != 1 && !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
+ G_MissileImpact( ent, &tr );
+ client->ps.weaponTime = 0;
+ } else {
+ Weapon_BFPBeamRun( ent );
+ Weapon_SBeam_Run( ent );
+ }
+
+ if ( client->ps.pm_type == PM_DEAD // if just died, then stop
+ || client->pers.connected == CON_DISCONNECTED // if disconnected, stop
+ || ( tr.surfaceFlags & SURF_NOIMPACT )
+ || tr.fraction != 1 ) {
+ Weapon_BFPBeamFree( ent );
+ return;
+ }
+ }
+
if ( tr.fraction != 1 ) {
+
+ // BFP - When the charged projectile touches into something, return to ready state
+ if ( client
+ && ( client->ps.weaponstate == WEAPON_ACTIVE
+ || client->ps.weaponstate == WEAPON_BEAMSTRUGGLE )
+ && ent->s.weapon != WP_PLASMAGUN ) {
+ client->ps.weaponstate = WEAPON_READY;
+ }
+
+ // BFP - Splitting ki ball
+ if ( client
+ && ent->s.weapon == WP_PLASMAGUN
+ && !ent->splitKiBall ) {
+ client->ps.weaponstate = WEAPON_READY;
+ }
+
// never explode or bounce on sky
if ( tr.surfaceFlags & SURF_NOIMPACT ) {
// BFP - no hook
@@ -257,10 +1171,25 @@ void G_RunMissile( gentity_t *ent ) {
ent->parent->client->hook = NULL;
}
#endif
- G_FreeEntity( ent );
+
+ // BFP - Don't disappear instantly on piercing weapons
+ if ( !ent->piercing ) {
+ G_FreeEntity( ent );
+ }
+ return;
+ }
+
+ // BFP - Splitting ki ball
+ if ( client
+ && ent->s.weapon == WP_PLASMAGUN && !ent->splitKiBall ) {
+ G_RDMissile( ent, client );
return;
}
- G_MissileImpact( ent, &tr );
+
+ // BFP - Don't explode on piercing weapons
+ if ( !ent->piercing ) {
+ G_MissileImpact( ent, &tr );
+ }
if ( ent->s.eType != ET_MISSILE ) {
return; // exploded
}
@@ -284,9 +1213,10 @@ gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) {
VectorNormalize (dir);
bolt = G_Spawn();
- bolt->classname = "plasma";
+ bolt->classname = "rdmissile";
+ bolt->attackType = ATK_RDMISSILE; // BFP - Attack type
bolt->nextthink = level.time + 10000;
- bolt->think = G_ExplodeMissile;
+ bolt->think = G_DetonateMissile;
bolt->s.eType = ET_MISSILE;
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_PLASMAGUN;
@@ -300,14 +1230,33 @@ gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) {
bolt->clipmask = MASK_SHOT;
bolt->target_ent = NULL;
+ // BFP - Save ki charged points
+ bolt->kiChargePoints = self->client->ps.generic1;
+ bolt->s.generic1 = self->client->ps.generic1;
+
+ bolt->splitKiBall = qfalse; // BFP - For splitting ki ball
+
+ // BFP - Speed similar to BFP ki blast attack (missileSpeed)
+ bolt->speed = 700;
+
+ // BFP - Projectile radius
+ bolt->radius = 30;
+
+ // BFP - Priority
+ bolt->priority = 0;
+
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
- VectorScale( dir, 2000, bolt->s.pos.trDelta );
+ VectorScale( dir, bolt->speed, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->r.currentOrigin);
+ // BFP - Collision radius
+ VectorSet( bolt->r.mins, -bolt->radius, -bolt->radius, -10 );
+ VectorSet( bolt->r.maxs, bolt->radius, bolt->radius, bolt->radius );
+
return bolt;
}
@@ -322,34 +1271,85 @@ fire_grenade
gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) {
gentity_t *bolt;
+ // BFP - TODO: For weapon config, apply the properties from bfp_weapon.cfg
+
+ // BFP - Like (multiball) weapon
+
VectorNormalize (dir);
bolt = G_Spawn();
- bolt->classname = "grenade";
- bolt->nextthink = level.time + 2500;
+ // BFP - missileDuration, bounces...
+ bolt->missileDuration = 1500;
+ bolt->bounces = qtrue;
+
+ bolt->classname = "missile";
+ bolt->attackType = ATK_MISSILE; // BFP - Attack type
+ bolt->nextthink = level.time + bolt->missileDuration;
bolt->think = G_ExplodeMissile;
bolt->s.eType = ET_MISSILE;
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_GRENADE_LAUNCHER;
- bolt->s.eFlags = EF_BOUNCE_HALF;
bolt->r.ownerNum = self->s.number;
bolt->parent = self;
- bolt->damage = 100;
- bolt->splashDamage = 100;
- bolt->splashRadius = 150;
+ bolt->damage = 8;
+ bolt->splashDamage = 8;
+ bolt->splashRadius = 250;
bolt->methodOfDeath = MOD_GRENADE;
bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
bolt->clipmask = MASK_SHOT;
bolt->target_ent = NULL;
- bolt->s.pos.trType = TR_GRAVITY;
+ // BFP - Applying radius, missileSpeed, bounceFriction, ...
+ bolt->radius = 50;
+ bolt->speed = 3000;
+ bolt->bounceFriction = 0.9;
+ bolt->noZBounce = 0; //0.000001; // weird value found in the cfg files, really, it's a boolean though :/
+
+ bolt->homing = 0.3;
+ bolt->homingRange = 300;
+ bolt->homingAcceleration = 0;
+
+ bolt->missileGravity = 100;
+ bolt->missileAcceleration = 0;
+
+ bolt->s.pos.trType = TR_LINEAR;
+ // BFP - missileGravity
+ if ( bolt->missileGravity > 0 ) {
+ bolt->s.pos.trType = TR_GRAVITY;
+ }
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
+ if ( bolt->missileAcceleration > 0 ) { // BFP - Start from the very first frame if there's acceleration
+ bolt->s.pos.trTime = level.time;
+ }
VectorCopy( start, bolt->s.pos.trBase );
- VectorScale( dir, 700, bolt->s.pos.trDelta );
+ VectorScale( dir, bolt->speed, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->r.currentOrigin);
+ // BFP - Set delta time to avoid timescale < 1 issues
+ bolt->deltaTime = level.time;
+
+ // BFP - Collision radius
+ VectorSet( bolt->r.mins, -bolt->radius, -bolt->radius, -10 );
+ VectorSet( bolt->r.maxs, bolt->radius, bolt->radius, bolt->radius );
+
+ // BFP - Save ki charged points
+ bolt->kiChargePoints = self->client->ps.generic1;
+ bolt->s.generic1 = self->client->ps.generic1;
+
+ // BFP - Priority
+ bolt->priority = 1;
+
+ // BFP - Weapon config charge fields
+ bolt->chargeDamageMult = 20;
+ bolt->maxDamage = 80;
+ bolt->chargeRadiusMult = 25;
+ bolt->maxRadius = 75;
+ bolt->chargeExpRadiusMult = 120;
+ bolt->maxExpRadius = 700;
+ G_ChargeDamageScaling( bolt, 2 );
+
return bolt;
}
@@ -403,11 +1403,13 @@ fire_rocket
*/
gentity_t *fire_rocket (gentity_t *self, vec3_t start, vec3_t dir) {
gentity_t *bolt;
+ // BFP - Modified to fit (ki_blast) weapon properties
VectorNormalize (dir);
bolt = G_Spawn();
- bolt->classname = "rocket";
+ bolt->classname = "missile";
+ bolt->attackType = ATK_MISSILE; // BFP - Attack type
bolt->nextthink = level.time + 15000;
bolt->think = G_ExplodeMissile;
bolt->s.eType = ET_MISSILE;
@@ -415,21 +1417,48 @@ gentity_t *fire_rocket (gentity_t *self, vec3_t start, vec3_t dir) {
bolt->s.weapon = WP_ROCKET_LAUNCHER;
bolt->r.ownerNum = self->s.number;
bolt->parent = self;
- bolt->damage = 100;
- bolt->splashDamage = 100;
- bolt->splashRadius = 120;
+ bolt->damage = 20;
+ bolt->splashDamage = 20;
+ bolt->splashRadius = 125;
bolt->methodOfDeath = MOD_ROCKET;
bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH;
bolt->clipmask = MASK_SHOT;
bolt->target_ent = NULL;
+ // BFP - Save ki charged points
+ bolt->kiChargePoints = self->client->ps.generic1;
+ bolt->s.generic1 = self->client->ps.generic1;
+
+ // BFP - Speed similar to BFP ki blast attack (missileSpeed)
+ bolt->speed = 6000;
+
+ // BFP - Projectile radius
+ bolt->radius = 20;
+
+ // BFP - Priority
+ bolt->priority = 0;
+
+ bolt->missileGravity = 0;
+ bolt->missileAcceleration = 0;
+
bolt->s.pos.trType = TR_LINEAR;
+ // BFP - missileGravity
+ if ( bolt->missileGravity > 0 ) {
+ bolt->s.pos.trType = TR_GRAVITY;
+ }
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
- VectorScale( dir, 900, bolt->s.pos.trDelta );
+ VectorScale( dir, bolt->speed, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->r.currentOrigin);
+ // BFP - Collision radius
+ VectorSet( bolt->r.mins, -bolt->radius, -bolt->radius, -10 );
+ VectorSet( bolt->r.maxs, bolt->radius, bolt->radius, bolt->radius );
+
+ // BFP - Charge scaling
+ G_ChargeDamageScaling( bolt, 0 );
+
return bolt;
}
// BFP - no hook
@@ -470,3 +1499,263 @@ gentity_t *fire_grapple (gentity_t *self, vec3_t start, vec3_t dir) {
return hook;
}
#endif
+
+/*
+=================
+fire_bfpbeam
+=================
+*/
+gentity_t *fire_bfpbeam (gentity_t *self, vec3_t start, vec3_t dir) {
+ gentity_t *beam;
+
+ VectorNormalize( dir );
+
+ beam = G_Spawn();
+ beam->classname = "beam";
+ beam->attackType = ATK_BEAM; // BFP - Attack type
+ beam->nextthink = level.time + 20000;
+ beam->think = Weapon_BFPBeamFree;
+ beam->s.eType = ET_MISSILE;
+ beam->r.svFlags = SVF_USE_CURRENT_ORIGIN;
+ beam->s.weapon = WP_GRAPPLING_HOOK;
+ beam->r.ownerNum = self->s.number;
+ beam->methodOfDeath = MOD_KI_ATTACK;
+ beam->clipmask = MASK_SHOT;
+ beam->parent = self;
+ beam->target_ent = NULL;
+
+ beam->damage = 80;
+ beam->splashDamage = 80;
+ beam->splashRadius = 350;
+ beam->splashMethodOfDeath = MOD_KI_ATTACK;
+
+ // BFP - Save ki charged points
+ beam->kiChargePoints = self->client->ps.generic1;
+ beam->s.generic1 = self->client->ps.generic1;
+
+ // BFP - Speed similar to BFP lightning blast/heaven's wrath attack (missileSpeed)
+ beam->speed = 2000;
+
+ // BFP - Projectile radius
+ beam->radius = 30;
+
+ // BFP - Piercing
+ beam->piercing = 0;
+
+ // BFP - Priority
+ beam->priority = 1;
+
+ beam->s.pos.trType = TR_LINEAR;
+ beam->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
+ beam->s.otherEntityNum = self->s.number; // use to match beam in client
+ VectorCopy( start, beam->s.pos.trBase );
+ VectorScale( dir, beam->speed, beam->s.pos.trDelta ); // speed
+ SnapVector( beam->s.pos.trDelta ); // save net bandwidth
+ VectorCopy( start, beam->r.currentOrigin );
+
+ // BFP - Set beam delta time and distance to avoid timescale < 1 issues
+ beam->deltaTime = level.time;
+ beam->distance = 0;
+
+ // BFP - TODO: For weapon config, if uses chargeAttack, maxRadius, chargeRadiusMult, calculate the radius here
+#if 0
+ if ( beam->chargeAttack ) {
+ float r = cfg->radius + ( beam->kiChargePoints - cfg->minCharge ) * cfg->chargeRadiusMult;
+ if ( r > cfg->maxRadius && cfg->maxRadius > 0 ) {
+ r = cfg->maxRadius;
+ }
+ radius = r;
+ }
+#endif
+
+ // BFP - Collision radius
+ VectorSet( beam->r.mins, -beam->radius, -beam->radius, -beam->radius );
+ VectorSet( beam->r.maxs, beam->radius, beam->radius, beam->radius );
+
+ // BFP - Weapon config charge fields
+ beam->damage = 10;
+ beam->maxDamage = 50;
+ beam->splashDamage = 10;
+ beam->splashRadius = 350;
+ beam->chargeDamageMult = 10;
+ beam->chargeExpRadiusMult = 200;
+ // beam->maxExpRadius = 0;
+ G_ChargeDamageScaling( beam, 2 );
+
+ return beam;
+}
+
+// BFP - Just a testing disk proyectile
+/*
+=================
+fire_disk
+=================
+*/
+gentity_t *fire_disk (gentity_t *self, vec3_t start, vec3_t dir) {
+ gentity_t *disk;
+
+ VectorNormalize ( dir );
+
+ disk = G_Spawn();
+ disk->classname = "missile";
+ disk->attackType = ATK_MISSILE; // BFP - Attack type
+ disk->nextthink = level.time + 10000;
+ disk->think = G_FreeEntity;
+ disk->s.eType = ET_MISSILE;
+ disk->r.svFlags = SVF_USE_CURRENT_ORIGIN;
+ disk->s.weapon = WP_BFG;
+ disk->r.ownerNum = self->s.number;
+ disk->parent = self;
+ disk->damage = 20;
+ disk->splashDamage = 20;
+ disk->splashRadius = 120;
+ disk->methodOfDeath = MOD_KI_ATTACK;
+ disk->splashMethodOfDeath = MOD_KI_ATTACK;
+ disk->clipmask = MASK_SHOT;
+ disk->target_ent = NULL;
+
+ // BFP - Save ki charged points
+ disk->kiChargePoints = self->client->ps.generic1;
+ disk->s.generic1 = self->client->ps.generic1;
+
+ disk->speed = 1000;
+
+ // BFP - Projectile radius
+ disk->radius = 75;
+
+ // BFP - Priority
+ disk->priority = 0;
+
+ disk->piercing = 1;
+ disk->homing = 0.5;
+ disk->homingRange = 2000;
+ disk->homingAcceleration = 0;
+
+ // BFP - Extra knockback
+ disk->extraKnockback = -1000;
+
+ disk->s.pos.trType = TR_LINEAR;
+ disk->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
+ VectorCopy( start, disk->s.pos.trBase );
+ VectorScale( dir, disk->speed, disk->s.pos.trDelta );
+ SnapVector( disk->s.pos.trDelta ); // save net bandwidth
+ VectorCopy (start, disk->r.currentOrigin);
+
+ // BFP - Collision radius
+ VectorSet( disk->r.mins, -disk->radius, -disk->radius, -10 );
+ VectorSet( disk->r.maxs, disk->radius, disk->radius, disk->radius );
+
+ // BFP - Charge scaling
+ G_ChargeDamageScaling( disk, 2 );
+
+ return disk;
+}
+
+// BFP - Just a testing forcefield
+/*
+=================
+fire_forcefield
+=================
+*/
+gentity_t *fire_forcefield (gentity_t *self, vec3_t start)
+{
+ // BFP - TODO: bfp_weapon.cfg: radius, ...
+ gentity_t *field;
+
+ field = G_Spawn();
+ field->classname = "forcefield";
+ field->attackType = ATK_FORCEFIELD; // BFP - Attack type
+ field->nextthink = level.time + 200;
+ field->think = Weapon_Forcefield_Think;
+ field->s.eType = ET_MISSILE;
+ field->r.svFlags = SVF_USE_CURRENT_ORIGIN;
+ field->s.weapon = WP_SHOTGUN;
+ field->r.ownerNum = self->s.number;
+ field->parent = self;
+ field->clipmask = MASK_SHOT;
+ field->target_ent = NULL;
+
+ field->damage = 20;
+ field->splashDamage = 0;
+ field->splashRadius = 0;
+ field->methodOfDeath = MOD_KI_ATTACK;
+ field->splashMethodOfDeath = MOD_KI_ATTACK;
+
+ // BFP - Save ki charged points
+ field->kiChargePoints = self->client->ps.generic1;
+ field->s.generic1 = self->client->ps.generic1;
+
+ field->radius = 900;
+ field->blinding = qfalse;
+
+ field->s.pos.trType = TR_STATIONARY;
+ field->s.pos.trTime = level.time;
+ VectorCopy( start, field->s.pos.trBase );
+ VectorClear( field->s.pos.trDelta );
+
+ VectorCopy( start, field->r.currentOrigin );
+
+ self->client->hook = field;
+
+ return field;
+}
+
+// BFP - Just a testing sbeam
+/*
+=================
+fire_sbeam
+=================
+*/
+gentity_t *fire_sbeam (gentity_t *self, vec3_t start, vec3_t dir)
+{
+ // BFP - TODO: bfp_weapon.cfg: radius, ...
+ gentity_t *sbeam;
+
+ sbeam = G_Spawn();
+ sbeam->classname = "sbeam";
+ sbeam->attackType = ATK_SBEAM; // BFP - Attack type
+ sbeam->nextthink = level.time + 20000;
+ sbeam->think = G_FreeEntity;
+ sbeam->s.eType = ET_MISSILE;
+ sbeam->r.svFlags = SVF_USE_CURRENT_ORIGIN;
+ sbeam->s.weapon = WP_GRAPPLING_HOOK;
+ sbeam->r.ownerNum = self->s.number;
+ sbeam->parent = self;
+ sbeam->clipmask = MASK_SHOT;
+ sbeam->target_ent = NULL;
+
+ sbeam->damage = 15;
+ sbeam->splashDamage = 15;
+ sbeam->splashRadius = 200;
+ sbeam->methodOfDeath = MOD_KI_ATTACK;
+ sbeam->splashMethodOfDeath = MOD_KI_ATTACK;
+
+ sbeam->radius = 50;
+
+ // BFP - Save ki charged points
+ sbeam->kiChargePoints = self->client->ps.generic1;
+ sbeam->s.generic1 = self->client->ps.generic1;
+
+ // BFP - Speed similar to BFP mouth beam (missileSpeed)
+ sbeam->speed = 1000;
+
+ // BFP - Priority
+ sbeam->priority = 0;
+
+ sbeam->s.pos.trType = TR_LINEAR;
+ sbeam->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
+ sbeam->s.otherEntityNum = self->s.number; // use to match sbeam in client
+ VectorCopy( start, sbeam->s.pos.trBase );
+ VectorScale( dir, sbeam->speed, sbeam->s.pos.trDelta ); // speed
+ SnapVector( sbeam->s.pos.trDelta ); // save net bandwidth
+ VectorCopy( start, sbeam->r.currentOrigin );
+
+ // BFP - Collision radius
+ VectorSet( sbeam->r.mins, -sbeam->radius, -sbeam->radius, -sbeam->radius );
+ VectorSet( sbeam->r.maxs, sbeam->radius, sbeam->radius, sbeam->radius );
+
+ // BFP - Charge scaling
+ G_ChargeDamageScaling( sbeam, 0 );
+
+ return sbeam;
+}
diff --git a/source/game/g_mover.c b/source/game/g_mover.c
index c496b10..2c563aa 100644
--- a/source/game/g_mover.c
+++ b/source/game/g_mover.c
@@ -166,7 +166,7 @@ qboolean G_TryPushingEntity( gentity_t *check, gentity_t *pusher, vec3_t move, v
// may have pushed them off an edge
if ( check->s.groundEntityNum != pusher->s.number ) {
- check->s.groundEntityNum = -1;
+ check->s.groundEntityNum = ENTITYNUM_NONE;
}
block = G_TestEntityPosition( check );
@@ -191,7 +191,7 @@ qboolean G_TryPushingEntity( gentity_t *check, gentity_t *pusher, vec3_t move, v
VectorCopy( (pushed_p-1)->angles, check->s.apos.trBase );
block = G_TestEntityPosition (check);
if ( !block ) {
- check->s.groundEntityNum = -1;
+ check->s.groundEntityNum = ENTITYNUM_NONE;
pushed_p--;
return qtrue;
}
@@ -319,6 +319,10 @@ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **
for ( e = 0 ; e < listedEntities ; e++ ) {
check = &g_entities[ entityList[ e ] ];
+ if ( !check->inuse || ( check->health <= 0 && check->takedamage ) ) {
+ continue;
+ }
+
// only push items and players
if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) {
continue;
@@ -793,26 +797,29 @@ Touch_DoorTriggerSpectator
================
*/
static void Touch_DoorTriggerSpectator( gentity_t *ent, gentity_t *other, trace_t *trace ) {
- int i, axis;
- vec3_t origin, dir, angles;
+ int axis;
+ vec3_t origin;
+ float len, doorMin, doorMax;
+
+ VectorCopy( other->s.origin, origin );
axis = ent->count;
- VectorClear(dir);
- if (fabs(other->s.origin[axis] - ent->r.absmax[axis]) <
- fabs(other->s.origin[axis] - ent->r.absmin[axis])) {
- origin[axis] = ent->r.absmin[axis] - 10;
- dir[axis] = -1;
- }
- else {
- origin[axis] = ent->r.absmax[axis] + 10;
- dir[axis] = 1;
+ doorMax = other->s.origin[axis] - ent->r.absmax[axis];
+ doorMin = other->s.origin[axis] - ent->r.absmin[axis];
+
+ if ( fabs(doorMax) < fabs(doorMin) ) {
+ origin[axis] = ent->r.absmin[axis] - 1;
+ len = doorMax + 120;
+ } else {
+ origin[axis] = ent->r.absmax[axis] + 1;
+ len = 120 - doorMin;
}
- for (i = 0; i < 3; i++) {
- if (i == axis) continue;
- origin[i] = (ent->r.absmin[i] + ent->r.absmax[i]) * 0.5;
+
+ if ( len >= 40 ) {
+ return;
}
- vectoangles(dir, angles);
- TeleportPlayer(other, origin, angles );
+
+ TeleportPlayer( other, origin, NULL );
}
/*
@@ -847,6 +854,10 @@ void Think_SpawnNewDoorTrigger( gentity_t *ent ) {
vec3_t mins, maxs;
int i, best;
+ if (!ent) {
+ return;
+ }
+
// set all of the slaves as shootable
for ( other = ent ; other ; other = other->teamchain ) {
other->takedamage = qtrue;
@@ -913,8 +924,13 @@ void SP_func_door (gentity_t *ent) {
vec3_t size;
float lip;
- ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/doors/dr1_strt.wav");
- ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/doors/dr1_end.wav");
+ if ( ent->spawnflags & 32 ) {
+ ent->sound1to2 = ent->sound2to1 = 0;
+ ent->soundPos1 = ent->soundPos2 = 0;
+ } else {
+ ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/doors/dr1_strt.wav");
+ ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/doors/dr1_end.wav");
+ }
ent->blocked = Blocked_Door;
@@ -1574,3 +1590,20 @@ void SP_func_pendulum(gentity_t *ent) {
ent->s.apos.trType = TR_SINE;
ent->s.apos.trDelta[2] = speed;
}
+
+
+// BFP - Breakable map entities
+void func_breakable_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
+ if ( ent->health <= 0 ) {
+ G_FreeEntity( ent );
+ }
+}
+
+void SP_func_breakable( gentity_t *ent ) {
+ trap_SetBrushModel( ent, ent->model );
+ ent->s.eType = ET_MOVER;
+ ent->r.contents = CONTENTS_SOLID;
+ G_SpawnInt( "health", "0", &ent->health );
+ ent->takedamage = qtrue;
+ ent->use = func_breakable_use;
+}
diff --git a/source/game/g_rankings.c b/source/game/g_rankings.c
index ec3f846..f48de95 100644
--- a/source/game/g_rankings.c
+++ b/source/game/g_rankings.c
@@ -898,21 +898,36 @@ void G_RankPickupPowerup( int self, int powerup )
case PW_QUAD:
trap_RankReportInt( self, -1, QGR_KEY_QUAD, 1, 1 );
break;
+ // BFP - No battlesuit powerup
+#if 0
case PW_BATTLESUIT:
trap_RankReportInt( self, -1, QGR_KEY_SUIT, 1, 1 );
break;
+#endif
+ // BFP - No haste powerup
+#if 0
case PW_HASTE:
trap_RankReportInt( self, -1, QGR_KEY_HASTE, 1, 1 );
break;
+#endif
+ // BFP - No invis powerup
+#if 0
case PW_INVIS:
trap_RankReportInt( self, -1, QGR_KEY_INVIS, 1, 1 );
break;
+#endif
+ // BFP - No regen powerup
+#if 0
case PW_REGEN:
trap_RankReportInt( self, -1, QGR_KEY_REGEN, 1, 1 );
break;
+#endif
+ // BFP - No flight powerup
+#if 0
case PW_FLIGHT:
trap_RankReportInt( self, -1, QGR_KEY_FLIGHT, 1, 1 );
break;
+#endif
default:
break;
}
diff --git a/source/game/g_rankings.h b/source/game/g_rankings.h
index 3f22f32..ac119e6 100644
--- a/source/game/g_rankings.h
+++ b/source/game/g_rankings.h
@@ -229,45 +229,6 @@ Key digits:
#define QGR_KEY_PICKUP_UNKNOWN 1111021109
#define QGR_KEY_TIME_UNKNOWN 1111021110
-#ifdef MISSIONPACK
-// new to team arena
-#define QGR_KEY_FRAG_NAILGIN 1211021200
-#define QGR_KEY_SUICIDE_NAILGIN 1111021201
-#define QGR_KEY_SHOT_FIRED_NAILGIN 1111021202
-#define QGR_KEY_HIT_GIVEN_NAILGIN 1111021203
-#define QGR_KEY_HIT_TAKEN_NAILGIN 1111021204
-#define QGR_KEY_DAMAGE_GIVEN_NAILGIN 1111021205
-#define QGR_KEY_DAMAGE_TAKEN_NAILGIN 1111021206
-#define QGR_KEY_SPLASH_GIVEN_NAILGIN 1111021207
-#define QGR_KEY_SPLASH_TAKEN_NAILGIN 1111021208
-#define QGR_KEY_PICKUP_NAILGIN 1111021209
-#define QGR_KEY_TIME_NAILGIN 1111021210
-// new to team arena
-#define QGR_KEY_FRAG_PROX_LAUNCHER 1211021300
-#define QGR_KEY_SUICIDE_PROX_LAUNCHER 1111021301
-#define QGR_KEY_SHOT_FIRED_PROX_LAUNCHER 1111021302
-#define QGR_KEY_HIT_GIVEN_PROX_LAUNCHER 1111021303
-#define QGR_KEY_HIT_TAKEN_PROX_LAUNCHER 1111021304
-#define QGR_KEY_DAMAGE_GIVEN_PROX_LAUNCHER 1111021305
-#define QGR_KEY_DAMAGE_TAKEN_PROX_LAUNCHER 1111021306
-#define QGR_KEY_SPLASH_GIVEN_PROX_LAUNCHER 1111021307
-#define QGR_KEY_SPLASH_TAKEN_PROX_LAUNCHER 1111021308
-#define QGR_KEY_PICKUP_PROX_LAUNCHER 1111021309
-#define QGR_KEY_TIME_PROX_LAUNCHER 1111021310
-// new to team arena
-#define QGR_KEY_FRAG_CHAINGUN 1211021400
-#define QGR_KEY_SUICIDE_CHAINGUN 1111021401
-#define QGR_KEY_SHOT_FIRED_CHAINGUN 1111021402
-#define QGR_KEY_HIT_GIVEN_CHAINGUN 1111021403
-#define QGR_KEY_HIT_TAKEN_CHAINGUN 1111021404
-#define QGR_KEY_DAMAGE_GIVEN_CHAINGUN 1111021405
-#define QGR_KEY_DAMAGE_TAKEN_CHAINGUN 1111021406
-#define QGR_KEY_SPLASH_GIVEN_CHAINGUN 1111021407
-#define QGR_KEY_SPLASH_TAKEN_CHAINGUN 1111021408
-#define QGR_KEY_PICKUP_CHAINGUN 1111021409
-#define QGR_KEY_TIME_CHAINGUN 1111021410
-#endif /* MISSIONPACK */
-
// ammo keys
#define QGR_KEY_BOXES 1111030000
#define QGR_KEY_ROUNDS 1111030001
@@ -296,18 +257,6 @@ Key digits:
#define QGR_KEY_BOXES_BFG_AMMO 1111030800
#define QGR_KEY_ROUNDS_BFG_AMMO 1111030801
-#ifdef MISSIONPACK
-// new to team arena
-#define QGR_KEY_BOXES_NAILGUN_AMMO 1111030900
-#define QGR_KEY_ROUNDS_NAILGUN_AMMO 1111030901
-// new to team arena
-#define QGR_KEY_BOXES_PROX_LAUNCHER_AMMO 1111031000
-#define QGR_KEY_ROUNDS_PROX_LAUNCHER_AMMO 1111031001
-// new to team arena
-#define QGR_KEY_BOXES_CHAINGUN_AMMO 1111031100
-#define QGR_KEY_ROUNDS_CHAINGUN_AMMO 1111031101
-#endif /* MISSIONPACK */
-
// health keys
#define QGR_KEY_HEALTH 1111040000
#define QGR_KEY_HEALTH_TOTAL 1111040001
@@ -329,20 +278,13 @@ Key digits:
#define QGR_KEY_POWERUP 1111060000
#define QGR_KEY_QUAD 1111060100
#define QGR_KEY_SUIT 1111060200
-#define QGR_KEY_HASTE 1111060300
+// BFP - No haste powerup
+// #define QGR_KEY_HASTE 1111060300
#define QGR_KEY_INVIS 1111060400
-#define QGR_KEY_REGEN 1111060500
-#define QGR_KEY_FLIGHT 1111060600
-
-#ifdef MISSIONPACK
-// persistant powerup keys
-// new to team arena
-#define QGR_KEY_SCOUT 1111160800
-#define QGR_KEY_GUARD 1111160801
-#define QGR_KEY_DOUBLER 1111160802
-#define QGR_KEY_AMMOREGEN 1111160803
-
-#endif //MISSIONPACK
+// BFP - No regen powerup
+// #define QGR_KEY_REGEN 1111060500
+// BFP - No flight powerup
+// #define QGR_KEY_FLIGHT 1111060600
// holdable item keys
#define QGR_KEY_MEDKIT 1111070000
@@ -351,18 +293,6 @@ Key digits:
#define QGR_KEY_TELEPORTER 1111070100
#define QGR_KEY_TELEPORTER_USE 1111070101
-#ifdef MISSIONPACK
-// new to team arena
-#define QGR_KEY_KAMIKAZE 1111070200
-#define QGR_KEY_KAMIKAZE_USE 1111070201
-// new to team arena
-#define QGR_KEY_PORTAL 1111070300
-#define QGR_KEY_PORTAL_USE 1111070301
-// new to team arena
-#define QGR_KEY_INVULNERABILITY 1111070400
-#define QGR_KEY_INVULNERABILITY_USE 1111070401
-#endif /* MISSIONPACK */
-
// hazard keys
#define QGR_KEY_HAZARD_DEATH 1111080000
#define QGR_KEY_WATER 1111080100
diff --git a/source/game/g_session.c b/source/game/g_session.c
index c2c01d5..f76d0ed 100644
--- a/source/game/g_session.c
+++ b/source/game/g_session.c
@@ -61,16 +61,14 @@ void G_WriteClientSessionData( gclient_t *client ) {
/*
================
-G_ReadSessionData
+G_ReadClientSessionData
Called on a reconnect
================
*/
-void G_ReadSessionData( gclient_t *client ) {
+void G_ReadClientSessionData( gclient_t *client ) {
char s[MAX_STRING_CHARS];
const char *var;
-
- // bk001205 - format
int teamLeader;
int spectatorState;
int sessionTeam;
@@ -78,20 +76,23 @@ void G_ReadSessionData( gclient_t *client ) {
var = va( "session%i", client - level.clients );
trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
- sscanf( s, "%i %i %i %i %i %i %i",
- &sessionTeam, // bk010221 - format
+ Q_sscanf( s, "%i %i %i %i %i %i %i",
+ &sessionTeam,
&client->sess.spectatorTime,
- &spectatorState, // bk010221 - format
+ &spectatorState,
&client->sess.spectatorClient,
&client->sess.wins,
&client->sess.losses,
- &teamLeader // bk010221 - format
+ &teamLeader
);
- // bk001205 - format issues
client->sess.sessionTeam = (team_t)sessionTeam;
client->sess.spectatorState = (spectatorState_t)spectatorState;
client->sess.teamLeader = (qboolean)teamLeader;
+
+ if ( (unsigned)client->sess.sessionTeam >= TEAM_NUM_TEAMS ) {
+ client->sess.sessionTeam = TEAM_SPECTATOR;
+ }
}
@@ -102,24 +103,40 @@ G_InitSessionData
Called on a first-time connect
================
*/
-void G_InitSessionData( gclient_t *client, char *userinfo ) {
+void G_InitSessionData( gclient_t *client, const char *team, qboolean isBot ) {
clientSession_t *sess;
- const char *value;
sess = &client->sess;
// initial team determination
if ( g_gametype.integer >= GT_TEAM ) {
- if ( g_teamAutoJoin.integer ) {
- sess->sessionTeam = PickTeam( -1 );
- BroadcastTeamChange( client, -1 );
+ if ( team[0] == 's' || team[0] == 'S' ) {
+ // a willing spectator, not a waiting-in-line
+ sess->sessionTeam = TEAM_SPECTATOR;
} else {
- // always spawn as spectator in team games
- sess->sessionTeam = TEAM_SPECTATOR;
+ if ( g_teamAutoJoin.integer & 2 ) {
+ sess->sessionTeam = PickTeam( -1 );
+ } else {
+ // always spawn as spectator in team games
+ if ( isBot == qfalse ) {
+ sess->sessionTeam = TEAM_SPECTATOR;
+ } else {
+ // bind player to specified team
+ if ( team[0] == 'r' || team[0] == 'R' ) {
+ sess->sessionTeam = TEAM_RED;
+ } else if ( team[0] == 'b' || team[0] == 'B' ) {
+ sess->sessionTeam = TEAM_BLUE;
+ } else {
+ // or choose new
+ sess->sessionTeam = PickTeam( -1 );
+ }
+ }
+ }
}
+ // BFP - Team Last Man Standing, keep selected team
+ client->selectedTeam = sess->sessionTeam;
} else {
- value = Info_ValueForKey( userinfo, "team" );
- if ( value[0] == 's' ) {
+ if ( team[0] == 's' || team[0] == 'S' ) {
// a willing spectator, not a waiting-in-line
sess->sessionTeam = TEAM_SPECTATOR;
} else {
@@ -127,11 +144,14 @@ void G_InitSessionData( gclient_t *client, char *userinfo ) {
default:
case GT_FFA:
case GT_SINGLE_PLAYER:
- if ( g_maxGameClients.integer > 0 &&
- level.numNonSpectatorClients >= g_maxGameClients.integer ) {
+ if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) {
sess->sessionTeam = TEAM_SPECTATOR;
} else {
- sess->sessionTeam = TEAM_FREE;
+ // BFP - As for ec-/baseq3a, this makes the players moving to spectators like a real game, but that's a nice improvement
+ //if ( g_teamAutoJoin.integer & 1 || isBot || g_gametype.integer == GT_SINGLE_PLAYER )
+ sess->sessionTeam = TEAM_FREE;
+ // else
+ // sess->sessionTeam = TEAM_SPECTATOR;
}
break;
case GT_TOURNAMENT:
@@ -139,17 +159,23 @@ void G_InitSessionData( gclient_t *client, char *userinfo ) {
if ( level.numNonSpectatorClients >= 2 ) {
sess->sessionTeam = TEAM_SPECTATOR;
} else {
- sess->sessionTeam = TEAM_FREE;
+ // BFP - As for ec-/baseq3a, this makes the players moving to spectators like a real game, but that's a nice improvement
+ // if ( g_teamAutoJoin.integer & 1 || isBot )
+ sess->sessionTeam = TEAM_FREE;
+ // else
+ // sess->sessionTeam = TEAM_SPECTATOR;
}
break;
+ case GT_SURVIVAL: // BFP - Survival
+ // always spawn as spectator
+ sess->sessionTeam = TEAM_SPECTATOR;
+ break;
}
}
}
sess->spectatorState = SPECTATOR_FREE;
- sess->spectatorTime = level.time;
-
- G_WriteClientSessionData( client );
+ sess->spectatorTime = 0;
}
diff --git a/source/game/g_spawn.c b/source/game/g_spawn.c
index dfd193c..0df228e 100644
--- a/source/game/g_spawn.c
+++ b/source/game/g_spawn.c
@@ -65,7 +65,7 @@ qboolean G_SpawnVector( const char *key, const char *defaultString, float *out )
qboolean present;
present = G_SpawnString( key, defaultString, &s );
- sscanf( s, "%f %f %f", &out[0], &out[1], &out[2] );
+ Q_sscanf( s, "%f %f %f", &out[0], &out[1], &out[2] );
return present;
}
@@ -143,6 +143,7 @@ void SP_func_button (gentity_t *ent);
void SP_func_door (gentity_t *ent);
void SP_func_train (gentity_t *ent);
void SP_func_timer (gentity_t *self);
+void SP_func_breakable (gentity_t *ent); // BFP - Breakable map entities
void SP_trigger_always (gentity_t *ent);
void SP_trigger_multiple (gentity_t *ent);
@@ -186,7 +187,7 @@ void SP_team_CTF_blueplayer( gentity_t *ent );
void SP_team_CTF_redspawn( gentity_t *ent );
void SP_team_CTF_bluespawn( gentity_t *ent );
-void SP_item_botroam( gentity_t *ent ) {};
+void SP_item_botroam( gentity_t *ent ) { (void)ent; }
spawn_t spawns[] = {
// info entities don't do anything at all, but provide positional
@@ -207,6 +208,7 @@ spawn_t spawns[] = {
{"func_pendulum", SP_func_pendulum},
{"func_train", SP_func_train},
{"func_group", SP_info_null},
+ {"func_breakable", SP_func_breakable}, // BFP - Breakable map entities
{"func_timer", SP_func_timer}, // rename trigger_timer?
// Triggers are brush objects that cause an effect when contacted
@@ -270,6 +272,8 @@ returning qfalse if not found
qboolean G_CallSpawn( gentity_t *ent ) {
spawn_t *s;
gitem_t *item;
+ // BFP - To avoid showing these kind of spawn function warning messages
+ qboolean cannotSpawn = qfalse;
if ( !ent->classname ) {
G_Printf ("G_CallSpawn: NULL classname\n");
@@ -278,6 +282,11 @@ qboolean G_CallSpawn( gentity_t *ent ) {
// check item spawn functions
for ( item=bg_itemlist+1 ; item->classname ; item++ ) {
+ // BFP - Don't spawn ammo, health and weapon items
+ if ( item->giType == IT_AMMO || item->giType == IT_HEALTH || item->giType == IT_WEAPON ) {
+ cannotSpawn = qtrue;
+ continue;
+ }
if ( !strcmp(item->classname, ent->classname) ) {
G_SpawnItem( ent, item );
return qtrue;
@@ -292,6 +301,10 @@ qboolean G_CallSpawn( gentity_t *ent ) {
return qtrue;
}
}
+ // BFP - To avoid showing these kind of spawn function warning messages
+ if ( cannotSpawn ) {
+ return qfalse;
+ }
G_Printf ("%s doesn't have a spawn function\n", ent->classname);
return qfalse;
}
@@ -358,7 +371,7 @@ void G_ParseField( const char *key, const char *value, gentity_t *ent ) {
*(char **)(b+f->ofs) = G_NewString (value);
break;
case F_VECTOR:
- sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]);
+ Q_sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]);
((float *)(b+f->ofs))[0] = vec[0];
((float *)(b+f->ofs))[1] = vec[1];
((float *)(b+f->ofs))[2] = vec[2];
@@ -399,7 +412,7 @@ void G_SpawnGEntityFromSpawnVars( void ) {
int i;
gentity_t *ent;
char *s, *value, *gametypeName;
- static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"};
+ static char *gametypeNames[] = {"ffa", "tournament", "single", "survival", "monster", "team", "tlms", "ctf"};
// get the next free entity
ent = G_Spawn();
diff --git a/source/game/g_syscalls.asm b/source/game/g_syscalls.asm
index 19de97d..ad2ead9 100644
--- a/source/game/g_syscalls.asm
+++ b/source/game/g_syscalls.asm
@@ -181,7 +181,7 @@ equ trap_BotAvoidGoalTime -541
equ trap_BotInitLevelItems -542
equ trap_BotUpdateEntityItems -543
equ trap_BotLoadItemWeights -544
-equ trap_BotFreeItemWeights -546
+equ trap_BotFreeItemWeights -545
equ trap_BotSaveGoalFuzzyLogic -546
equ trap_BotAllocGoalState -547
equ trap_BotFreeGoalState -548
diff --git a/source/game/g_syscalls.c b/source/game/g_syscalls.c
index 3ef3f5c..2ddca85 100644
--- a/source/game/g_syscalls.c
+++ b/source/game/g_syscalls.c
@@ -31,7 +31,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1;
-void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) {
+DLLEXPORT void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) {
syscall = syscallptr;
}
@@ -80,7 +80,7 @@ int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf
return syscall( G_FS_GETFILELIST, path, extension, listbuf, bufsize );
}
-int trap_FS_Seek( fileHandle_t f, long offset, int origin ) {
+int trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin ) {
return syscall( G_FS_SEEK, f, offset, origin );
}
diff --git a/source/game/g_target.c b/source/game/g_target.c
index 062d058..e1d07f6 100644
--- a/source/game/g_target.c
+++ b/source/game/g_target.c
@@ -73,8 +73,6 @@ void Use_target_remove_powerups( gentity_t *ent, gentity_t *other, gentity_t *ac
Team_ReturnFlag( TEAM_RED );
} else if( activator->client->ps.powerups[PW_BLUEFLAG] ) {
Team_ReturnFlag( TEAM_BLUE );
- } else if( activator->client->ps.powerups[PW_NEUTRALFLAG] ) {
- Team_ReturnFlag( TEAM_FREE );
}
memset( activator->client->ps.powerups, 0, sizeof( activator->client->ps.powerups ) );
@@ -122,6 +120,8 @@ void SP_target_delay( gentity_t *ent ) {
The activator is given this many points.
*/
void Use_Target_Score (gentity_t *ent, gentity_t *other, gentity_t *activator) {
+ if ( !activator )
+ return;
AddScore( activator, ent->r.currentOrigin, ent->count );
}
@@ -140,7 +140,7 @@ void SP_target_score( gentity_t *ent ) {
If "private", only the activator gets the message. If no checks, all clients get the message.
*/
void Use_Target_Print (gentity_t *ent, gentity_t *other, gentity_t *activator) {
- if ( activator->client && ( ent->spawnflags & 4 ) ) {
+ if ( activator && activator->client && ( ent->spawnflags & 4 ) ) {
trap_SendServerCommand( activator-g_entities, va("cp \"%s\"", ent->message ));
return;
}
@@ -155,7 +155,7 @@ void Use_Target_Print (gentity_t *ent, gentity_t *other, gentity_t *activator) {
return;
}
- trap_SendServerCommand( -1, va("cp \"%s\"", ent->message ));
+ G_BroadcastServerCommand( -1, va("cp \"%s\"", ent->message ));
}
void SP_target_print( gentity_t *ent ) {
@@ -185,7 +185,7 @@ void Use_Target_Speaker (gentity_t *ent, gentity_t *other, gentity_t *activator)
else
ent->s.loopSound = ent->noise_index; // start it
}else { // normal sound
- if ( ent->spawnflags & 8 ) {
+ if ( ent->spawnflags & 8 && activator ) {
G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index );
} else if (ent->spawnflags & 4) {
G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index );
@@ -206,7 +206,7 @@ void SP_target_speaker( gentity_t *ent ) {
G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
}
- // force all client reletive sounds to be "activator" speakers that
+ // force all client relative sounds to be "activator" speakers that
// play on the entity that activates it
if ( s[0] == '*' ) {
ent->spawnflags |= 8;
@@ -227,9 +227,10 @@ void SP_target_speaker( gentity_t *ent ) {
// check for prestarted looping sound
- if ( ent->spawnflags & 1 ) {
+ if ( ent->spawnflags & 1 )
ent->s.loopSound = ent->noise_index;
- }
+ else
+ ent->s.loopSound = 0;
ent->use = Use_Target_Speaker;
@@ -345,7 +346,7 @@ void SP_target_laser (gentity_t *self)
void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
gentity_t *dest;
- if (!activator->client)
+ if ( !activator || !activator->client )
return;
dest = G_PickTarget( self->target );
if (!dest) {
@@ -375,11 +376,12 @@ The activator can be forced to be from a certain team.
if RANDOM is checked, only one of the targets will be fired, not all of them
*/
void target_relay_use (gentity_t *self, gentity_t *other, gentity_t *activator) {
- if ( ( self->spawnflags & 1 ) && activator->client
+
+ if ( ( self->spawnflags & 1 ) && activator && activator->client
&& activator->client->sess.sessionTeam != TEAM_RED ) {
return;
}
- if ( ( self->spawnflags & 2 ) && activator->client
+ if ( ( self->spawnflags & 2 ) && activator && activator->client
&& activator->client->sess.sessionTeam != TEAM_BLUE ) {
return;
}
@@ -406,6 +408,8 @@ void SP_target_relay (gentity_t *self) {
Kills the activator.
*/
void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
+ if ( !activator )
+ return;
G_Damage ( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
}
diff --git a/source/game/g_team.c b/source/game/g_team.c
index 4251500..caf503e 100644
--- a/source/game/g_team.c
+++ b/source/game/g_team.c
@@ -32,14 +32,10 @@ typedef struct teamgame_s {
flagStatus_t flagStatus; // One Flag CTF
int redTakenTime;
int blueTakenTime;
- int redObeliskAttackedTime;
- int blueObeliskAttackedTime;
} teamgame_t;
teamgame_t teamgame;
-gentity_t *neutralObelisk;
-
static void Team_SetFlagStatus( team_t team, flagStatus_t status );
void Team_InitGame( void ) {
@@ -102,7 +98,7 @@ void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) {
char *p;
va_start (argptr,fmt);
- if ( vsprintf( msg, fmt, argptr ) >= sizeof( msg ) ) {
+ if ( Q_vsprintf( msg, fmt, argptr ) >= sizeof( msg ) ) {
G_Error ( "PrintMsg overrun" );
}
va_end (argptr);
@@ -182,7 +178,6 @@ qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) {
static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2' };
-static char oneFlagStatusRemap[] = { '0', '1', '2', '3', '4' };
static void Team_SetFlagStatus( team_t team, flagStatus_t status ) {
qboolean modified = qfalse;
@@ -202,13 +197,6 @@ static void Team_SetFlagStatus( team_t team, flagStatus_t status ) {
}
break;
- case TEAM_FREE: // One Flag CTF
- if ( teamgame.flagStatus != status ) {
- teamgame.flagStatus = status;
- modified = qtrue;
- }
- break;
-
default:
return;
}
@@ -220,9 +208,6 @@ static void Team_SetFlagStatus( team_t team, flagStatus_t status ) {
st[0] = ctfFlagStatusRemap[teamgame.redStatus];
st[1] = ctfFlagStatusRemap[teamgame.blueStatus];
st[2] = '\0';
- } else { // GT_1FCTF
- st[0] = oneFlagStatusRemap[teamgame.flagStatus];
- st[1] = '\0';
}
trap_SetConfigstring( CS_FLAGSTATUS, st );
@@ -236,11 +221,10 @@ void Team_CheckDroppedItem( gentity_t *dropped ) {
else if( dropped->item->giTag == PW_BLUEFLAG ) {
Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED );
}
- else if( dropped->item->giTag == PW_NEUTRALFLAG ) {
- Team_SetFlagStatus( TEAM_FREE, FLAG_DROPPED );
- }
}
+// BFP - No force gesture anim
+#if 0
/*
================
Team_ForceGesture
@@ -262,6 +246,7 @@ static void Team_ForceGesture( team_t team ) {
ent->flags |= FL_FORCE_GESTURE;
}
}
+#endif
/*
================
@@ -349,11 +334,13 @@ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker
attacker->client->pers.teamState.carrierdefense++;
targ->client->pers.teamState.lasthurtcarrier = 0;
- attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
+ // BFP - No defend counter
+ // attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
team = attacker->client->sess.sessionTeam;
// add the sprite over the player's head
- attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ attacker->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
@@ -367,11 +354,13 @@ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker
attacker->client->pers.teamState.carrierdefense++;
targ->client->pers.teamState.lasthurtcarrier = 0;
- attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
+ // BFP - No defend counter
+ // attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
team = attacker->client->sess.sessionTeam;
// add the sprite over the player's head
- attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ attacker->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
@@ -424,10 +413,12 @@ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker
AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);
attacker->client->pers.teamState.basedefense++;
- attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
+ // BFP - No defend counter
+ // attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
// add the sprite over the player's head
- attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ attacker->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
@@ -445,10 +436,12 @@ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker
AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS);
attacker->client->pers.teamState.carrierdefense++;
- attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
+ // BFP - No defend counter
+ // attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
// add the sprite over the player's head
- attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ attacker->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
@@ -480,11 +473,6 @@ void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker)
if (targ->client->ps.powerups[flag_pw] &&
targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
attacker->client->pers.teamState.lasthurtcarrier = level.time;
-
- // skulls
- if (targ->client->ps.generic1 &&
- targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
- attacker->client->pers.teamState.lasthurtcarrier = level.time;
}
@@ -625,9 +613,6 @@ void Team_FreeEntity( gentity_t *ent ) {
else if( ent->item->giTag == PW_BLUEFLAG ) {
Team_ReturnFlag( TEAM_BLUE );
}
- else if( ent->item->giTag == PW_NEUTRALFLAG ) {
- Team_ReturnFlag( TEAM_FREE );
- }
}
@@ -647,9 +632,6 @@ void Team_DroppedFlagThink(gentity_t *ent) {
else if( ent->item->giTag == PW_BLUEFLAG ) {
team = TEAM_BLUE;
}
- else if( ent->item->giTag == PW_NEUTRALFLAG ) {
- team = TEAM_FREE;
- }
Team_ReturnFlagSound( Team_ResetFlag( team ), team );
// Reset Flag will delete this entity
@@ -698,14 +680,17 @@ static int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, team_t team ) {
// Increase the team's score
AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1);
- Team_ForceGesture(other->client->sess.sessionTeam);
+ // BFP - No force gesture anim
+ // Team_ForceGesture(other->client->sess.sessionTeam);
other->client->pers.teamState.captures++;
// add the sprite over the player's head
- other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- other->client->ps.eFlags |= EF_AWARD_CAP;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ other->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // other->client->ps.eFlags |= EF_AWARD_CAP;
other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
- other->client->ps.persistant[PERS_CAPTURES]++;
+ // BFP - No capture counter
+ // other->client->ps.persistant[PERS_CAPTURES]++;
// other gets another 10 frag bonus
AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS);
@@ -728,10 +713,12 @@ static int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, team_t team ) {
AddScore (player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS);
other->client->pers.teamState.assists++;
- player->client->ps.persistant[PERS_ASSIST_COUNT]++;
+ // BFP - No assist counter
+ // player->client->ps.persistant[PERS_ASSIST_COUNT]++;
// add the sprite over the player's head
- player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- player->client->ps.eFlags |= EF_AWARD_ASSIST;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ player->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // player->client->ps.eFlags |= EF_AWARD_ASSIST;
player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
}
@@ -739,10 +726,13 @@ static int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, team_t team ) {
CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS);
other->client->pers.teamState.assists++;
- player->client->ps.persistant[PERS_ASSIST_COUNT]++;
+ // BFP - No assist counter
+ // player->client->ps.persistant[PERS_ASSIST_COUNT]++;
+
// add the sprite over the player's head
- player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- player->client->ps.eFlags |= EF_AWARD_ASSIST;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ player->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // player->client->ps.eFlags |= EF_AWARD_ASSIST;
player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
}
}
@@ -945,7 +935,7 @@ gentity_t *SelectCTFSpawnPoint( gentity_t *ent, team_t team, int teamstate, vec3
VectorCopy( spot->s.origin, origin );
VectorCopy( spot->s.angles, angles );
- origin[2] += 9.0f;
+ origin[2] += SPAWN_HEIGHT;
return spot;
}
diff --git a/source/game/g_trigger.c b/source/game/g_trigger.c
index 9db2304..f3dc4b9 100644
--- a/source/game/g_trigger.c
+++ b/source/game/g_trigger.c
@@ -174,7 +174,7 @@ void AimAtTarget( gentity_t *self ) {
height = ent->s.origin[2] - origin[2];
gravity = g_gravity.value;
- time = sqrt( height / ( .5 * gravity ) );
+ time = sqrt( height / ( 0.5 * gravity ) );
if ( !time ) {
G_FreeEntity( self );
return;
@@ -221,9 +221,13 @@ void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator )
if ( activator->client->ps.pm_type != PM_NORMAL ) {
return;
}
+
+// BFP - No flight powerup
+#if 0
if ( activator->client->ps.powerups[PW_FLIGHT] ) {
return;
}
+#endif
VectorCopy (self->s.origin2, activator->client->ps.velocity);
diff --git a/source/game/g_utils.c b/source/game/g_utils.c
index 8c6f35b..6924fc5 100644
--- a/source/game/g_utils.c
+++ b/source/game/g_utils.c
@@ -54,12 +54,12 @@ void AddRemap(const char *oldShader, const char *newShader, float timeOffset) {
}
}
-const char *BuildShaderStateConfig() {
+const char *BuildShaderStateConfig(void) {
static char buff[MAX_STRING_CHARS*4];
char out[(MAX_QPATH * 2) + 5];
int i;
- memset(buff, 0, MAX_STRING_CHARS);
+ memset( buff, 0, sizeof( buff ) );
for (i = 0; i < remapCount; i++) {
Com_sprintf(out, (MAX_QPATH * 2) + 5, "%s=%s:%5.2f@", remappedShaders[i].oldShader, remappedShaders[i].newShader, remappedShaders[i].timeOffset);
Q_strcat( buff, sizeof( buff ), out);
@@ -81,7 +81,7 @@ G_FindConfigstringIndex
================
*/
-int G_FindConfigstringIndex( char *name, int start, int max, qboolean create ) {
+int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create ) {
int i;
char s[MAX_STRING_CHARS];
@@ -113,14 +113,197 @@ int G_FindConfigstringIndex( char *name, int start, int max, qboolean create ) {
}
-int G_ModelIndex( char *name ) {
+int G_ModelIndex( const char *name ) {
return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
}
-int G_SoundIndex( char *name ) {
+int G_SoundIndex( const char *name ) {
return G_FindConfigstringIndex (name, CS_SOUNDS, MAX_SOUNDS, qtrue);
}
+// BFP - Struct for handling the list of models in the server in order to avoid using a model which isn't in the list
+typedef struct {
+ char modelNames[MAX_MODELS][MAX_QPATH];
+ int numModels;
+} modelList_t;
+
+static modelList_t serverModels;
+
+/*
+================
+G_PlayerModelNameCompare
+
+Comparison function for qsort (alphabetical order)
+================
+*/
+static int QDECL G_PlayerModelNameCompare( const void *a, const void *b ) { // BFP - Compare to sort alphabetically
+ return Q_stricmp( (const char *)a, (const char *)b );
+}
+
+/*
+================
+G_GetPlayerModelName
+
+Get player model name from a client
+================
+*/
+const char *G_GetPlayerModelName( int clientNum, const char userinfo[MAX_INFO_STRING] ) { // BFP - Get player model name from a client
+ static char model[MAX_QPATH];
+ // skin name to truncate "/default", "/red", "/blue"... words
+ char skinName[MAX_QPATH];
+ char *slash;
+ int len, i;
+
+ // set model
+ if ( g_gametype.integer >= GT_TEAM ) {
+ Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) );
+ } else {
+ Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) );
+ }
+
+ // if only executes the prefix, e.g. "bfp1-", loads the first player model in the list for this prefix
+ len = strlen( model );
+ if ( len > 0 && model[len - 1] == '-' ) {
+ for ( i = 0; i < serverModels.numModels; ++i ) {
+ if ( !Q_strncmp( serverModels.modelNames[i], model, len ) ) {
+ Q_strncpyz( model, serverModels.modelNames[i], sizeof( model ) );
+ break;
+ }
+ }
+ }
+
+ // truncate the "/default", "/red", "/blue"... words
+ slash = strchr( model, '/' );
+ if ( slash ) {
+ Q_strncpyz( skinName, slash + 1, sizeof( skinName ) );
+ *slash = 0;
+ }
+
+ return model;
+}
+
+/*
+================
+G_InitPlayerModelList
+
+Initialize player model list at server startup
+================
+*/
+void G_InitPlayerModelList( void ) { // BFP - For Illegal player model handling
+ char fileList[4096];
+ char tempName[MAX_QPATH];
+ char *filePtr;
+ int numFiles, i, len;
+
+ serverModels.numModels = 0;
+
+ // scan models/players directory for available models
+ numFiles = trap_FS_GetFileList( "models/players", "/", fileList, sizeof(fileList) );
+
+ filePtr = fileList;
+ for ( i = 0; i < numFiles && serverModels.numModels < MAX_MODELS; ++i ) {
+ len = strlen( filePtr );
+
+ if ( len > 0 && filePtr[len - 1] != '.' ) { // skip hidden files
+ // remove trailing slash if present
+ if ( filePtr[len - 1] == '/' ) {
+ Q_strncpyz( tempName, filePtr, len );
+ } else {
+ Q_strncpyz( tempName, filePtr, sizeof(tempName) );
+ }
+
+ // filter only by prefix
+ if ( strchr( tempName, '-' ) ) {
+ Q_strncpyz( serverModels.modelNames[serverModels.numModels], tempName, MAX_QPATH );
+ ++serverModels.numModels;
+ }
+ }
+ filePtr += len + 1;
+ }
+
+ // sort them alphabetically
+ if ( serverModels.numModels > 1 ) {
+ qsort( serverModels.modelNames, serverModels.numModels,
+ sizeof( serverModels.modelNames[0] ), G_PlayerModelNameCompare );
+ }
+
+ // BFP - To debug the loaded player models
+#if 0
+ for ( i = 0; i < serverModels.numModels; ++i ) {
+ Com_Printf( "^5%d - player model: ^3%s\n", i, serverModels.modelNames[i] );
+ }
+ Com_Printf( "^2LOADED PLAYER MODELS: ^5%d\n", serverModels.numModels );
+#endif
+}
+
+/*
+================
+G_PlayerModelExistsOnServer
+
+Check if model exists on server
+================
+*/
+qboolean G_PlayerModelExistsOnServer( const char *modelName ) { // BFP - Illegal player model handling
+ int i;
+
+ if ( !Q_stricmp( modelName, MONSTER_NAME ) ) {
+ return qfalse;
+ }
+
+ for ( i = 0; i < serverModels.numModels; ++i ) {
+ if ( !Q_stricmp( modelName, serverModels.modelNames[i] ) ) {
+ return qtrue;
+ }
+ }
+
+ return qfalse;
+}
+
+/*
+==================
+G_ResolvePlayerModel
+
+Resolves a model string (e.g. "bfp3-") to
+the actual model name (e.g. "bfp3-ryuujin"),
+keeps the skin part if it exists
+==================
+*/
+void G_ResolvePlayerModel( const char userinfo[MAX_INFO_STRING], const char *input, char *output, int outSize ) { // BFP - Resolve player model when loading by prefix
+ char model[MAX_QPATH];
+ char resolved[MAX_QPATH];
+ int len, i;
+
+ Q_strncpyz( output, input, outSize );
+ Q_strncpyz( model, output, sizeof( model ) );
+
+ if ( g_gametype.integer >= GT_TEAM ) {
+ Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) );
+ } else {
+ Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) );
+ }
+
+ // if only executes the prefix, e.g. "bfp1-", loads the first player model in the list for this prefix
+ len = strlen( model );
+ if ( len > 0 && model[len - 1] == '-' ) {
+ resolved[0] = '\0';
+ for ( i = 0; i < serverModels.numModels; i++ ) {
+ if ( !Q_strncmp( serverModels.modelNames[i], model, len ) ) {
+ Q_strncpyz( resolved, serverModels.modelNames[i], sizeof(resolved) );
+ break;
+ }
+ }
+ // if found, use it; else keep the original one, it might not exist
+ if ( resolved[0] ) {
+ Q_strncpyz( output, resolved, outSize );
+ } else {
+ Q_strncpyz( output, model, outSize ); // fallback
+ }
+ } else {
+ // not a prefix, just use model
+ Q_strncpyz( output, model, outSize );
+ }
+}
+
//=====================================================================
@@ -131,13 +314,13 @@ G_TeamCommand
Broadcasts a command to only a specific team
================
*/
-void G_TeamCommand( team_t team, char *cmd ) {
+void G_TeamCommand( team_t team, const char *cmd ) {
int i;
for ( i = 0 ; i < level.maxclients ; i++ ) {
if ( level.clients[i].pers.connected == CON_CONNECTED ) {
if ( level.clients[i].sess.sessionTeam == team ) {
- trap_SendServerCommand( i, va("%s", cmd ));
+ trap_SendServerCommand( i, cmd );
}
}
}
@@ -158,6 +341,7 @@ NULL will be returned if the end of the list is reached.
*/
gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
{
+ const gentity_t *to;
char *s;
if (!from)
@@ -165,7 +349,9 @@ gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
else
from++;
- for ( ; from < &g_entities[level.num_entities] ; from++)
+ to = &g_entities[ level.num_entities ];
+
+ for ( ; from < to ; from++ )
{
if (!from->inuse)
continue;
@@ -180,6 +366,69 @@ gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
}
+/*
+================
+FindRadius
+================
+*/
+gentity_t *FindRadius ( gentity_t *from, vec3_t org, float rad ) { // BFP - FindRadius function
+ vec3_t eorg;
+ int j;
+
+ if ( !from )
+ from = g_entities;
+ else
+ from++;
+
+ for ( ; from < &g_entities[level.num_entities]; from++ ) {
+ if ( !from->inuse )
+ continue;
+
+ for ( j = 0; j < 3; j++ )
+ eorg[j] = org[j] - ( from->r.currentOrigin[j] + ( from->r.mins[j] + from->r.maxs[j] ) * 0.5 );
+
+ if ( VectorLength( eorg ) > rad )
+ continue;
+
+ return from;
+ }
+ return NULL;
+}
+
+
+/*
+================
+IsValidTargetRadius
+
+Validates target within the radius
+================
+*/
+qboolean IsValidTargetRadius( gentity_t *ent, gentity_t *rad ) { // BFP - Helper function that validates target within the radius
+ if ( rad == ent || rad->r.ownerNum == ent->r.ownerNum ) {
+ return qfalse;
+ }
+ if ( rad == ent->parent ) {
+ return qfalse;
+ }
+ if ( !rad->client || !rad->takedamage ) {
+ return qfalse;
+ }
+ if ( rad->health <= 0 || rad->client->ps.pm_type == PM_DEAD ) {
+ return qfalse;
+ }
+ if ( rad->client->pers.connected == CON_DISCONNECTED ) {
+ return qfalse;
+ }
+ if ( rad->client->sess.sessionTeam == TEAM_SPECTATOR ) {
+ return qfalse;
+ }
+ if ( OnSameTeam( rad, ent->parent ) ) {
+ return qfalse;
+ }
+ return qtrue;
+}
+
+
/*
=============
G_PickTarget
@@ -189,11 +438,11 @@ Selects a random entity from among the targets
*/
#define MAXCHOICES 32
-gentity_t *G_PickTarget (char *targetname)
+gentity_t *G_PickTarget( const char *targetname )
{
- gentity_t *ent = NULL;
- int num_choices = 0;
gentity_t *choice[MAXCHOICES];
+ gentity_t *ent;
+ int num_choices;
if (!targetname)
{
@@ -201,13 +450,15 @@ gentity_t *G_PickTarget (char *targetname)
return NULL;
}
+ ent = NULL;
+ num_choices = 0;
while(1)
{
ent = G_Find (ent, FOFS(targetname), targetname);
if (!ent)
break;
choice[num_choices++] = ent;
- if (num_choices == MAXCHOICES)
+ if ( num_choices >= MAXCHOICES )
break;
}
@@ -234,13 +485,14 @@ match (string)self.target and call their .use function
*/
void G_UseTargets( gentity_t *ent, gentity_t *activator ) {
gentity_t *t;
+ float f;
if ( !ent ) {
return;
}
if (ent->targetShaderName && ent->targetShaderNewName) {
- float f = level.time * 0.001;
+ f = level.time * 0.001;
AddRemap(ent->targetShaderName, ent->targetShaderNewName, f);
trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
}
@@ -371,6 +623,7 @@ void G_InitGentity( gentity_t *e ) {
e->r.ownerNum = ENTITYNUM_NONE;
}
+
/*
=================
G_Spawn
@@ -387,23 +640,23 @@ angles and bad trails.
=================
*/
gentity_t *G_Spawn( void ) {
- int i, force;
+ int i, timeout;
gentity_t *e;
- e = NULL; // shut up warning
- i = 0; // shut up warning
- for ( force = 0 ; force < 2 ; force++ ) {
+ e = NULL; // shut up warning
+ // try to release oldest items first
+ for ( timeout = 1000 ; timeout >= 0 ; timeout -= 250 ) {
// if we go through all entities and can't find one to free,
// override the normal minimum times before use
- e = &g_entities[MAX_CLIENTS];
- for ( i = MAX_CLIENTS ; iinuse ) {
continue;
}
// the first couple seconds of server time can involve a lot of
// freeing and allocating, so relax the replacement policy
- if ( !force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000 ) {
+ if ( e->freetime > level.startTime + 2000 && level.time - e->freetime < timeout ) {
continue;
}
@@ -411,10 +664,12 @@ gentity_t *G_Spawn( void ) {
G_InitGentity( e );
return e;
}
- if ( i != MAX_GENTITIES ) {
+
+ if ( level.num_entities < ENTITYNUM_MAX_NORMAL ) {
break;
}
}
+
if ( i == ENTITYNUM_MAX_NORMAL ) {
for (i = 0; i < MAX_GENTITIES; i++) {
G_Printf("%4i: %s\n", i, g_entities[i].classname);
@@ -433,6 +688,7 @@ gentity_t *G_Spawn( void ) {
return e;
}
+
/*
=================
G_EntitiesFree
@@ -474,6 +730,7 @@ void G_FreeEntity( gentity_t *ed ) {
ed->inuse = qfalse;
}
+
/*
=================
G_TempEntity
@@ -514,6 +771,38 @@ Kill box
==============================================================================
*/
+/*
+=================
+G_EntitiesInBox
+=================
+*/
+int G_EntitiesInBox( vec3_t mins, vec3_t maxs, int *touch, int maxents ) {
+ const gentity_t *ent;
+ int i, count;
+
+ count = 0;
+ ent = g_entities;
+
+ for ( i = 0; i < maxents; i++, ent++ ) {
+ if ( !ent->r.linked || !ent->inuse ) {
+ continue;
+ }
+ if ( ent->r.absmin[0] > maxs[0] ||
+ ent->r.absmin[1] > maxs[1] ||
+ ent->r.absmin[2] > maxs[2] ||
+ ent->r.absmax[0] < mins[0] ||
+ ent->r.absmax[1] < mins[1] ||
+ ent->r.absmax[2] < mins[2] ) {
+ continue;
+ }
+ touch[ count ] = i;
+ count++;
+ }
+
+ return count;
+}
+
+
/*
=================
G_KillBox
@@ -522,17 +811,17 @@ Kills all entities that would touch the proposed new positioning
of ent. Ent should be unlinked before calling this!
=================
*/
-void G_KillBox (gentity_t *ent) {
+void G_KillBox( gentity_t *ent ) {
int i, num;
- int touch[MAX_GENTITIES];
+ int touch[MAX_CLIENTS];
gentity_t *hit;
vec3_t mins, maxs;
VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
- num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
+ num = G_EntitiesInBox( mins, maxs, touch, level.maxclients );
- for (i=0 ; iclient ) {
continue;
@@ -542,7 +831,6 @@ void G_KillBox (gentity_t *ent) {
G_Damage ( hit, ent, ent, NULL, NULL,
100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
}
-
}
//==============================================================================
@@ -556,11 +844,11 @@ client side: jumppads and item pickups
Adds an event+parm and twiddles the event counter
===============
*/
-void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm ) {
+void G_AddPredictableEvent( gentity_t *ent, entity_event_t event, int eventParm ) {
if ( !ent->client ) {
return;
}
- BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps );
+ BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps, -1 );
}
diff --git a/source/game/g_weapon.c b/source/game/g_weapon.c
index 5dc371e..05cdb5f 100644
--- a/source/game/g_weapon.c
+++ b/source/game/g_weapon.c
@@ -29,8 +29,6 @@ static float s_quadFactor;
static vec3_t forward, right, up;
static vec3_t muzzle;
-#define NUM_NAILSHOTS 15
-
/*
================
G_BounceProjectile
@@ -57,36 +55,64 @@ GAUNTLET
======================================================================
*/
-void Weapon_Gauntlet( gentity_t *ent ) {
+// BFP - TODO: That's just a test for gauntlet.
+// It might modify if weapon config is going to be implemented
+// check for the hit-scan gauntlet, don't let the action
+// go through as an attack unless it actually hits something
+void Weapon_Gauntlet( gentity_t *ent ) {
+ CheckKiShockwavePushAttack( ent );
}
+// BFP - TODO: There's a ki attack that acts like a shockwave to push opponents,
+// gauntlet attack can be modified adding punch attack distance and pushing opponents
+
/*
===============
-CheckGauntletAttack
+CheckKiShockwavePushAttack
===============
*/
-qboolean CheckGauntletAttack( gentity_t *ent ) {
+qboolean CheckKiShockwavePushAttack( gentity_t *ent ) {
trace_t tr;
vec3_t end;
- gentity_t *tent;
+ // gentity_t *tent;
gentity_t *traceEnt;
- int damage;
+ // BFP - bfp_weapon.cfg: damage, range ...
+ int damage = 8;
+ int range = 500;
+
+ // BFP - TODO: For weapon config, set this as hitscan attack type conditional
+ ent->classname = "hitscan";
+ ent->attackType = ATK_HITSCAN; // BFP - Attack type
+ if ( ent->attackType != ATK_HITSCAN ) {
+ return qfalse;
+ }
// set aiming directions
AngleVectors (ent->client->ps.viewangles, forward, right, up);
CalcMuzzlePoint ( ent, forward, right, up, muzzle );
- VectorMA (muzzle, 32, forward, end);
+ VectorMA (muzzle, range, forward, end);
+
+ // BFP - Reflective
+ ent->reflective = qtrue;
+
+ // BFP - Extra knockback
+ ent->extraKnockback = 200;
trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
if ( tr.surfaceFlags & SURF_NOIMPACT ) {
return qfalse;
}
+ // BFP - Reflective
+ G_Reflective( ent, qtrue, muzzle );
+
traceEnt = &g_entities[ tr.entityNum ];
+ // BFP - No EV_MISSILE_HIT here
+#if 0
// send blood impact
if ( traceEnt->takedamage && traceEnt->client ) {
tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
@@ -94,11 +120,14 @@ qboolean CheckGauntletAttack( gentity_t *ent ) {
tent->s.eventParm = DirToByte( tr.plane.normal );
tent->s.weapon = ent->s.weapon;
}
+#endif
if ( !traceEnt->takedamage) {
return qfalse;
}
+ // BFP - No PW_QUAD damage calculation here
+#if 0
if (ent->client->ps.powerups[PW_QUAD] ) {
G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
s_quadFactor = g_quadfactor.value;
@@ -107,12 +136,273 @@ qboolean CheckGauntletAttack( gentity_t *ent ) {
}
damage = 50 * s_quadFactor;
+#endif
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
damage, 0, MOD_GAUNTLET );
return qtrue;
}
+/*
+===================
+GetEntityNearMeleeRadius
+===================
+*/
+gentity_t *GetEntityNearMeleeRadius( vec3_t point, gentity_t *attacker, gentity_t *target ) { // BFP - Melee near radius detection
+ // BFP - NOTE: Maybe there's an idea that could be detected for any entity than client,
+ // imagine a player who wants to use melee against a rock and break it.
+ // So, that would be MAX_GENTITIES and trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
+ // to trace entities that doesn't have ->client data and just ->takedamage and ->health
+ // like func_breakable ones
+ int i, num, touch[MAX_CLIENTS];
+ vec3_t mins, maxs;
+ gentity_t *prevTarget = target;
+
+ // if already exists, don't apply calculation detection
+ if ( prevTarget->client ) {
+ return prevTarget;
+ }
+
+ VectorAdd( point, attacker->r.mins, mins );
+ VectorAdd( point, attacker->r.maxs, maxs );
+ num = G_EntitiesInBox( mins, maxs, touch, level.maxclients );
+
+ for ( i = 0; i < num; i++ ) {
+ target = &g_entities[ touch[i] ];
+ if ( target->client && target->client != attacker->client ) {
+ return target;
+ }
+ }
+
+ return prevTarget;
+}
+
+/*
+===============
+CheckMeleeAttack
+===============
+*/
+qboolean CheckMeleeAttack( gentity_t *attacker ) { // BFP - Melee
+ trace_t tr;
+ vec3_t end;
+ gentity_t *traceTarget;
+ vec3_t traceMins, traceMaxs;
+ float diveRange = g_meleeDiveRange.integer;
+
+ // BFP - Monster gamemode, adjust dive range for player monster
+ if ( attacker->client->ps.eFlags & EF_MONSTER ) {
+ diveRange *= 2.5;
+ }
+
+ // set aiming directions
+ AngleVectors( attacker->client->ps.viewangles, forward, NULL, NULL );
+ CalcMuzzlePoint( attacker, forward, NULL, NULL, muzzle );
+ VectorMA( muzzle, diveRange, forward, end );
+
+ // BFP - Monster gamemode, use scaled bounding box for player monster
+ if ( attacker->client->ps.eFlags & EF_MONSTER ) {
+ // scale down the trace box for monsters to allow teleportation through tighter spaces
+ // use 60% of monster size to avoid getting stuck
+ VectorScale( attacker->r.mins, 0.6, traceMins );
+ VectorScale( attacker->r.maxs, 0.6, traceMaxs );
+ } else {
+ VectorCopy( attacker->r.mins, traceMins );
+ VectorCopy( attacker->r.maxs, traceMaxs );
+ }
+
+ // that part is where the target can be detected when the attacker is on air, if not, the trace will be different
+ trap_Trace( &tr, muzzle, traceMins, traceMaxs, end, attacker->s.number, MASK_SHOT );
+ if ( attacker->client->ps.groundEntityNum != ENTITYNUM_NONE ) {
+ trap_Trace( &tr, muzzle, NULL, NULL, end, attacker->s.number, MASK_SHOT );
+ }
+
+ // when the attacker is very near from the target, continue attacking if that happens
+ traceTarget = GetEntityNearMeleeRadius( muzzle, attacker, &g_entities[ tr.entityNum ] );
+
+ // avoid entity null references
+ if ( !traceTarget || traceTarget == NULL ) {
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+
+ // stop melee if there's no entity
+ if ( !traceTarget->takedamage ) {
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+
+ // avoid if the entity isn't a player (e.g. a breakable map entity)
+ if ( traceTarget->s.eType != ET_PLAYER ) {
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+
+ // the target's corpse is starting to sink, avoid interacting with a sinking corpse, nothing special happens
+ if ( traceTarget->physicsObject ) {
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+
+ // BFP - NOTE: Apply g_friendlyFire for melee, originally in BFP, friendly fire wasn't never applied
+ // stop melee if the entity is in the same team
+ if ( OnSameTeam( traceTarget, attacker ) ) {
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+
+ // ENTITY DETECTED!
+ if ( traceTarget->client ) {
+ gentity_t *target;
+ vec3_t direction;
+ float distance;
+ // BFP - Melee range, it isn't known why, but it's the approximation
+ float rangeMultiplier = g_meleeRange.integer + 45;
+
+ // BFP - Monster gamemode, adjust range multiplier for player monster
+ if ( attacker->client->ps.eFlags & EF_MONSTER ) {
+ rangeMultiplier *= 2.5;
+ }
+
+ VectorSubtract( traceTarget->client->ps.origin, attacker->client->ps.origin, direction );
+ distance = VectorLength( direction );
+
+ // distance from the attacker and the target, more range = attacker can attack at that distance without being teleported
+ // the distance needs to be greater than 25, otherwise it won't respect the lengths around the target
+ if ( distance >= rangeMultiplier && distance >= 25 ) {
+ // trace only when the player is alive
+ if ( traceTarget->client->ps.pm_type != PM_DEAD ) {
+ trap_Trace( &tr, muzzle, traceMins, traceMaxs, end, attacker->s.number, MASK_PLAYERSOLID );
+ }
+
+ // if the target is very near to some brush (solid or surface with no impact) from the map
+ // avoid the attacker teleporting there, otherwise gets stuck
+ if ( tr.startsolid || tr.allsolid ) {
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+
+ // if the target position is being covered under something solid (e.g. a brush from the map),
+ // avoid the attacker teleporting there, otherwise gets stuck
+ target = &g_entities[ tr.entityNum ];
+ if ( target && target->client // avoids DLL/SO crash
+ && target->client->ps.pm_type != PM_DEAD && !target->takedamage ) {
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+
+ // try to trace when the target is dead, that's what BFP originally did, and teleport near the corpse
+ if ( traceTarget->client->ps.pm_type == PM_DEAD ) {
+ trap_Trace( &tr, muzzle, traceMins, traceMaxs, end, attacker->s.number, MASK_PLAYERSOLID );
+ }
+
+ // TELEPORT!
+ if ( target && target->client // avoids DLL/SO crash
+ && attacker->client->ps.origin[2] == target->client->ps.origin[2] ) {
+ tr.endpos[2] = target->client->ps.origin[2];
+ }
+
+ // BFP - Monster gamemode, for player monster, adjust teleport position to account for larger bbox
+ if ( attacker->client->ps.eFlags & EF_MONSTER ) {
+ vec3_t adjustedPos, pullBack;
+ VectorCopy( tr.endpos, adjustedPos );
+
+ // pull back slightly from target to ensure monster fits
+ VectorSubtract( attacker->client->ps.origin, traceTarget->client->ps.origin, pullBack );
+ VectorNormalize( pullBack );
+ VectorMA( adjustedPos, 30.0f, pullBack, adjustedPos );
+
+ // final check that monster fits at this position
+ trap_Trace( &tr, adjustedPos, attacker->r.mins, attacker->r.maxs, adjustedPos, attacker->s.number, MASK_PLAYERSOLID );
+ if ( !tr.startsolid && !tr.allsolid ) {
+ VectorCopy( adjustedPos, attacker->client->ps.origin );
+ } else { // can't fit, abort melee
+ attacker->client->ps.pm_flags &= ~PMF_MELEE;
+ return qfalse;
+ }
+ } else {
+ VectorCopy( tr.endpos, attacker->client->ps.origin );
+ }
+ }
+
+ // BFP - Don't deal damage on warmup
+ if ( level.time < level.warmupTime ) {
+ // act as if the player is attacking
+ return qtrue;
+ }
+
+ // PUSH AND DEAL DAMAGE!
+ if ( g_meleeDamage.integer > 0 ) {
+ // consume 5% of ki when being defended and apply knockback
+ if ( ( traceTarget->client->ps.pm_flags & PMF_BLOCK ) && traceTarget->client->blockKnockbackTime <= 0 ) {
+ traceTarget->client->ps.stats[STAT_KI] -= traceTarget->client->ps.stats[STAT_MAX_KI] * 0.05;
+ traceTarget->client->blockKnockbackTime = level.time + 250;
+ }
+ G_Damage ( traceTarget, attacker, attacker, direction, forward,
+ g_meleeDamage.integer, 0, MOD_MELEE );
+ if ( g_meleeDamage.integer < 4 ) { // lose altitude while flying, when damage is lesser than 4
+ traceTarget->client->ps.velocity[2] -= 300;
+ }
+ if ( g_meleeDamage.integer < 30 ) {
+ traceTarget->client->ps.pm_time = 200;
+ }
+ traceTarget->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
+ }
+ }
+
+ // if the attacker is using ki boost, stun the target! (g_hitStun enabled only)
+ if ( g_hitStun.integer >= 1
+ && ( attacker->client->ps.eFlags & EF_KI_BOOST )
+ && traceTarget->client->ps.stats[STAT_HITSTUN_TIME] <= 0
+ && !( traceTarget->client->ps.pm_flags & PMF_BLOCK )
+ && attacker->client->hitStunMeleeDelayTime <= 0 ) {
+ // add 3 seconds to the hitstun when there's no delay
+ traceTarget->client->ps.stats[STAT_HITSTUN_TIME] = 3000;
+ attacker->client->hitStunMeleeDelayTime = level.time + 6000;
+ }
+
+ return qtrue;
+}
+
+/*
+===============
+ApplyMuzzleOffsets
+===============
+*/
+static void ApplyMuzzleOffsets( gentity_t *ent, float randXOffset, float randYOffset, float alternatingXOffset ) { // BFP - Muzzle offsets
+ // randXOffset, randYOffset and alternatingXOffset
+ if ( randXOffset > 0 ) {
+ VectorMA( muzzle, crandom() * randXOffset, right, muzzle );
+ }
+ if ( randYOffset > 0 ) {
+ VectorMA( muzzle, crandom() * randYOffset, up, muzzle );
+ }
+
+ if ( alternatingXOffset > 0 ) {
+ float side = ( ent->client->alternatingOffsetSide ) ? alternatingXOffset : -alternatingXOffset;
+ VectorMA( muzzle, side, right, muzzle );
+ ent->client->alternatingOffsetSide = !ent->client->alternatingOffsetSide;
+ }
+}
+
+/*
+===============
+ApplyConeOfFire
+===============
+*/
+static void ApplyConeOfFire( float coneOfFireX, float coneOfFireY ) { // BFP - Cone of fire
+ if ( coneOfFireX <= 0 && coneOfFireY <= 0 ) {
+ return;
+ }
+ if ( coneOfFireX > 0 ) {
+ VectorMA( forward, crandom() * coneOfFireX * 0.001f, right, forward );
+ }
+ if ( coneOfFireY > 0 ) {
+ VectorMA( forward, crandom() * coneOfFireY * 0.001f, up, forward );
+ }
+ if ( coneOfFireX > 0 || coneOfFireY > 0 ) {
+ VectorNormalize( forward );
+ }
+}
/*
======================================================================
@@ -155,48 +445,50 @@ void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
float u;
gentity_t *tent;
gentity_t *traceEnt;
- int i, passent;
+ int passent;
+
+ // BFP - bfp_weapon.cfg: randYOffset, randXOffset, range
+ float randYOffset = 35;
+ float randXOffset = 35;
+ float range = 1500;
damage *= s_quadFactor;
- r = random() * M_PI * 2.0f;
- u = sin(r) * crandom() * spread * 16;
- r = cos(r) * crandom() * spread * 16;
- VectorMA (muzzle, 8192*16, forward, end);
+ r = crandom() * randXOffset;
+ u = crandom() * randYOffset;
+ VectorMA (muzzle, range, forward, end);
VectorMA (end, r, right, end);
VectorMA (end, u, up, end);
passent = ent->s.number;
- for (i = 0; i < 10; i++) {
- trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
- if ( tr.surfaceFlags & SURF_NOIMPACT ) {
- return;
- }
+ trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
+ if ( tr.surfaceFlags & SURF_NOIMPACT ) {
+ return;
+ }
- traceEnt = &g_entities[ tr.entityNum ];
+ traceEnt = &g_entities[ tr.entityNum ];
- // snap the endpos to integers, but nudged towards the line
- SnapVectorTowards( tr.endpos, muzzle );
+ // snap the endpos to integers, but nudged towards the line
+ SnapVectorTowards( tr.endpos, muzzle );
- // send bullet impact
- if ( traceEnt->takedamage && traceEnt->client ) {
- tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
- tent->s.eventParm = traceEnt->s.number;
- if( LogAccuracyHit( traceEnt, ent ) ) {
- ent->client->accuracy_hits++;
- }
- } else {
- tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
- tent->s.eventParm = DirToByte( tr.plane.normal );
+ // send bullet impact
+ if ( traceEnt->takedamage && traceEnt->client ) {
+ tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); // BFP - Before Q3: EV_BULLET_HIT_FLESH
+ tent->s.eventParm = DirToByte( tr.plane.normal ); // BFP - Before Q3: traceEnt->s.number
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ ent->client->accuracy_hits++;
}
- tent->s.otherEntityNum = ent->s.number;
+ } else {
+ tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); // BFP - Before Q3: EV_BULLET_HIT_WALL
+ tent->s.eventParm = DirToByte( tr.plane.normal );
+ }
+ tent->s.weapon = ent->s.weapon; // BFP - Sends weapon info to the event
+ tent->s.otherEntityNum = ent->s.number;
- if ( traceEnt->takedamage) {
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- damage, 0, MOD_MACHINEGUN);
- }
- break;
+ if ( traceEnt->takedamage ) {
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_MACHINEGUN);
}
}
@@ -212,7 +504,8 @@ BFG
void BFG_Fire ( gentity_t *ent ) {
gentity_t *m;
- m = fire_bfg (ent, muzzle, forward);
+ // BFP - Homing disk testing
+ m = fire_disk( ent, muzzle, forward ); // fire_bfg (ent, muzzle, forward);
m->damage *= s_quadFactor;
m->splashDamage *= s_quadFactor;
@@ -220,6 +513,71 @@ void BFG_Fire ( gentity_t *ent ) {
}
+// BFP - Forcefield
+/*
+======================================================================
+
+FORCEFIELD
+
+======================================================================
+*/
+
+void Forcefield_Fire ( gentity_t *ent ) { // BFP - Forcefield fire
+ if ( ent->client->hook ) {
+ if ( !ent->client->hook->inuse ) {
+ ent->client->hook = NULL;
+ } else {
+ return;
+ }
+ }
+ fire_forcefield( ent, muzzle );
+}
+
+static void Forcefield_Free( gentity_t *self ) { // BFP - Forcefield free
+ self->parent->client->hook = NULL;
+ G_FreeEntity( self );
+}
+
+void Weapon_Forcefield_Think ( gentity_t *ent ) { // BFP - Forcefield
+ gentity_t *rad = NULL;
+ int damage = ( ent->splashDamage ) ? ent->splashDamage : ent->damage;
+ qboolean chargeAutoFire = qtrue;
+
+ if ( !ent->parent || !ent->parent->client || !ent->parent->client->hook
+ || ent->parent->client->ps.pm_type == PM_DEAD
+ || ent->parent->client->pers.connected == CON_DISCONNECTED ) {
+ Forcefield_Free( ent );
+ return;
+ }
+
+ if ( chargeAutoFire && ent->parent->client->ps.weaponstate != WEAPON_FIRING
+ && ent->parent->client->ps.weaponstate != WEAPON_ACTIVE ) {
+ Forcefield_Free( ent );
+ return;
+ }
+
+ VectorCopy( ent->parent->r.currentOrigin, ent->r.currentOrigin );
+ VectorCopy( ent->parent->r.currentOrigin, ent->s.pos.trBase );
+ ent->s.pos.trTime = level.time;
+
+ // corrects force field origin
+ BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
+
+ while ( ( rad = FindRadius(rad, ent->r.currentOrigin, ent->radius) ) != NULL ) {
+ if ( IsValidTargetRadius( ent, rad ) ) {
+ G_Damage( rad, ent, ent->parent, forward, rad->r.currentOrigin,
+ damage, 0, ent->methodOfDeath );
+ }
+ }
+
+ // no chargeAutoFire: removes hook pointer at that instant
+ if ( !chargeAutoFire ) {
+ Forcefield_Free( ent );
+ return;
+ }
+ ent->nextthink = level.time + 200;
+}
+
/*
======================================================================
@@ -234,31 +592,26 @@ SHOTGUN
qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
trace_t tr;
- int damage, i, passent;
+ int damage;
gentity_t *traceEnt;
- vec3_t tr_start, tr_end;
- passent = ent->s.number;
- VectorCopy( start, tr_start );
- VectorCopy( end, tr_end );
- for (i = 0; i < 10; i++) {
- trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT);
- traceEnt = &g_entities[ tr.entityNum ];
-
- // send bullet impact
- if ( tr.surfaceFlags & SURF_NOIMPACT ) {
- return qfalse;
- }
+ trap_Trace (&tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
+ traceEnt = &g_entities[ tr.entityNum ];
- if ( traceEnt->takedamage) {
- damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
- G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN);
- if( LogAccuracyHit( traceEnt, ent ) ) {
- return qtrue;
- }
- }
+ // send bullet impact
+ if ( tr.surfaceFlags & SURF_NOIMPACT ) {
return qfalse;
}
+
+ if ( traceEnt->takedamage ) {
+ damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
+
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_SHOTGUN );
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ return qtrue;
+ }
+ }
return qfalse;
}
@@ -268,7 +621,7 @@ void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
float r, u;
vec3_t end;
vec3_t forward, right, up;
- int oldScore;
+ // int oldScore;
qboolean hitClient = qfalse;
// derive the right and up vectors from the forward vector, because
@@ -277,7 +630,7 @@ void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
PerpendicularVector( right, forward );
CrossProduct( forward, right, up );
- oldScore = ent->client->ps.persistant[PERS_SCORE];
+ // oldScore = ent->client->ps.persistant[PERS_SCORE];
// generate the "random" spread pattern
for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
@@ -320,12 +673,26 @@ void weapon_grenadelauncher_fire (gentity_t *ent) {
gentity_t *m;
// extra vertical velocity
- forward[2] += 0.2f;
- VectorNormalize( forward );
+ //forward[2] += 0.2f;
+ //VectorNormalize( forward );
+ ApplyMuzzleOffsets( ent, 7, 25, 15 ); // BFP - Muzzle offsets
+ ApplyConeOfFire( 40, 50 ); // BFP - Cone of fire
m = fire_grenade (ent, muzzle, forward);
- m->damage *= s_quadFactor;
- m->splashDamage *= s_quadFactor;
+
+ // BFP - If the projectile muzzle offset landed within solid or out of bounds geometry,
+ // then remove
+ {
+ trace_t tr;
+ trap_Trace( &tr, m->s.pos.trBase, m->r.mins, m->r.maxs, muzzle, m->s.number, MASK_SOLID );
+ if ( tr.fraction < 1.0f || ( tr.surfaceFlags & SURF_NOIMPACT ) ) {
+ m->parent->client->hook = NULL;
+ G_FreeEntity( m );
+ }
+ }
+
+ //m->damage *= s_quadFactor;
+ //m->splashDamage *= s_quadFactor;
// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
}
@@ -381,51 +748,32 @@ RAILGUN
weapon_railgun_fire
=================
*/
-#define MAX_RAIL_HITS 4
void weapon_railgun_fire (gentity_t *ent) {
vec3_t end;
trace_t trace;
- gentity_t *tent;
- gentity_t *traceEnt;
+ gentity_t *tent = NULL;
+ gentity_t *traceEnt = NULL;
int damage;
- int i;
int hits;
- int unlinked;
int passent;
- gentity_t *unlinkedEntities[MAX_RAIL_HITS];
+ // BFP - For splash damage
+ int splashRadius = 120;
- damage = 100 * s_quadFactor;
+ // BFP - Damage
+ damage = 60;
VectorMA (muzzle, 8192, forward, end);
// trace only against the solids, so the railgun will go through people
- unlinked = 0;
hits = 0;
passent = ent->s.number;
- do {
- trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
- if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
- break;
- }
+ trap_Trace( &trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
+ if ( trace.entityNum < ENTITYNUM_MAX_NORMAL ) {
traceEnt = &g_entities[ trace.entityNum ];
- if ( traceEnt->takedamage ) {
- if( LogAccuracyHit( traceEnt, ent ) ) {
- hits++;
- }
- G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
+ if ( traceEnt && traceEnt->takedamage ) {
+ // BFP - Railgun events are also treated as a missile
+ tent = G_TempEntity( trace.endpos, EV_MISSILE_HIT );
}
- if ( trace.contents & CONTENTS_SOLID ) {
- break; // we hit something solid enough to stop the beam
- }
- // unlink this entity, so the next trace will go past it
- trap_UnlinkEntity( traceEnt );
- unlinkedEntities[unlinked] = traceEnt;
- unlinked++;
- } while ( unlinked < MAX_RAIL_HITS );
-
- // link back in any entities we unlinked
- for ( i = 0 ; i < unlinked ; i++ ) {
- trap_LinkEntity( unlinkedEntities[i] );
}
// the final trace endpos will be the terminal point of the rail trail
@@ -433,6 +781,14 @@ void weapon_railgun_fire (gentity_t *ent) {
// snap the endpos to integers to save net bandwidth, but nudged towards the line
SnapVectorTowards( trace.endpos, muzzle );
+ // BFP - Moved EV_MISSILE_MISS there
+ // no explosion at end if SURF_NOIMPACT, but still make the trail
+ if ( !( trace.surfaceFlags & SURF_NOIMPACT ) ) {
+ // BFP - Railgun events are also treated as a missile
+ tent = G_TempEntity( trace.endpos, EV_MISSILE_MISS );
+ tent->s.eventParm = DirToByte( trace.plane.normal ); // BFP - Sends dir vector variable to the event
+ }
+
// send railgun beam effect
tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
@@ -444,13 +800,10 @@ void weapon_railgun_fire (gentity_t *ent) {
VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
- // no explosion at end if SURF_NOIMPACT, but still make the trail
- if ( trace.surfaceFlags & SURF_NOIMPACT ) {
- tent->s.eventParm = 255; // don't make the explosion at the end
- } else {
- tent->s.eventParm = DirToByte( trace.plane.normal );
+ // BFP - Finger beam splash damage
+ if ( G_RadiusDamage( ent, trace.endpos, ent, damage, splashRadius, 0, MOD_RAILGUN ) ) {
+ hits++;
}
- tent->s.clientNum = ent->s.clientNum;
// give the shooter a reward sound if they have made two railgun hits in a row
if ( hits == 0 ) {
@@ -461,10 +814,12 @@ void weapon_railgun_fire (gentity_t *ent) {
ent->client->accurateCount += hits;
if ( ent->client->accurateCount >= 2 ) {
ent->client->accurateCount -= 2;
- ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
+ // BFP - No impressive counter
+ // ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
// add the sprite over the player's head
- ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
+ // BFP - No impressive, gauntlet, defend, assist and cap medals
+ ent->client->ps.eFlags &= ~(EF_AWARD_EXCELLENT /*| EF_AWARD_IMPRESSIVE | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP*/ );
+ // ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
}
ent->client->accuracy_hits++;
@@ -515,6 +870,316 @@ void Weapon_HookThink (gentity_t *ent)
}
#endif
+// BFP - BFP Beam attack
+/*
+======================================================================
+
+BFP BEAM
+
+======================================================================
+*/
+
+void Weapon_BFPBeam_Fire ( gentity_t *ent ) // BFP - BFP Beam fire
+{
+ if ( !ent->client->fireHeld && !ent->client->hook )
+ fire_bfpbeam ( ent, muzzle, forward );
+
+ ent->client->fireHeld = qtrue;
+}
+
+void Weapon_BFPBeamFree ( gentity_t *ent ) // BFP - BFP Beam free
+{
+ if ( ent && ent->parent && ent->parent->client ) {
+ ent->parent->client->hook = NULL;
+ ent->parent->client->ps.weaponstate = WEAPON_READY;
+ ent->parent->client->ps.generic1 = 0; // BFP - Reset ki charge points
+ ent->parent->client->fireHeld = qfalse;
+ }
+ G_FreeEntity( ent );
+}
+
+static void Weapon_BFPBeam_SetDistance( gentity_t *beam, float distance, vec3_t beamViewPos ) { // BFP - Set BFP beam distance
+ vec3_t newPos;
+ float correctBeamDist;
+
+ if ( !beam->parent->client ) {
+ return;
+ }
+
+ AngleVectors( beam->parent->client->ps.viewangles, forward, NULL, NULL );
+ CalcMuzzlePoint( beam->parent, forward, NULL, NULL, muzzle );
+
+ VectorMA( muzzle, distance, forward, newPos );
+
+ VectorCopy( newPos, beam->r.currentOrigin );
+ VectorCopy( newPos, beam->s.pos.trBase );
+ VectorScale( forward, beam->speed, newPos );
+ VectorCopy( newPos, beam->s.pos.trDelta );
+ beam->distance = distance;
+
+ correctBeamDist = Distance( beam->r.currentOrigin, beamViewPos );
+ if ( correctBeamDist != distance ) {
+ VectorClear( beam->s.pos.trDelta );
+ }
+ beam->s.pos.trTime = level.time;
+}
+
+static qboolean Weapon_BFPBeamStruggle( gentity_t *ent, vec3_t ownerViewPos, float ownerDist ) { // BFP - BFP Beam struggle
+ gentity_t *target = NULL;
+ gentity_t *rad = NULL;
+ vec3_t raddir, targetViewPos;
+ float distTarget, powerEnt, powerTarget;
+ const float BEAM_PUSH_STEP = 200.0f;
+ float struggleRadius = ( ent->splashRadius > ent->radius ) ? ent->splashRadius : ent->radius;
+
+ while ( ( rad = FindRadius(rad, ent->r.currentOrigin, struggleRadius) ) != NULL ) {
+ if ( rad && ( rad->r.ownerNum == ent->r.ownerNum
+ || rad->s.eType != ET_MISSILE ) ) { // projectiles only
+ continue;
+ }
+ if ( rad && rad != ent ) {
+ // BFP - If there's a piercing projectile, break it
+ trace_t trace;
+ trap_Trace( &trace, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin,
+ ent->r.ownerNum, ent->clipmask );
+
+ // BFP - Priority (only for non-beam weapons)
+ if ( rad->attackType != ATK_BEAM ) {
+ if ( ent->priority > rad->priority || rad->priority <= 0 ) {
+ // BFP - If it's a splitting ki ball, break and split!
+ if ( !G_BreakRDMissile( rad ) ) {
+ rad->parent->client->ps.weaponstate = WEAPON_READY;
+ G_MissileImpact( rad, &trace );
+ }
+ continue;
+ } else if ( ent->priority < rad->priority ) {
+ G_MissileImpact( rad, &trace );
+ return qtrue;
+ }
+ }
+
+ if ( rad->attackType == ATK_BEAM ) {
+ VectorSubtract( rad->r.currentOrigin, ent->r.currentOrigin, raddir );
+ distTarget = VectorLength( raddir );
+ if ( target == NULL ) {
+ target = rad;
+ }
+ }
+ }
+ }
+
+ if ( !target ) {
+ return qfalse;
+ }
+
+ if ( !ent->parent || !ent->parent->client || !target->parent || !target->parent->client ) {
+ return qfalse;
+ }
+
+ // you're in a struggle, don't move!
+ ent->parent->client->ps.weaponstate = WEAPON_BEAMSTRUGGLE;
+ target->parent->client->ps.weaponstate = WEAPON_BEAMSTRUGGLE;
+
+ // calculate power using calculated damage
+ powerEnt = (float)ent->damage;
+ powerTarget = (float)target->damage;
+ if ( ( ent->parent->client->ps.eFlags & EF_KI_BOOST )
+ || ( ent->parent->client->pers.cmd.buttons & BUTTON_KI_USE ) ) {
+ powerEnt *= 2;
+ }
+ if ( ( target->parent->client->ps.eFlags & EF_KI_BOOST )
+ || ( target->parent->client->pers.cmd.buttons & BUTTON_KI_USE ) ) {
+ powerTarget *= 2;
+ }
+
+ // get beam target distance
+ VectorCopy( target->parent->client->ps.origin, targetViewPos );
+ targetViewPos[2] += target->parent->client->ps.viewheight;
+ VectorSubtract( target->r.currentOrigin, targetViewPos, raddir );
+ distTarget = VectorLength( raddir );
+
+ if ( powerEnt > powerTarget ) {
+ // target beam loses distance
+ Weapon_BFPBeam_SetDistance( target, distTarget - BEAM_PUSH_STEP, targetViewPos );
+ } else if ( powerTarget > powerEnt ) {
+ // owner beam loses distance
+ Weapon_BFPBeam_SetDistance( ent, ownerDist - BEAM_PUSH_STEP, ownerViewPos );
+ } else {
+ // both beams lose distance
+ Weapon_BFPBeam_SetDistance( ent, ownerDist - BEAM_PUSH_STEP, ownerViewPos );
+ Weapon_BFPBeam_SetDistance( target, distTarget - BEAM_PUSH_STEP, targetViewPos );
+ }
+
+ // visual effect
+ VectorAdd( ent->r.currentOrigin, target->r.currentOrigin, targetViewPos );
+ VectorScale( targetViewPos, 0.5f, targetViewPos );
+ {
+ gentity_t *effect = G_TempEntity( targetViewPos, EV_SPARK );
+ effect->s.otherEntityNum = ent->s.number;
+ effect->s.otherEntityNum2 = target->s.number;
+ }
+ return qtrue;
+}
+
+static qboolean Weapon_BFPBeamTrace ( gentity_t *ent, vec3_t origin ) { // BFP - Beam trace
+ trace_t trace;
+
+ trap_Trace( &trace, origin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin,
+ ent->r.ownerNum, MASK_SHOT | MASK_SOLID );
+
+ if ( trace.surfaceFlags & SURF_NOIMPACT ) {
+ Weapon_BFPBeamFree( ent );
+ return qtrue;
+ }
+
+ if ( trace.fraction < 1.0 ) {
+ VectorCopy( trace.endpos, ent->r.currentOrigin );
+ G_MissileImpact( ent, &trace );
+ return qtrue;
+ }
+ return qfalse;
+}
+
+void Weapon_BFPBeamRun ( gentity_t *ent ) // BFP - BFP Beam run
+{
+ vec3_t ownerViewPos, vel, dir;
+ float distance, deltaTime;
+
+ if ( ent->attackType != ATK_BEAM ) {
+ return;
+ }
+
+ VectorCopy( ent->parent->client->ps.origin, ownerViewPos );
+ ownerViewPos[2] += ent->parent->client->ps.viewheight;
+ AngleVectors( ent->parent->client->ps.viewangles, forward, NULL, NULL );
+
+ CalcMuzzlePoint( ent->parent, forward, NULL, NULL, muzzle );
+
+ VectorSubtract( ent->r.currentOrigin, ownerViewPos, dir );
+
+ // to fix timescale < 1 issues
+ deltaTime = ( level.time - ent->deltaTime ) * 0.001f;
+ ent->deltaTime = level.time;
+ distance = ent->distance + ent->speed * deltaTime;
+ ent->distance = distance;
+
+ VectorMA( muzzle, distance, forward, ent->s.pos.trBase );
+ distance = VectorLength( dir );
+ if ( !Weapon_BFPBeamStruggle( ent, ownerViewPos, distance ) ) {
+ VectorScale( forward, ent->speed, vel );
+ VectorCopy( vel, ent->s.pos.trDelta );
+ }
+ if ( !ent->inuse ) { // if already disappeared
+ return;
+ }
+ ent->s.pos.trTime = level.time; // smooth it
+
+ BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
+
+ if ( Weapon_BFPBeamTrace( ent, ownerViewPos ) ) {
+ return;
+ }
+ Weapon_BFPBeamTrace( ent, ent->r.currentOrigin );
+}
+
+// BFP - sbeam (Super Beam?)
+/*
+======================================================================
+
+SBEAM
+
+======================================================================
+*/
+
+void Weapon_SBeam_Fire ( gentity_t *ent ) // BFP - sbeam (Super Beam?) fire
+{
+ if ( !ent->client->fireHeld && !ent->client->hook )
+ fire_sbeam ( ent, muzzle, forward );
+
+ ent->client->fireHeld = qtrue;
+}
+
+static qboolean Weapon_SBeamRadius( gentity_t *ent ) { // BFP - sbeam (Super Beam?) radius
+ gentity_t *rad = NULL;
+ float radius = ( ent->splashRadius > ent->radius ) ? ent->splashRadius : ent->radius;
+
+ if ( !ent->parent || !ent->parent->client ) {
+ return qtrue;
+ }
+
+ while ( ( rad = FindRadius(rad, ent->r.currentOrigin, radius) ) != NULL ) {
+ if ( rad && ( rad->r.ownerNum == ent->r.ownerNum
+ || rad->s.eType != ET_MISSILE ) ) { // projectiles only
+ continue;
+ }
+ if ( rad && rad != ent ) {
+ // BFP - If there's a piercing projectile, break it
+ trace_t trace;
+ trap_Trace( &trace, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin,
+ ent->r.ownerNum, ent->clipmask );
+
+ // BFP - Priority (only for non-beam weapons)
+ if ( rad->attackType != ATK_SBEAM ) {
+ if ( ent->priority > rad->priority ) {
+ // BFP - If it's a splitting ki ball, break and split!
+ if ( !G_BreakRDMissile( rad ) ) {
+ rad->parent->client->ps.weaponstate = WEAPON_READY;
+ G_MissileImpact( rad, &trace );
+ }
+ continue;
+ } else if ( ent->priority < rad->priority ) {
+ G_MissileImpact( rad, &trace );
+ return qtrue;
+ }
+ }
+
+ // if one of them has more damage power, the other breaks
+ if ( rad->attackType == ATK_SBEAM ) {
+ if ( ent->damage > rad->damage ) {
+ G_MissileImpact( ent, &trace );
+ continue;
+ } else if ( ent->damage < rad->damage ) {
+ G_MissileImpact( rad, &trace );
+ return qtrue;
+ }
+ G_MissileImpact( rad, &trace );
+ G_MissileImpact( ent, &trace );
+ return qtrue;
+ }
+ }
+ }
+ return qfalse;
+}
+
+void Weapon_SBeam_Run ( gentity_t *ent ) // BFP - sbeam (Super Beam?) run
+{
+ vec3_t ownerViewPos, vel;
+
+ if ( ent->attackType != ATK_SBEAM ) {
+ return;
+ }
+
+ VectorCopy( ent->parent->client->ps.origin, ownerViewPos );
+ ownerViewPos[2] += ent->parent->client->ps.viewheight;
+ AngleVectors( ent->parent->client->ps.viewangles, forward, NULL, NULL );
+
+ CalcMuzzlePoint( ent->parent, forward, NULL, NULL, muzzle );
+
+ VectorScale( forward, ent->speed, vel );
+ VectorCopy( vel, ent->s.pos.trDelta );
+ VectorCopy( ownerViewPos, ent->s.pos.trBase );
+
+ BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
+
+ if ( Weapon_BFPBeamTrace( ent, ownerViewPos ) ) {
+ return;
+ }
+ if ( Weapon_SBeamRadius( ent ) ) {
+ return;
+ }
+ Weapon_BFPBeamTrace( ent, ent->r.currentOrigin );
+}
+
/*
======================================================================
@@ -527,41 +1192,44 @@ void Weapon_LightningFire( gentity_t *ent ) {
trace_t tr;
vec3_t end;
gentity_t *traceEnt, *tent;
- int damage, i, passent;
+ int damage, passent;
+ // BFP - bfp_weapon.cfg: range
+ float range = LIGHTNING_RANGE;
damage = 8 * s_quadFactor;
passent = ent->s.number;
- for (i = 0; i < 10; i++) {
- VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
+ VectorMA( muzzle, range, forward, end );
- trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
+ trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
- if ( tr.entityNum == ENTITYNUM_NONE ) {
- return;
- }
+ if ( tr.surfaceFlags & SURF_NOIMPACT /*tr.entityNum == ENTITYNUM_NONE*/ ) {
+ return;
+ }
- traceEnt = &g_entities[ tr.entityNum ];
+ traceEnt = &g_entities[ tr.entityNum ];
- if ( traceEnt->takedamage) {
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- damage, 0, MOD_LIGHTNING);
- }
+ if ( traceEnt->takedamage) {
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_LIGHTNING);
+ }
- if ( traceEnt->takedamage && traceEnt->client ) {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- if( LogAccuracyHit( traceEnt, ent ) ) {
- ent->client->accuracy_hits++;
- }
- } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
- tent->s.eventParm = DirToByte( tr.plane.normal );
+ if ( traceEnt->takedamage && traceEnt->client ) {
+ tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
+ tent->s.otherEntityNum = traceEnt->s.number;
+ tent->s.eventParm = DirToByte( tr.plane.normal );
+ tent->s.weapon = ent->s.weapon;
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ ent->client->accuracy_hits++;
}
+ return;
+ }
- break;
+ // BFP - That random handles avoiding the particle spam with lightning gun type
+ if ( random() < 0.3 ) {
+ tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
+ tent->s.eventParm = DirToByte( tr.plane.normal );
+ tent->s.weapon = ent->s.weapon; // BFP - Sends weapon info to the event
}
}
@@ -664,8 +1332,9 @@ void FireWeapon( gentity_t *ent ) {
case WP_LIGHTNING:
Weapon_LightningFire( ent );
break;
+// BFP - Using that as forcefield
case WP_SHOTGUN:
- weapon_supershotgun_fire( ent );
+ Forcefield_Fire( ent );
break;
case WP_MACHINEGUN:
if ( g_gametype.integer != GT_TEAM ) {
@@ -675,8 +1344,17 @@ void FireWeapon( gentity_t *ent ) {
}
break;
case WP_GRENADE_LAUNCHER:
- weapon_grenadelauncher_fire( ent );
+ {
+ int i;
+ // BFP - Multishot
+ if ( ent->multishot <= 0 ) {
+ ent->multishot = 1;
+ }
+ for ( i = 0; i < ent->multishot; ++i ) {
+ weapon_grenadelauncher_fire( ent );
+ }
break;
+ }
case WP_ROCKET_LAUNCHER:
Weapon_RocketLauncher_Fire( ent );
break;
@@ -689,12 +1367,11 @@ void FireWeapon( gentity_t *ent ) {
case WP_BFG:
BFG_Fire( ent );
break;
-// BFP - no hook
-#if 0
+// BFP - Using that as BFP Beam
case WP_GRAPPLING_HOOK:
- Weapon_GrapplingHook_Fire( ent );
+ //Weapon_SBeam_Fire( ent );
+ Weapon_BFPBeam_Fire( ent );
break;
-#endif
default:
// FIXME G_Error( "Bad ent->s.weapon" );
break;
diff --git a/source/game/game.bat b/source/game/game.bat
index 2a9a3d8..f71cc66 100644
--- a/source/game/game.bat
+++ b/source/game/game.bat
@@ -24,6 +24,8 @@ set cc=..\..\..\tools\bin\lcc.exe -DQ3_VM -S -Wf-target=bytecode -Wf-g -I%src%\c
%cc% %src%/game/q_shared.c
@if errorlevel 1 goto quit
+%cc% %src%/game/ai_bfp.c
+@if errorlevel 1 goto quit
%cc% %src%/game/ai_dmnet.c
@if errorlevel 1 goto quit
%cc% %src%/game/ai_dmq3.c
diff --git a/source/game/game.def b/source/game/game.def
new file mode 100644
index 0000000..2ee748e
--- /dev/null
+++ b/source/game/game.def
@@ -0,0 +1,3 @@
+EXPORTS
+ vmMain
+ dllEntry
diff --git a/source/game/inv.h b/source/game/inv.h
index 8c7e429..0d62a7e 100644
--- a/source/game/inv.h
+++ b/source/game/inv.h
@@ -34,44 +34,27 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define INVENTORY_PLASMAGUN 11
#define INVENTORY_BFG10K 13
#define INVENTORY_GRAPPLINGHOOK 14
-#define INVENTORY_NAILGUN 15
-#define INVENTORY_PROXLAUNCHER 16
-#define INVENTORY_CHAINGUN 17
//ammo
-#define INVENTORY_SHELLS 18
-#define INVENTORY_BULLETS 19
-#define INVENTORY_GRENADES 20
-#define INVENTORY_CELLS 21
-#define INVENTORY_LIGHTNINGAMMO 22
-#define INVENTORY_ROCKETS 23
-#define INVENTORY_SLUGS 24
-#define INVENTORY_BFGAMMO 25
-#define INVENTORY_NAILS 26
-#define INVENTORY_MINES 27
-#define INVENTORY_BELT 28
+#define INVENTORY_SHELLS 15
+#define INVENTORY_BULLETS 16
+#define INVENTORY_GRENADES 17
+#define INVENTORY_CELLS 18
+#define INVENTORY_LIGHTNINGAMMO 20
+#define INVENTORY_ROCKETS 21
+#define INVENTORY_SLUGS 22
+#define INVENTORY_BFGAMMO 23
//powerups
-#define INVENTORY_HEALTH 29
-#define INVENTORY_TELEPORTER 30
-#define INVENTORY_MEDKIT 31
-#define INVENTORY_KAMIKAZE 32
-#define INVENTORY_PORTAL 33
-#define INVENTORY_INVULNERABILITY 34
-#define INVENTORY_QUAD 35
-#define INVENTORY_ENVIRONMENTSUIT 36
-#define INVENTORY_HASTE 37
-#define INVENTORY_INVISIBILITY 38
-#define INVENTORY_REGEN 39
-#define INVENTORY_FLIGHT 40
-#define INVENTORY_SCOUT 41
-#define INVENTORY_GUARD 42
-#define INVENTORY_DOUBLER 43
-#define INVENTORY_AMMOREGEN 44
-
-#define INVENTORY_REDFLAG 45
-#define INVENTORY_BLUEFLAG 46
-#define INVENTORY_NEUTRALFLAG 47
-#define INVENTORY_REDCUBE 48
-#define INVENTORY_BLUECUBE 49
+#define INVENTORY_HEALTH 24
+#define INVENTORY_TELEPORTER 25
+#define INVENTORY_MEDKIT 26
+#define INVENTORY_QUAD 27
+#define INVENTORY_ENVIRONMENTSUIT 28
+#define INVENTORY_HASTE 29
+#define INVENTORY_INVISIBILITY 30
+#define INVENTORY_REGEN 31
+#define INVENTORY_FLIGHT 32
+#define INVENTORY_REDFLAG 33
+#define INVENTORY_BLUEFLAG 34
//enemy stuff
#define ENEMY_HORIZONTAL_DIST 200
#define ENEMY_HEIGHT 201
@@ -115,33 +98,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MODELINDEX_INVISIBILITY 31
#define MODELINDEX_REGEN 32
#define MODELINDEX_FLIGHT 33
-
#define MODELINDEX_REDFLAG 34
#define MODELINDEX_BLUEFLAG 35
-// mission pack only defines
-
-#define MODELINDEX_KAMIKAZE 36
-#define MODELINDEX_PORTAL 37
-#define MODELINDEX_INVULNERABILITY 38
-
-#define MODELINDEX_NAILS 39
-#define MODELINDEX_MINES 40
-#define MODELINDEX_BELT 41
-
-#define MODELINDEX_SCOUT 42
-#define MODELINDEX_GUARD 43
-#define MODELINDEX_DOUBLER 44
-#define MODELINDEX_AMMOREGEN 45
-
-#define MODELINDEX_NEUTRALFLAG 46
-#define MODELINDEX_REDCUBE 47
-#define MODELINDEX_BLUECUBE 48
-
-#define MODELINDEX_NAILGUN 49
-#define MODELINDEX_PROXLAUNCHER 50
-#define MODELINDEX_CHAINGUN 51
-
//
#define WEAPONINDEX_GAUNTLET 1
@@ -154,6 +113,3 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define WEAPONINDEX_PLASMAGUN 8
#define WEAPONINDEX_BFG 9
#define WEAPONINDEX_GRAPPLING_HOOK 10
-#define WEAPONINDEX_NAILGUN 11
-#define WEAPONINDEX_PROXLAUNCHER 12
-#define WEAPONINDEX_CHAINGUN 13
diff --git a/source/game/match.h b/source/game/match.h
index d520932..191712d 100644
--- a/source/game/match.h
+++ b/source/game/match.h
@@ -65,8 +65,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MSG_WHICHTEAM 29 //ask which team a bot is in
#define MSG_TASKPREFERENCE 30 //tell your teamplay task preference
#define MSG_ATTACKENEMYBASE 31 //attack the enemy base
-#define MSG_HARVEST 32 //go harvest
-#define MSG_SUICIDE 33 //order to suicide
+#define MSG_SUICIDE 32 //order to suicide
//
#define MSG_ME 100
#define MSG_EVERYONE 101
@@ -103,7 +102,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ST_CAPTUREDFLAG 8192
#define ST_RETURNEDFLAG 16384
#define ST_TEAM 32768
-#define ST_1FCTFGOTFLAG 65535
//ctf task preferences
#define ST_DEFENDER 1
#define ST_ATTACKER 2
diff --git a/source/game/menudef.h b/source/game/menudef.h
index 7bb7b0a..2518341 100644
--- a/source/game/menudef.h
+++ b/source/game/menudef.h
@@ -92,23 +92,19 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CG_SHOW_BLUE_TEAM_HAS_REDFLAG 0x00000001
#define CG_SHOW_RED_TEAM_HAS_BLUEFLAG 0x00000002
#define CG_SHOW_ANYTEAMGAME 0x00000004
-#define CG_SHOW_HARVESTER 0x00000008
-#define CG_SHOW_ONEFLAG 0x00000010
-#define CG_SHOW_CTF 0x00000020
-#define CG_SHOW_OBELISK 0x00000040
-#define CG_SHOW_HEALTHCRITICAL 0x00000080
-#define CG_SHOW_SINGLEPLAYER 0x00000100
-#define CG_SHOW_TOURNAMENT 0x00000200
-#define CG_SHOW_DURINGINCOMINGVOICE 0x00000400
-#define CG_SHOW_IF_PLAYER_HAS_FLAG 0x00000800
-#define CG_SHOW_LANPLAYONLY 0x00001000
-#define CG_SHOW_MINED 0x00002000
-#define CG_SHOW_HEALTHOK 0x00004000
-#define CG_SHOW_TEAMINFO 0x00008000
-#define CG_SHOW_NOTEAMINFO 0x00010000
-#define CG_SHOW_OTHERTEAMHASFLAG 0x00020000
-#define CG_SHOW_YOURTEAMHASENEMYFLAG 0x00040000
-#define CG_SHOW_ANYNONTEAMGAME 0x00080000
+#define CG_SHOW_CTF 0x00000008
+#define CG_SHOW_HEALTHCRITICAL 0x00000010
+#define CG_SHOW_SINGLEPLAYER 0x00000020
+#define CG_SHOW_TOURNAMENT 0x00000040
+#define CG_SHOW_DURINGINCOMINGVOICE 0x00000080
+#define CG_SHOW_IF_PLAYER_HAS_FLAG 0x00000100
+#define CG_SHOW_LANPLAYONLY 0x00000200
+#define CG_SHOW_HEALTHOK 0x00000400
+#define CG_SHOW_TEAMINFO 0x00000800
+#define CG_SHOW_NOTEAMINFO 0x00001000
+#define CG_SHOW_OTHERTEAMHASFLAG 0x00002000
+#define CG_SHOW_YOURTEAMHASENEMYFLAG 0x00004000
+#define CG_SHOW_ANYNONTEAMGAME 0x00008000
#define CG_SHOW_2DONLY 0x10000000
@@ -168,47 +164,43 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CG_RED_SCORE 28
#define CG_RED_NAME 29
#define CG_BLUE_NAME 30
-#define CG_HARVESTER_SKULLS 31 // only shows in harvester
-#define CG_ONEFLAG_STATUS 32 // only shows in one flag
-#define CG_PLAYER_LOCATION 33
-#define CG_TEAM_COLOR 34
-#define CG_CTF_POWERUP 35
-
-#define CG_AREA_POWERUP 36
-#define CG_AREA_LAGOMETER 37 // painted with old system
-#define CG_PLAYER_HASFLAG 38
-#define CG_GAME_TYPE 39 // not done
+#define CG_PLAYER_LOCATION 31
+#define CG_TEAM_COLOR 32
+#define CG_CTF_POWERUP 33
-#define CG_SELECTEDPLAYER_ARMOR 40
-#define CG_SELECTEDPLAYER_HEALTH 41
-#define CG_PLAYER_STATUS 42
-#define CG_FRAGGED_MSG 43 // painted with old system
-#define CG_PROXMINED_MSG 44 // painted with old system
-#define CG_AREA_FPSINFO 45 // painted with old system
-#define CG_AREA_SYSTEMCHAT 46 // painted with old system
-#define CG_AREA_TEAMCHAT 47 // painted with old system
-#define CG_AREA_CHAT 48 // painted with old system
-#define CG_GAME_STATUS 49
-#define CG_KILLER 50
-#define CG_PLAYER_ARMOR_ICON2D 51
-#define CG_PLAYER_AMMO_ICON2D 52
-#define CG_ACCURACY 53
-#define CG_ASSISTS 54
-#define CG_DEFEND 55
-#define CG_EXCELLENT 56
-#define CG_IMPRESSIVE 57
-#define CG_PERFECT 58
-#define CG_GAUNTLET 59
-#define CG_SPECTATORS 60
-#define CG_TEAMINFO 61
-#define CG_VOICE_HEAD 62
-#define CG_VOICE_NAME 63
-#define CG_PLAYER_HASFLAG2D 64
-#define CG_HARVESTER_SKULLS2D 65 // only shows in harvester
-#define CG_CAPFRAGLIMIT 66
-#define CG_1STPLACE 67
-#define CG_2NDPLACE 68
-#define CG_CAPTURES 69
+#define CG_AREA_POWERUP 34
+#define CG_AREA_LAGOMETER 35 // painted with old system
+#define CG_PLAYER_HASFLAG 36
+#define CG_GAME_TYPE 37 // not done
+
+#define CG_SELECTEDPLAYER_ARMOR 38
+#define CG_SELECTEDPLAYER_HEALTH 39
+#define CG_PLAYER_STATUS 40
+#define CG_FRAGGED_MSG 41 // painted with old system
+#define CG_AREA_FPSINFO 42 // painted with old system
+#define CG_AREA_SYSTEMCHAT 43 // painted with old system
+#define CG_AREA_TEAMCHAT 44 // painted with old system
+#define CG_AREA_CHAT 45 // painted with old system
+#define CG_GAME_STATUS 46
+#define CG_KILLER 47
+#define CG_PLAYER_ARMOR_ICON2D 48
+#define CG_PLAYER_AMMO_ICON2D 49
+#define CG_ACCURACY 50
+#define CG_ASSISTS 51
+#define CG_DEFEND 52
+#define CG_EXCELLENT 53
+#define CG_IMPRESSIVE 54
+#define CG_PERFECT 55
+#define CG_GAUNTLET 56
+#define CG_SPECTATORS 57
+#define CG_TEAMINFO 58
+#define CG_VOICE_HEAD 59
+#define CG_VOICE_NAME 60
+#define CG_PLAYER_HASFLAG2D 61
+#define CG_CAPFRAGLIMIT 62
+#define CG_1STPLACE 63
+#define CG_2NDPLACE 64
+#define CG_CAPTURES 65
diff --git a/source/game/q_math.c b/source/game/q_math.c
index ca40098..2f5b645 100644
--- a/source/game/q_math.c
+++ b/source/game/q_math.c
@@ -208,6 +208,20 @@ void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ) {
cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
+
+// BFP - Vector2Set sets the 2 values of a vec2_t variable in a single line
+void Vector2Set( vec2_t v, float x, float y ) {
+ v[0] = x;
+ v[1] = y;
+}
+
+// BFP - Byte4Set sets the 4 values of a vec4_t variable in a single line
+void Byte4Set( byte v[4], float w, float x, float y, float z ) {
+ v[0] = w;
+ v[1] = x;
+ v[2] = y;
+ v[3] = z;
+}
#endif
//=======================================================
@@ -496,8 +510,12 @@ void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )
float inv_denom;
inv_denom = DotProduct( normal, normal );
+ if ( Q_fabs( inv_denom ) == 0.0f ) {
+ VectorCopy( p, dst );
+ return;
+ }
#ifndef Q3_VM
- assert( Q_fabs(inv_denom) != 0.0f ); // bk010122 - zero vectors get here
+ //assert( Q_fabs(inv_denom) != 0.0f ); // zero vectors get here
#endif
inv_denom = 1.0f / inv_denom;
@@ -549,8 +567,10 @@ void VectorRotate( vec3_t in, vec3_t matrix[3], vec3_t out )
/*
** float q_rsqrt( float number )
*/
+#define ORIGINAL_QRSQRT 0
float Q_rsqrt( float number )
{
+#if ORIGINAL_QRSQRT
long i;
float x2, y;
const float threehalfs = 1.5F;
@@ -563,12 +583,25 @@ float Q_rsqrt( float number )
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
-#ifndef Q3_VM
-#ifdef __linux__
- assert( !isnan(y) ); // bk010122 - FPE?
-#endif
-#endif
return y;
+#else /* BFP - No compiler warnings version */
+ const float threehalfs = 1.5F;
+ float x2, y;
+ union {
+ float f;
+ long i;
+ } conv; // union for safe type-punning
+
+ y = number;
+ x2 = number * 0.5F;
+ conv.f = y;
+ conv.i = 0x5f3759df - ( conv.i >> 1 ); // what the fuck?
+ y = conv.f;
+ y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
+// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
+
+ return y;
+#endif
}
float Q_fabs( float f ) {
@@ -792,7 +825,6 @@ void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ) {
vec_t VectorNormalize( vec3_t v ) {
- // NOTE: TTimo - Apple G4 altivec source uses double?
float length, ilength;
length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
@@ -977,7 +1009,7 @@ void PerpendicularVector( vec3_t dst, const vec3_t src )
{
int pos;
int i;
- float minelem = 1.0F;
+ float f, minelem = 1.0F;
vec3_t tempvec;
/*
@@ -985,10 +1017,11 @@ void PerpendicularVector( vec3_t dst, const vec3_t src )
*/
for ( pos = 0, i = 0; i < 3; i++ )
{
- if ( fabs( src[i] ) < minelem )
+ f = fabs( src[i] );
+ if ( f < minelem )
{
pos = i;
- minelem = fabs( src[i] );
+ minelem = f;
}
}
tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
diff --git a/source/game/q_shared.c b/source/game/q_shared.c
index 3863389..4f618ee 100644
--- a/source/game/q_shared.c
+++ b/source/game/q_shared.c
@@ -58,11 +58,20 @@ char *COM_SkipPath (char *pathname)
COM_StripExtension
============
*/
-void COM_StripExtension( const char *in, char *out ) {
- while ( *in && *in != '.' ) {
- *out++ = *in++;
+void COM_StripExtension( const char *in, char *out, int destsize ) {
+ int length;
+
+ Q_strncpyz(out, in, destsize);
+
+ length = strlen(out)-1;
+ while (length > 0 && out[length] != '.')
+ {
+ length--;
+ if (out[length] == '/')
+ return; // no extension
}
- *out = 0;
+ if ( length )
+ out[ length ] = '\0';
}
@@ -79,7 +88,7 @@ void COM_DefaultExtension (char *path, int maxSize, const char *extension ) {
// if path doesn't have a .EXT, append extension
// (extension should include the .)
//
- src = path + (int)strlen(path) - 1;
+ src = path + strlen(path) - 1;
while (*src != '/' && src != path) {
if ( *src == '.' ) {
@@ -92,144 +101,6 @@ void COM_DefaultExtension (char *path, int maxSize, const char *extension ) {
Com_sprintf( path, maxSize, "%s%s", oldPath, extension );
}
-/*
-============================================================================
-
- BYTE ORDER FUNCTIONS
-
-============================================================================
-*/
-/*
-// can't just use function pointers, or dll linkage can
-// mess up when qcommon is included in multiple places
-static short (*_BigShort) (short l);
-static short (*_LittleShort) (short l);
-static int (*_BigLong) (int l);
-static int (*_LittleLong) (int l);
-static qint64 (*_BigLong64) (qint64 l);
-static qint64 (*_LittleLong64) (qint64 l);
-static float (*_BigFloat) (const float *l);
-static float (*_LittleFloat) (const float *l);
-
-short BigShort(short l){return _BigShort(l);}
-short LittleShort(short l) {return _LittleShort(l);}
-int BigLong (int l) {return _BigLong(l);}
-int LittleLong (int l) {return _LittleLong(l);}
-qint64 BigLong64 (qint64 l) {return _BigLong64(l);}
-qint64 LittleLong64 (qint64 l) {return _LittleLong64(l);}
-float BigFloat (const float *l) {return _BigFloat(l);}
-float LittleFloat (const float *l) {return _LittleFloat(l);}
-*/
-
-short ShortSwap (short l)
-{
- byte b1,b2;
-
- b1 = l&255;
- b2 = (l>>8)&255;
-
- return (b1<<8) + b2;
-}
-
-short ShortNoSwap (short l)
-{
- return l;
-}
-
-int LongSwap (int l)
-{
- byte b1,b2,b3,b4;
-
- b1 = l&255;
- b2 = (l>>8)&255;
- b3 = (l>>16)&255;
- b4 = (l>>24)&255;
-
- return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
-}
-
-int LongNoSwap (int l)
-{
- return l;
-}
-
-qint64 Long64Swap (qint64 ll)
-{
- qint64 result;
-
- result.b0 = ll.b7;
- result.b1 = ll.b6;
- result.b2 = ll.b5;
- result.b3 = ll.b4;
- result.b4 = ll.b3;
- result.b5 = ll.b2;
- result.b6 = ll.b1;
- result.b7 = ll.b0;
-
- return result;
-}
-
-qint64 Long64NoSwap (qint64 ll)
-{
- return ll;
-}
-
-typedef union {
- float f;
- unsigned int i;
-} _FloatByteUnion;
-
-float FloatSwap (const float *f) {
- const _FloatByteUnion *in;
- _FloatByteUnion out;
-
- in = (_FloatByteUnion *)f;
- out.i = LongSwap(in->i);
-
- return out.f;
-}
-
-float FloatNoSwap (const float *f)
-{
- return *f;
-}
-
-/*
-================
-Swap_Init
-================
-*/
-/*
-void Swap_Init (void)
-{
- byte swaptest[2] = {1,0};
-
-// set the byte swapping variables in a portable manner
- if ( *(short *)swaptest == 1)
- {
- _BigShort = ShortSwap;
- _LittleShort = ShortNoSwap;
- _BigLong = LongSwap;
- _LittleLong = LongNoSwap;
- _BigLong64 = Long64Swap;
- _LittleLong64 = Long64NoSwap;
- _BigFloat = FloatSwap;
- _LittleFloat = FloatNoSwap;
- }
- else
- {
- _BigShort = ShortNoSwap;
- _LittleShort = ShortSwap;
- _BigLong = LongNoSwap;
- _LittleLong = LongSwap;
- _BigLong64 = Long64NoSwap;
- _LittleLong64 = Long64Swap;
- _BigFloat = FloatNoSwap;
- _LittleFloat = FloatSwap;
- }
-
-}
-*/
/*
============================================================================
@@ -242,15 +113,24 @@ PARSING
static char com_token[MAX_TOKEN_CHARS];
static char com_parsename[MAX_TOKEN_CHARS];
static int com_lines;
+static int com_tokenline;
+static int is_separator[ 256 ];
void COM_BeginParseSession( const char *name )
{
- com_lines = 0;
+ com_lines = 1;
+ com_tokenline = 0;
Com_sprintf(com_parsename, sizeof(com_parsename), "%s", name);
}
+
int COM_GetCurrentParseLine( void )
{
+ if ( com_tokenline )
+ {
+ return com_tokenline;
+ }
+
return com_lines;
}
@@ -259,30 +139,36 @@ char *COM_Parse( char **data_p )
return COM_ParseExt( data_p, qtrue );
}
+
+extern int Q_vsprintf( char *buffer, const char *fmt, va_list argptr );
+
+
void COM_ParseError( char *format, ... )
{
va_list argptr;
static char string[4096];
va_start (argptr, format);
- vsprintf (string, format, argptr);
+ Q_vsprintf (string, format, argptr);
va_end (argptr);
Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, com_lines, string);
}
+
void COM_ParseWarning( char *format, ... )
{
va_list argptr;
static char string[4096];
va_start (argptr, format);
- vsprintf (string, format, argptr);
+ Q_vsprintf (string, format, argptr);
va_end (argptr);
Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, com_lines, string);
}
+
/*
==============
COM_Parse
@@ -312,6 +198,7 @@ static char *SkipWhitespace( char *data, qboolean *hasNewLines ) {
return data;
}
+
int COM_Compress( char *data_p ) {
char *in, *out;
int c;
@@ -346,7 +233,7 @@ int COM_Compress( char *data_p ) {
*out++ = '\n';
newline = qfalse;
whitespace = qfalse;
- } if (whitespace) {
+ } else if (whitespace) {
*out++ = ' ';
whitespace = qfalse;
}
@@ -380,6 +267,7 @@ int COM_Compress( char *data_p ) {
return out - data_p;
}
+
char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
{
int c = 0, len;
@@ -388,7 +276,8 @@ char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
data = *data_p;
len = 0;
- com_token[0] = 0;
+ com_token[0] = '\0';
+ com_tokenline = 0;
// make sure incoming data is valid
if ( !data )
@@ -423,11 +312,15 @@ char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
}
}
// skip /* */ comments
- else if ( c=='/' && data[1] == '*' )
+ else if ( c == '/' && data[1] == '*' )
{
data += 2;
while ( *data && ( *data != '*' || data[1] != '/' ) )
{
+ if ( *data == '\n' )
+ {
+ com_lines++;
+ }
data++;
}
if ( *data )
@@ -441,22 +334,32 @@ char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
}
}
+ com_tokenline = com_lines;
+
// handle quoted strings
- if (c == '\"')
+ if ( c == '"' )
{
data++;
- while (1)
+ while ( 1 )
{
- c = *data++;
- if (c=='\"' || !c)
+ c = *data;
+ if ( c == '"' || c == '\0' )
{
- com_token[len] = 0;
+ if ( c == '"' )
+ data++;
+
+ com_token[ len ] = '\0';
*data_p = ( char * ) data;
return com_token;
}
- if (len < MAX_TOKEN_CHARS)
+ data++;
+ if ( c == '\n' )
+ {
+ com_lines++;
+ }
+ if ( len < MAX_TOKEN_CHARS-1 )
{
- com_token[len] = c;
+ com_token[ len ] = c;
len++;
}
}
@@ -465,84 +368,22 @@ char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
// parse a regular word
do
{
- if (len < MAX_TOKEN_CHARS)
+ if ( len < MAX_TOKEN_CHARS - 1 )
{
- com_token[len] = c;
+ com_token[ len ] = c;
len++;
}
data++;
c = *data;
- if ( c == '\n' )
- com_lines++;
- } while (c>32);
+ } while ( c > ' ' );
- if (len == MAX_TOKEN_CHARS)
- {
-// Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
- len = 0;
- }
- com_token[len] = 0;
+ com_token[ len ] = '\0';
*data_p = ( char * ) data;
return com_token;
}
-#if 0
-// no longer used
-/*
-===============
-COM_ParseInfos
-===============
-*/
-int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ) {
- char *token;
- int count;
- char key[MAX_TOKEN_CHARS];
-
- count = 0;
-
- while ( 1 ) {
- token = COM_Parse( &buf );
- if ( !token[0] ) {
- break;
- }
- if ( strcmp( token, "{" ) ) {
- Com_Printf( "Missing { in info file\n" );
- break;
- }
-
- if ( count == max ) {
- Com_Printf( "Max infos exceeded\n" );
- break;
- }
-
- infos[count][0] = 0;
- while ( 1 ) {
- token = COM_ParseExt( &buf, qtrue );
- if ( !token[0] ) {
- Com_Printf( "Unexpected end of info file\n" );
- break;
- }
- if ( !strcmp( token, "}" ) ) {
- break;
- }
- Q_strncpyz( key, token, sizeof( key ) );
-
- token = COM_ParseExt( &buf, qfalse );
- if ( !token[0] ) {
- strcpy( token, "" );
- }
- Info_SetValueForKey( infos[count], key, token );
- }
- count++;
- }
-
- return count;
-}
-#endif
-
-
/*
==================
COM_MatchToken
@@ -567,7 +408,7 @@ Skips until a matching close brace is found.
Internal brace depths are properly skipped.
=================
*/
-void SkipBracedSection (char **program) {
+void SkipBracedSection( char **program ) {
char *token;
int depth;
@@ -585,17 +426,23 @@ void SkipBracedSection (char **program) {
} while( depth && *program );
}
+
/*
=================
SkipRestOfLine
=================
*/
-void SkipRestOfLine ( char **data ) {
+void SkipRestOfLine( char **data ) {
char *p;
int c;
p = *data;
- while ( (c = *p++) != 0 ) {
+
+ if ( !*p )
+ return;
+
+ while ( (c = *p) != '\0' ) {
+ p++;
if ( c == '\n' ) {
com_lines++;
break;
@@ -606,6 +453,212 @@ void SkipRestOfLine ( char **data ) {
}
+void Com_InitSeparators( void )
+{
+ is_separator['\n']=1;
+ is_separator[';']=1;
+ is_separator['=']=1;
+ is_separator['{']=1;
+ is_separator['}']=1;
+}
+
+
+void SkipTillSeparators( char **data )
+{
+ char *p;
+ int c;
+
+ p = *data;
+
+ if ( !*p )
+ return;
+
+ while ( (c = *p) != '\0' )
+ {
+ p++;
+ if ( is_separator[ c ] )
+ {
+ if ( c == '\n' )
+ {
+ com_lines++;
+ }
+ break;
+ }
+ }
+
+ *data = p;
+}
+
+
+char *COM_ParseSep( char **data_p, qboolean allowLineBreaks )
+{
+ int c = 0, len;
+ qboolean hasNewLines = qfalse;
+ char *data;
+
+ data = *data_p;
+ len = 0;
+ com_token[0] = '\0';
+ com_tokenline = 0;
+
+ // make sure incoming data is valid
+ if ( !data )
+ {
+ *data_p = NULL;
+ return com_token;
+ }
+
+ while ( 1 )
+ {
+ // skip whitespace
+ data = SkipWhitespace( data, &hasNewLines );
+ if ( !data )
+ {
+ *data_p = NULL;
+ return com_token;
+ }
+ if ( hasNewLines && !allowLineBreaks )
+ {
+ *data_p = data;
+ return com_token;
+ }
+
+ c = *data;
+
+ // skip double slash comments
+ if ( c == '/' && data[1] == '/' )
+ {
+ data += 2;
+ while (*data && *data != '\n') {
+ data++;
+ }
+ }
+ // skip /* */ comments
+ else if ( c == '/' && data[1] == '*' )
+ {
+ data += 2;
+ while ( *data && ( *data != '*' || data[1] != '/' ) )
+ {
+ if ( *data == '\n' )
+ {
+ com_lines++;
+ }
+ data++;
+ }
+ if ( *data )
+ {
+ data += 2;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ com_tokenline = com_lines;
+
+ // handle quoted strings
+ if ( c == '"' )
+ {
+ data++;
+ while ( 1 )
+ {
+ c = *data;
+ if ( c == '"' || c == '\0' )
+ {
+ if ( c == '"' )
+ data++;
+
+ com_token[ len ] = '\0';
+ *data_p = ( char * ) data;
+ return com_token;
+ }
+ data++;
+ if ( c == '\n' )
+ {
+ com_lines++;
+ }
+ if ( len < MAX_TOKEN_CHARS-1 )
+ {
+ com_token[ len ] = c;
+ len++;
+ }
+ }
+ }
+
+ // special case for separators
+ if ( is_separator[ c ] )
+ {
+ com_token[ len ] = c;
+ len++;
+ data++;
+ }
+ else // parse a regular word
+ do
+ {
+ if ( len < MAX_TOKEN_CHARS - 1 )
+ {
+ com_token[ len ] = c;
+ len++;
+ }
+ data++;
+ c = *data;
+ } while ( c > ' ' && !is_separator[ c ] );
+
+ com_token[ len ] = '\0';
+
+ *data_p = ( char * ) data;
+ return com_token;
+}
+
+
+/*
+============
+Com_Split
+============
+*/
+int Com_Split( char *in, char **out, int outsz, int delim )
+{
+ int c;
+ char **o = out, **end = out + outsz;
+ // skip leading spaces
+ if ( delim >= ' ' ) {
+ while( (c = *in) != '\0' && c <= ' ' )
+ in++;
+ }
+ *out = in; out++;
+ while( out < end ) {
+ while( (c = *in) != '\0' && c != delim )
+ in++;
+ *in = '\0';
+ if ( !c ) {
+ // don't count last null value
+ if ( out[-1][0] == '\0' )
+ out--;
+ break;
+ }
+ in++;
+ // skip leading spaces
+ if ( delim >= ' ' ) {
+ while( (c = *in) != '\0' && c <= ' ' )
+ in++;
+ }
+ *out = in; out++;
+ }
+ // sanitize last value
+ while( (c = *in) != '\0' && c != delim )
+ in++;
+ *in = '\0';
+ c = out - o;
+ // set remaining out pointers
+ while( out < end ) {
+ *out = in; out++;
+ }
+ return c;
+}
+
+
void Parse1DMatrix (char **buf_p, int x, float *m) {
char *token;
int i;
@@ -653,6 +706,43 @@ void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m) {
============================================================================
*/
+const byte locase[ 256 ] =
+{
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+ 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+ 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
+ 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
+ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+ 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
+ 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+ 0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
+ 0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
+ 0x78,0x79,0x7a,0x5b,0x5c,0x5d,0x5e,0x5f,
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
+ 0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
+ 0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
+ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
+ 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
+ 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
+ 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
+ 0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
+ 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,
+ 0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
+ 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,
+ 0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
+ 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,
+ 0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
+ 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,
+ 0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
+ 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,
+ 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
+};
+
+
int Q_isprint( int c )
{
if ( c >= 0x20 && c <= 0x7E )
@@ -660,6 +750,7 @@ int Q_isprint( int c )
return ( 0 );
}
+
int Q_islower( int c )
{
if (c >= 'a' && c <= 'z')
@@ -667,6 +758,7 @@ int Q_islower( int c )
return ( 0 );
}
+
int Q_isupper( int c )
{
if (c >= 'A' && c <= 'Z')
@@ -674,6 +766,7 @@ int Q_isupper( int c )
return ( 0 );
}
+
int Q_isalpha( int c )
{
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
@@ -681,6 +774,7 @@ int Q_isalpha( int c )
return ( 0 );
}
+
char* Q_strrchr( const char* string, int c )
{
char cc = c;
@@ -701,6 +795,7 @@ char* Q_strrchr( const char* string, int c )
return sp;
}
+
/*
=============
Q_strncpyz
@@ -708,46 +803,45 @@ Q_strncpyz
Safe strncpy that ensures a trailing zero
=============
*/
-void Q_strncpyz( char *dest, const char *src, int destsize ) {
- // bk001129 - also NULL dest
- if ( !dest ) {
- Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" );
- }
+void Q_strncpyz( char *dest, const char *src, int destsize )
+{
+ if ( !dest ) {
+ Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" );
+ }
+
if ( !src ) {
Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
}
+
if ( destsize < 1 ) {
Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" );
}
strncpy( dest, src, destsize-1 );
- dest[destsize-1] = 0;
+ dest[ destsize-1 ] = '\0';
}
-
-int Q_stricmpn (const char *s1, const char *s2, int n) {
- int c1, c2;
-
- // bk001129 - moved in 1.17 fix not in id codebase
- if ( s1 == NULL ) {
- if ( s2 == NULL )
- return 0;
- else
- return -1;
- }
- else if ( s2==NULL )
- return 1;
+
+int Q_stricmpn( const char *s1, const char *s2, int n ) {
+ int c1, c2;
+ if ( s1 == NULL ) {
+ if ( s2 == NULL )
+ return 0;
+ else
+ return -1;
+ } else if ( s2 == NULL )
+ return 1;
do {
- c1 = *s1++;
- c2 = *s2++;
+ c1 = *s1; s1++;
+ c2 = *s2; s2++;
- if (!n--) {
- return 0; // strings are equal until end point
+ if ( !n-- ) {
+ return 0; // strings are equal until end point
}
- if (c1 != c2) {
+ if ( c1 != c2 ) {
if (c1 >= 'a' && c1 <= 'z') {
c1 -= ('a' - 'A');
}
@@ -763,12 +857,13 @@ int Q_stricmpn (const char *s1, const char *s2, int n) {
return 0; // strings are equal
}
-int Q_strncmp (const char *s1, const char *s2, int n) {
+
+int Q_strncmp( const char *s1, const char *s2, int n ) {
int c1, c2;
do {
- c1 = *s1++;
- c2 = *s2++;
+ c1 = *s1; s1++;
+ c2 = *s2; s2++;
if (!n--) {
return 0; // strings are equal until end point
@@ -782,12 +877,46 @@ int Q_strncmp (const char *s1, const char *s2, int n) {
return 0; // strings are equal
}
-int Q_stricmp (const char *s1, const char *s2) {
- return (s1 && s2) ? Q_stricmpn (s1, s2, 99999) : -1;
+
+int Q_stricmp( const char *s1, const char *s2 )
+{
+ unsigned char c1, c2;
+
+ if ( s1 == NULL )
+ {
+ if ( s2 == NULL )
+ return 0;
+ else
+ return -1;
+ }
+ else if ( s2 == NULL )
+ return 1;
+
+ do
+ {
+ c1 = *s1; s1++;
+ c2 = *s2; s2++;
+
+ if ( c1 != c2 )
+ {
+ if ( c1 <= 'Z' && c1 >= 'A' )
+ c1 += ('a' - 'A');
+
+ if ( c2 <= 'Z' && c2 >= 'A' )
+ c2 += ('a' - 'A');
+
+ if ( c1 != c2 )
+ return c1 < c2 ? -1 : 1;
+ }
+ }
+ while ( c1 != '\0' );
+
+ return 0;
}
-char *Q_strlwr( char *s1 ) {
+char *Q_strlwr( char *s1 )
+{
char *s;
s = s1;
@@ -798,6 +927,7 @@ char *Q_strlwr( char *s1 ) {
return s1;
}
+
char *Q_strupr( char *s1 ) {
char *s;
@@ -814,7 +944,7 @@ char *Q_strupr( char *s1 ) {
void Q_strcat( char *dest, int size, const char *src ) {
int l1;
- l1 = (int)strlen( dest );
+ l1 = strlen( dest );
if ( l1 >= size ) {
Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
}
@@ -852,12 +982,12 @@ char *Q_CleanStr( char *string ) {
s = string;
d = string;
- while ((c = *s) != 0 ) {
+ while ((c = *s) != '\0' ) {
if ( Q_IsColorString( s ) ) {
s++;
- }
- else if ( c >= 0x20 && c <= 0x7E ) {
- *d++ = c;
+ }
+ else if ( c >= ' ' && c <= '~' ) {
+ *d = c; d++;
}
s++;
}
@@ -867,24 +997,19 @@ char *Q_CleanStr( char *string ) {
}
-void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
- int len;
- va_list argptr;
- char bigbuffer[32000]; // big, but small enough to fit in PPC stack
+int QDECL Com_sprintf( char *dest, int size, const char *fmt, ... ) {
+ va_list argptr;
+ int len;
- va_start (argptr,fmt);
- len = vsprintf (bigbuffer,fmt,argptr);
- va_end (argptr);
- if ( len >= sizeof( bigbuffer ) ) {
- Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
- }
- if (len >= size) {
- Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
-#ifdef _DEBUG
- __debugbreak();
-#endif
+ va_start( argptr, fmt );
+ len = Q_vsprintf( dest, fmt, argptr );
+ va_end( argptr );
+
+ if ( len >= size ) {
+ Com_Error( ERR_FATAL, "Com_sprintf: overflow of %i in %i\n", len, size );
}
- Q_strncpyz (dest, bigbuffer, size );
+
+ return len;
}
@@ -897,18 +1022,19 @@ varargs versions of all text functions.
FIXME: make this buffer size safe someday
============
*/
-char * QDECL va( char *format, ... ) {
+char * QDECL va( const char *format, ... )
+{
va_list argptr;
static char string[2][32000]; // in case va is called by nested functions
static int index = 0;
char *buf;
- buf = string[index & 1];
- index++;
+ buf = string[ index ];
+ index ^= 1;
- va_start (argptr, format);
- vsprintf (buf, format,argptr);
- va_end (argptr);
+ va_start( argptr, format );
+ Q_vsprintf( buf, format, argptr );
+ va_end( argptr );
return buf;
}
@@ -922,58 +1048,86 @@ char * QDECL va( char *format, ... ) {
=====================================================================
*/
+
+static qboolean Q_strkey( const char *str, const char *key, int key_len )
+{
+ int i;
+
+ for ( i = 0; i < key_len; i++ )
+ {
+ if ( locase[ (byte)str[i] ] != locase[ (byte)key[i] ] )
+ {
+ return qfalse;
+ }
+ }
+
+ return qtrue;
+}
+
+
/*
===============
Info_ValueForKey
Searches the string for the given
key and returns the associated value, or an empty string.
-FIXME: overflow check?
===============
*/
char *Info_ValueForKey( const char *s, const char *key ) {
- char pkey[BIG_INFO_KEY];
static char value[2][BIG_INFO_VALUE]; // use two buffers so compares
// work without stomping on each other
static int valueindex = 0;
- char *o;
-
- if ( !s || !key ) {
+ const char *v, *pkey;
+ char *o, *o2;
+ int klen, len;
+
+ if ( !s || !key || !*key )
return "";
- }
- if ( (int)strlen( s ) >= BIG_INFO_STRING ) {
- Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
- }
+ klen = (int)strlen( key );
- valueindex ^= 1;
- if (*s == '\\')
+ if ( *s == '\\' )
s++;
+
while (1)
{
- o = pkey;
- while (*s != '\\')
+ pkey = s;
+ while ( *s != '\\' )
{
- if (!*s)
+ if ( *s == '\0' )
return "";
- *o++ = *s++;
+ ++s;
}
- *o = 0;
- s++;
+ len = (int)(s - pkey);
+ s++; // skip '\\'
- o = value[valueindex];
+ v = s;
+ while ( *s != '\\' && *s !='\0' )
+ s++;
- while (*s != '\\' && *s)
+ if ( len == klen && Q_strkey( pkey, key, klen ) )
{
- *o++ = *s++;
+ o = o2 = value[ valueindex ];
+ valueindex ^= 1;
+ if ( (int)(s - v) >= BIG_INFO_STRING )
+ {
+ Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring value" );
+ }
+ else
+ {
+ while ( v < s )
+ {
+ *o = *v;
+ ++o; ++v;
+ }
+ }
+ *o = '\0';
+ return o2;
}
- *o = 0;
- if (!Q_stricmp (key, pkey) )
- return value[valueindex];
-
- if (!*s)
+ if ( *s == '\0' )
break;
+
s++;
}
@@ -988,37 +1142,34 @@ Info_NextPair
Used to itterate through all the key/value pairs in an info string
===================
*/
-void Info_NextPair( const char **head, char *key, char *value ) {
- char *o;
- const char *s;
-
- s = *head;
+const char *Info_NextPair( const char *s, char *key, char *value ) {
+ char *o;
if ( *s == '\\' ) {
s++;
}
- key[0] = 0;
- value[0] = 0;
+
+ key[0] = '\0';
+ value[0] = '\0';
o = key;
while ( *s != '\\' ) {
- if ( !*s ) {
- *o = 0;
- *head = s;
- return;
+ if ( *s == '\0' ) {
+ *o = '\0';
+ return s;
}
*o++ = *s++;
}
- *o = 0;
+ *o = '\0';
s++;
o = value;
- while ( *s != '\\' && *s ) {
+ while ( *s != '\\' && *s != '\0' ) {
*o++ = *s++;
}
- *o = 0;
+ *o = '\0';
- *head = s;
+ return s;
}
@@ -1027,132 +1178,94 @@ void Info_NextPair( const char **head, char *key, char *value ) {
Info_RemoveKey
===================
*/
-void Info_RemoveKey( char *s, const char *key ) {
+static int Info_RemoveKey( char *s, const char *key ) {
char *start;
- char pkey[MAX_INFO_KEY];
- char value[MAX_INFO_VALUE];
- char *o;
-
- if ( (int)strlen( s ) >= MAX_INFO_STRING ) {
- Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
- }
+ char *pkey;
+ int key_len, len;
- if (strchr (key, '\\')) {
- return;
- }
+ key_len = (int) strlen( key );
- while (1)
+ while ( 1 )
{
start = s;
- if (*s == '\\')
+ if ( *s == '\\' )
s++;
- o = pkey;
- while (*s != '\\')
+ pkey = s;
+ while ( *s != '\\' )
{
- if (!*s)
- return;
- *o++ = *s++;
+ if ( *s == '\0' )
+ return 0;
+ ++s;
}
- *o = 0;
- s++;
- o = value;
- while (*s != '\\' && *s)
- {
- if (!*s)
- return;
- *o++ = *s++;
- }
- *o = 0;
+ len = (int)(s - pkey);
+ ++s; // skip '\\'
- if (!strcmp (key, pkey) )
- {
- strcpy (start, s); // remove this part
- return;
- }
-
- if (!*s)
- return;
- }
-
-}
+ while ( *s != '\\' && *s != '\0' )
+ ++s;
-/*
-===================
-Info_RemoveKey_Big
-===================
-*/
-void Info_RemoveKey_Big( char *s, const char *key ) {
- char *start;
- char pkey[BIG_INFO_KEY];
- char value[BIG_INFO_VALUE];
- char *o;
-
- if ( (int)strlen( s ) >= BIG_INFO_STRING ) {
- Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" );
- }
-
- if (strchr (key, '\\')) {
- return;
- }
-
- while (1)
- {
- start = s;
- if (*s == '\\')
- s++;
- o = pkey;
- while (*s != '\\')
- {
- if (!*s)
- return;
- *o++ = *s++;
- }
- *o = 0;
- s++;
-
- o = value;
- while (*s != '\\' && *s)
+ if ( len == key_len && Q_strkey( pkey, key, key_len ) )
{
- if (!*s)
- return;
- *o++ = *s++;
+ memmove( start, s, strlen( s ) + 1 ); // remove this part
+ return (int)(s - start);
}
- *o = 0;
- if (!strcmp (key, pkey) )
- {
- strcpy (start, s); // remove this part
- return;
- }
-
- if (!*s)
- return;
+ if ( *s == '\0' )
+ break;
}
+ return 0;
}
-
-
/*
==================
-Info_Validate
+Info_ValidateKeyValue
Some characters are illegal in info strings because they
can mess up the server's parsing
==================
*/
-qboolean Info_Validate( const char *s ) {
- if ( strchr( s, '\"' ) ) {
+qboolean Info_Validate( const char *s )
+{
+loop:
+ switch ( *s )
+ {
+ case '\0':
+ return qtrue;
+ case '\"':
+ case ';':
return qfalse;
+ default:
+ ++s;
+ goto loop;
}
- if ( strchr( s, ';' ) ) {
+}
+
+
+/*
+==================
+Info_ValidateKeyValue
+==================
+*/
+qboolean Info_ValidateKeyValue( const char *s )
+{
+loop:
+ switch ( *s )
+ {
+ case '\0':
+ return qtrue;
+ case '\\':
+ case '\"':
+ case ';':
return qfalse;
+ default:
+ ++s;
+ goto loop;
}
- return qtrue;
}
+
/*
==================
Info_SetValueForKey
@@ -1160,47 +1273,42 @@ Info_SetValueForKey
Changes or adds a key/value pair
==================
*/
-void Info_SetValueForKey( char *s, const char *key, const char *value ) {
- char newi[MAX_INFO_STRING];
+qboolean Info_SetValueForKey( char *s, const char *key, const char *value ) {
+ char newi[MAX_INFO_STRING+2];
+ int len1, len2;
- if ( (int)strlen( s ) >= MAX_INFO_STRING ) {
+ len1 = (int)strlen( s );
+ if ( len1 >= MAX_INFO_STRING ) {
Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
}
- if (strchr (key, '\\') || strchr (value, '\\'))
- {
- Com_Printf ("Can't use keys or values with a \\\n");
- return;
- }
-
- if (strchr (key, ';') || strchr (value, ';'))
- {
- Com_Printf ("Can't use keys or values with a semicolon\n");
- return;
+ if ( !Info_ValidateKeyValue( key ) || *key == '\0' ) {
+ Com_Printf( S_COLOR_YELLOW "Invalid key name: '%s'\n", key );
+ return qfalse;
}
- if (strchr (key, '\"') || strchr (value, '\"'))
- {
- Com_Printf ("Can't use keys or values with a \"\n");
- return;
+ if ( !Info_ValidateKeyValue( value ) ) {
+ Com_Printf( S_COLOR_YELLOW "Invalid value name: '%s'\n", value );
+ return qfalse;
}
- Info_RemoveKey (s, key);
- if (!value || !strlen(value))
- return;
-
- Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
+ len1 -= Info_RemoveKey( s, key );
+ if ( !value || !*value )
+ return qtrue;
- if (strlen(newi) + (int)strlen(s) > MAX_INFO_STRING)
+ len2 = Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );
+
+ if ( len1 + len2 >= MAX_INFO_STRING )
{
- Com_Printf ("Info string length exceeded\n");
- return;
+ Com_Printf( S_COLOR_YELLOW "Info string length exceeded\n" );
+ return qfalse;
}
- strcat (newi, s);
- strcpy (s, newi);
+ strcpy( s + len1, newi );
+ return qtrue;
}
+
/*
==================
Info_SetValueForKey_Big
@@ -1208,49 +1316,37 @@ Info_SetValueForKey_Big
Changes or adds a key/value pair
==================
*/
-void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
- char newi[BIG_INFO_STRING];
+qboolean Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
+ char newi[BIG_INFO_STRING+2];
+ int len1, len2;
- if ( (int)strlen( s ) >= BIG_INFO_STRING ) {
+ len1 = (int)strlen( s );
+ if ( len1 >= BIG_INFO_STRING ) {
Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
}
- if (strchr (key, '\\') || strchr (value, '\\'))
- {
- Com_Printf ("Can't use keys or values with a \\\n");
- return;
- }
-
- if (strchr (key, ';') || strchr (value, ';'))
- {
- Com_Printf ("Can't use keys or values with a semicolon\n");
- return;
+ if ( !Info_ValidateKeyValue( key ) || *key == '\0' ) {
+ Com_Printf( S_COLOR_YELLOW "Invalid key name: '%s'\n", key );
+ return qfalse;
}
- if (strchr (key, '\"') || strchr (value, '\"'))
- {
- Com_Printf ("Can't use keys or values with a \"\n");
- return;
+ if ( !Info_ValidateKeyValue( value ) ) {
+ Com_Printf( S_COLOR_YELLOW "Invalid value name: '%s'\n", value );
+ return qfalse;
}
- Info_RemoveKey_Big (s, key);
- if (!value || !strlen(value))
- return;
+ len1 -= Info_RemoveKey( s, key );
+ if ( !value || !*value )
+ return qtrue;
- Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
+ len2 = Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );
- if (strlen(newi) + (int)strlen(s) > BIG_INFO_STRING)
+ if ( len1 + len2 >= BIG_INFO_STRING )
{
- Com_Printf ("BIG Info string length exceeded\n");
- return;
+ Com_Printf( S_COLOR_YELLOW "BIG Info string length exceeded\n" );
+ return qfalse;
}
- strcat (s, newi);
+ strcpy( s + len1, newi );
+ return qtrue;
}
-
-
-
-
-//====================================================================
-
-
diff --git a/source/game/q_shared.h b/source/game/q_shared.h
index 2b145f6..f08762f 100644
--- a/source/game/q_shared.h
+++ b/source/game/q_shared.h
@@ -31,33 +31,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MAX_TEAMNAME 32
-#ifdef _WIN32
+#ifdef _MSC_VER
-#pragma warning(disable : 4018) // signed/unsigned mismatch
-#pragma warning(disable : 4032)
-#pragma warning(disable : 4051)
-#pragma warning(disable : 4057) // slightly different base types
+#pragma warning(disable : 4244)
#pragma warning(disable : 4100) // unreferenced formal parameter
-#pragma warning(disable : 4115)
-#pragma warning(disable : 4125) // decimal digit terminates octal escape sequence
#pragma warning(disable : 4127) // conditional expression is constant
-#pragma warning(disable : 4136)
-#pragma warning(disable : 4152) // nonstandard extension, function/data pointer conversion in expression
-//#pragma warning(disable : 4201)
-//#pragma warning(disable : 4214)
-#pragma warning(disable : 4244)
-#pragma warning(disable : 4142) // benign redefinition
-//#pragma warning(disable : 4305) // truncation from const double to float
-//#pragma warning(disable : 4310) // cast truncates constant value
-//#pragma warning(disable: 4505) // unreferenced local function has been removed
-#pragma warning(disable : 4514)
#pragma warning(disable : 4702) // unreachable code
-#pragma warning(disable : 4711) // selected for automatic inline expansion
-#pragma warning(disable : 4220) // varargs matches remaining parameters
-
-//
-#pragma warning(disable : 4996) // the function or variable may be unsafe
-//
+#pragma warning(disable : 4267) // conversion from 'size_t' to 'int'
#endif
@@ -80,12 +60,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifdef Q3_VM
#include "bg_lib.h"
+#define DLLEXPORT
-typedef int intptr_t;
-
-#elif defined __linux__ || __FreeBSD__ // BFP - Linux and FreeBSD support
-
-typedef int intptr_t;
+#else
#include
#include
@@ -97,19 +74,14 @@ typedef int intptr_t;
#include
#include
-#else // BFP - MinGW support
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#ifndef _WIN32
+#include
+#define DLLEXPORT __attribute__((visibility ("default")))
+#else
+#define DLLEXPORT __declspec(dllexport)
+#endif // _WIN32
-#endif
+#endif // !Q3_VM
#ifdef _WIN32
@@ -117,34 +89,10 @@ typedef int intptr_t;
#endif
-
-// this is the define for determining if we have an asm version of a C function
-#if (defined _M_IX86 || defined __i386__) && !defined __sun__ && !defined __LCC__
-#define id386 1
-#else
-#define id386 0
-#endif
-
-#if (defined(powerc) || defined(powerpc) || defined(ppc) || defined(__ppc) || defined(__ppc__)) && !defined(C_ONLY)
-#define idppc 1
-#if defined(__VEC__)
-#define idppc_altivec 1
-#else
-#define idppc_altivec 0
-#endif
-#else
-#define idppc 0
-#define idppc_altivec 0
-#endif
-
// for windows fastcall option
#define QDECL
-short ShortSwap (short l);
-int LongSwap (int l);
-float FloatSwap (const float *f);
-
//======================= WIN32 DEFINES =================================
#ifdef WIN32
@@ -154,98 +102,12 @@ float FloatSwap (const float *f);
#undef QDECL
#define QDECL __cdecl
-#define CPUSTRING "generic"
-
#define ID_INLINE __inline
-static ID_INLINE short BigShort( short l) { return ShortSwap(l); }
-#define LittleShort
-static ID_INLINE int BigLong(int l) { LongSwap(l); }
-#define LittleLong
-static ID_INLINE float BigFloat(const float *l) { FloatSwap(l); }
-#define LittleFloat
-
#define PATH_SEP '\\'
#endif
-//======================= MAC OS X DEFINES =====================
-
-#if defined(MACOS_X)
-
-#define MAC_STATIC
-#define __cdecl
-#define __declspec(x)
-#define stricmp strcasecmp
-#define ID_INLINE inline
-
-#ifdef __ppc__
-#define CPUSTRING "MacOSX-ppc"
-#elif defined __i386__
-#define CPUSTRING "MacOSX-i386"
-#else
-#define CPUSTRING "MacOSX-other"
-#endif
-
-#define PATH_SEP '/'
-
-#define __rlwimi(out, in, shift, maskBegin, maskEnd) asm("rlwimi %0,%1,%2,%3,%4" : "=r" (out) : "r" (in), "i" (shift), "i" (maskBegin), "i" (maskEnd))
-#define __dcbt(addr, offset) asm("dcbt %0,%1" : : "b" (addr), "r" (offset))
-
-static inline unsigned int __lwbrx(register void *addr, register int offset) {
- register unsigned int word;
-
- asm("lwbrx %0,%2,%1" : "=r" (word) : "r" (addr), "b" (offset));
- return word;
-}
-
-static inline unsigned short __lhbrx(register void *addr, register int offset) {
- register unsigned short halfword;
-
- asm("lhbrx %0,%2,%1" : "=r" (halfword) : "r" (addr), "b" (offset));
- return halfword;
-}
-
-static inline float __fctiw(register float f) {
- register float fi;
-
- asm("fctiw %0,%1" : "=f" (fi) : "f" (f));
-
- return fi;
-}
-
-#define BigShort
-static inline short LittleShort(short l) { return ShortSwap(l); }
-#define BigLong
-static inline int LittleLong (int l) { return LongSwap(l); }
-#define BigFloat
-static inline float LittleFloat (const float l) { return FloatSwap(&l); }
-
-#endif
-
-//======================= MAC DEFINES =================================
-
-#ifdef __MACOS__
-
-#include
-#define MAC_STATIC
-#define ID_INLINE inline
-
-#define CPUSTRING "MacOS-PPC"
-
-#define PATH_SEP ':'
-
-void Sys_PumpEvents( void );
-
-#define BigShort
-static inline short LittleShort(short l) { return ShortSwap(l); }
-#define BigLong
-static inline int LittleLong (int l) { return LongSwap(l); }
-#define BigFloat
-static inline float LittleFloat (const float l) { return FloatSwap(&l); }
-
-#endif
-
//======================= LINUX DEFINES =================================
// the mac compiler can't handle >32k of locals, so we
@@ -255,43 +117,10 @@ static inline float LittleFloat (const float l) { return FloatSwap(&l); }
// bk001205 - from Makefile
#define stricmp strcasecmp
-#define MAC_STATIC // bk: FIXME
#define ID_INLINE inline
-#ifdef __i386__
-#define CPUSTRING "linux-i386"
-#elif defined __axp__
-#define CPUSTRING "linux-alpha"
-#else
-#define CPUSTRING "linux-other"
-#endif
-
#define PATH_SEP '/'
-// bk001205 - try
-#ifdef Q3_STATIC
-#define GAME_HARD_LINKED
-#define CGAME_HARD_LINKED
-#define UI_HARD_LINKED
-#define BOTLIB_HARD_LINKED
-#endif
-
-#if !idppc
-inline static short BigShort( short l) { return ShortSwap(l); }
-#define LittleShort
-inline static int BigLong(int l) { return LongSwap(l); }
-#define LittleLong
-inline static float BigFloat(const float *l) { return FloatSwap(l); }
-#define LittleFloat
-#else
-#define BigShort
-inline static short LittleShort(short l) { return ShortSwap(l); }
-#define BigLong
-inline static int LittleLong (int l) { return LongSwap(l); }
-#define BigFloat
-inline static float LittleFloat (const float *l) { return FloatSwap(l); }
-#endif
-
#endif
//======================= FreeBSD DEFINES =====================
@@ -302,34 +131,17 @@ inline static float LittleFloat (const float *l) { return FloatSwap(l); }
#define MAC_STATIC
#define ID_INLINE inline
-#ifdef __i386__
-#define CPUSTRING "freebsd-i386"
-#elif defined __axp__
-#define CPUSTRING "freebsd-alpha"
-#else
-#define CPUSTRING "freebsd-other"
-#endif
-
#define PATH_SEP '/'
-// bk010116 - omitted Q3STATIC (see Linux above), broken target
-
-#if !idppc
-static short BigShort( short l) { return ShortSwap(l); }
-#define LittleShort
-static int BigLong(int l) { LongSwap(l); }
-#define LittleLong
-static float BigFloat(const float *l) { FloatSwap(l); }
-#define LittleFloat
-#else
-#define BigShort
-static short LittleShort(short l) { return ShortSwap(l); }
-#define BigLong
-static int LittleLong (int l) { return LongSwap(l); }
-#define BigFloat
-static float LittleFloat (const float *l) { return FloatSwap(l); }
#endif
+//======================= C11 OR NEWER DEFINES =====================
+
+// BFP - C11 or newer support
+#ifndef ID_INLINE
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+ #define ID_INLINE inline
+#endif
#endif
//=============================================================
@@ -348,10 +160,17 @@ typedef int clipHandle_t;
#define NULL ((void *)0)
#endif
+#ifndef FS_INVALID_HANDLE
+#define FS_INVALID_HANDLE 0
+#endif
+
#define MAX_QINT 0x7fffffff
#define MIN_QINT (-MAX_QINT-1)
+#define MAX_UINT ((unsigned)(~0))
+
#define ARRAY_LEN(x) (sizeof(x) / sizeof(*(x)))
+#define STRARRAY_LEN(x) (ARRAY_LEN(x) - 1)
// angle indexes
#define PITCH 0 // up / down
@@ -461,21 +280,8 @@ void *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, in
void *Hunk_Alloc( int size, ha_pref preference );
#endif
-#ifdef __linux__
-// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
-// custom Snd_Memset implementation for glibc memset bug workaround
-void Snd_Memset (void* dest, const int val, const size_t count);
-#else
-#define Snd_Memset Com_Memset
-#endif
-
-#if !( defined __VECTORC )
-void Com_Memset (void* dest, const int val, const size_t count);
-void Com_Memcpy (void* dest, const void* src, const size_t count);
-#else
#define Com_Memset memset
#define Com_Memcpy memcpy
-#endif
#define CIN_system 1
#define CIN_loop 2
@@ -582,34 +388,8 @@ extern vec3_t axisDefault[3];
#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
-#if idppc
-
-static inline float Q_rsqrt( float number ) {
- float x = 0.5f * number;
- float y;
-#ifdef __GNUC__
- asm("frsqrte %0,%1" : "=f" (y) : "f" (number));
-#else
- y = __frsqrte( number );
-#endif
- return y * (1.5f - (x * y * y));
- }
-
-#ifdef __GNUC__
-static inline float Q_fabs(float x) {
- float abs_x;
-
- asm("fabs %0,%1" : "=f" (abs_x) : "f" (x));
- return abs_x;
-}
-#else
-#define Q_fabs __fabsf
-#endif
-
-#else
float Q_fabs( float f );
float Q_rsqrt( float f ); // reciprocal square root
-#endif
#define SQRTFAST( x ) ( (x) * Q_rsqrt( x ) )
@@ -640,7 +420,7 @@ void ByteToDir( int b, vec3_t dir );
#endif
-#ifdef __LCC__
+#ifdef Q3_VM
#ifdef VectorCopy
#undef VectorCopy
// this is a little hack to get more efficient copies in our interpreter
@@ -648,7 +428,6 @@ typedef struct {
float v[3];
} vec3struct_t;
#define VectorCopy(a,b) (*(vec3struct_t *)b=*(vec3struct_t *)a)
-#define ID_INLINE static
#endif
#endif
@@ -675,7 +454,7 @@ float RadiusFromBounds( const vec3_t mins, const vec3_t maxs );
void ClearBounds( vec3_t mins, vec3_t maxs );
void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs );
-#ifndef __LCC__
+#if !defined( Q3_VM )
static ID_INLINE int VectorCompare( const vec3_t v1, const vec3_t v2 ) {
if (v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2]) {
return 0;
@@ -730,6 +509,19 @@ static ID_INLINE void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cro
cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
+// BFP - Vector2Set sets the 2 values of a vec2_t variable in a single line
+static ID_INLINE void Vector2Set( vec2_t v, float x, float y ) {
+ v[0] = x;
+ v[1] = y;
+}
+// BFP - Byte4Set sets the 4 values of a byte[4] variable in a single line
+static ID_INLINE void Byte4Set( byte v[4], float w, float x, float y, float z ) {
+ v[0] = w;
+ v[1] = x;
+ v[2] = y;
+ v[3] = z;
+}
+
#else
int VectorCompare( const vec3_t v1, const vec3_t v2 );
@@ -747,6 +539,11 @@ void VectorInverse( vec3_t v );
void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross );
+// BFP - Vector2Set sets the 2 values of a vec2_t variable in a single line
+void Vector2Set( vec2_t v, float x, float y );
+// BFP - Byte4Set sets the 4 values of a byte[4] variable in a single line
+void Byte4Set( byte v[4], float w, float x, float y, float z );
+
#endif
vec_t VectorNormalize (vec3_t v); // returns vector length
@@ -801,7 +598,7 @@ void PerpendicularVector( vec3_t dst, const vec3_t src );
float Com_Clamp( float min, float max, float value );
char *COM_SkipPath( char *pathname );
-void COM_StripExtension( const char *in, char *out );
+void COM_StripExtension( const char *in, char *out, int destsize );
void COM_DefaultExtension( char *path, int maxSize, const char *extension );
void COM_BeginParseSession( const char *name );
@@ -844,8 +641,10 @@ void Parse1DMatrix (char **buf_p, int x, float *m);
void Parse2DMatrix (char **buf_p, int y, int x, float *m);
void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m);
-void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...);
+int QDECL Com_sprintf (char *dest, int size, const char *fmt, ...);
+// BFP - Buffer size for bfp.cfg and bfp_server.cfg files
+#define BFP_CFG_BUFFER_SIZE 16384
// mode parm for FS_FOpenFile
typedef enum {
@@ -863,6 +662,8 @@ typedef enum {
//=============================================
+extern const byte locase[ 256 ];
+
int Q_isprint( int c );
int Q_islower( int c );
int Q_isupper( int c );
@@ -887,34 +688,13 @@ char *Q_CleanStr( char *string );
//=============================================
-// 64-bit integers for global rankings interface
-// implemented as a struct for qvm compatibility
-typedef struct
-{
- byte b0;
- byte b1;
- byte b2;
- byte b3;
- byte b4;
- byte b5;
- byte b6;
- byte b7;
-} qint64;
+typedef intptr_t (*syscall_t)( intptr_t *parms );
+typedef intptr_t (QDECL *dllSyscall_t)( intptr_t callNum, ... );
+typedef void (QDECL *dllEntry_t)( dllSyscall_t syscallptr );
//=============================================
-/*
-short BigShort(short l);
-short LittleShort(short l);
-int BigLong (int l);
-int LittleLong (int l);
-qint64 BigLong64 (qint64 l);
-qint64 LittleLong64 (qint64 l);
-float BigFloat (const float *l);
-float LittleFloat (const float *l);
-
-void Swap_Init (void);
-*/
-char * QDECL va(char *format, ...);
+
+char * QDECL va( const char *format, ... );
//=============================================
@@ -922,12 +702,11 @@ char * QDECL va(char *format, ...);
// key / value info strings
//
char *Info_ValueForKey( const char *s, const char *key );
-void Info_RemoveKey( char *s, const char *key );
-void Info_RemoveKey_big( char *s, const char *key );
-void Info_SetValueForKey( char *s, const char *key, const char *value );
-void Info_SetValueForKey_Big( char *s, const char *key, const char *value );
+qboolean Info_SetValueForKey( char *s, const char *key, const char *value );
+qboolean Info_SetValueForKey_Big( char *s, const char *key, const char *value );
qboolean Info_Validate( const char *s );
-void Info_NextPair( const char **s, char *key, char *value );
+qboolean Info_ValidateKeyValue( const char *s );
+const char *Info_NextPair( const char *s, char *key, char *value );
// this is only here so the functions in q_shared.c and bg_*.c can link
void QDECL Com_Error( int level, const char *error, ... );
@@ -964,21 +743,6 @@ default values.
#define CVAR_CHEAT 512 // can not be changed if cheats are disabled
#define CVAR_NORESTART 1024 // do not clear when a cvar_restart is issued
-// nothing outside the Cvar_*() functions should modify these fields!
-typedef struct cvar_s {
- char *name;
- char *string;
- char *resetString; // cvar_restart will reset to this value
- char *latchedString; // for CVAR_LATCH vars
- int flags;
- qboolean modified; // set each time the cvar is changed
- int modificationCount; // incremented each time the cvar is changed
- float value; // atof( string )
- int integer; // atoi( string )
- struct cvar_s *next;
- struct cvar_s *hashNext;
-} cvar_t;
-
#define MAX_CVAR_VALUE_STRING 256
typedef int cvarHandle_t;
@@ -1065,7 +829,7 @@ typedef struct {
// in order from highest priority to lowest
// if none of the catchers are active, bound key strings will be executed
#define KEYCATCH_CONSOLE 0x0001
-#define KEYCATCH_UI 0x0002
+#define KEYCATCH_UI 0x0002
#define KEYCATCH_MESSAGE 0x0004
#define KEYCATCH_CGAME 0x0008
@@ -1117,6 +881,11 @@ typedef enum {
#define ENTITYNUM_MAX_NORMAL (MAX_GENTITIES-2)
+// BFP - Monster gamemode, monster model name macro
+#define MONSTER_NAME "oozaru"
+// BFP - Macro to enable/disable kicking the player who uses an illegal player model, if disabled, the player will be forced to spectate
+#define KICK_ILLEGAL_PLAYER_MODEL 1
+
#define MAX_MODELS 256 // these are sent over the net as 8 bits
#define MAX_SOUNDS 256 // so they cannot be blindly increased
@@ -1182,7 +951,7 @@ typedef struct playerState_s {
int torsoTimer; // don't change low priority animations until this runs out
int torsoAnim; // mask off ANIM_TOGGLEBIT
- int movementDir; // a number 0 to 7 that represents the reletive angle
+ int movementDir; // a number 0 to 7 that represents the relative angle
// of movement to the view angle (axial and diagonals)
// when at rest, the value will remain unchanged
// used to twist the legs during strafing
@@ -1313,7 +1082,7 @@ typedef struct entityState_s {
int otherEntityNum; // shotgun sources, etc
int otherEntityNum2;
- int groundEntityNum; // -1 = in air
+ int groundEntityNum;// ENTITYNUM_NONE = in air
int constantLight; // r + (g<<8) + (b<<16) + (intensity<<24)
int loopSound; // constantly loop this sound
@@ -1321,6 +1090,8 @@ typedef struct entityState_s {
int modelindex;
int modelindex2;
int clientNum; // 0 to (MAX_CLIENTS - 1), for players and corpses
+
+ // BFP - This "frame" is used to send the powerlevel info to cgame
int frame;
int solid; // for client side prediction, trap_linkentity sets this properly
@@ -1400,11 +1171,15 @@ typedef struct qtime_s {
// server browser sources
// TTimo: AS_MPLAYER is no longer used
-#define AS_LOCAL 0
+#define AS_LOCAL 0
#define AS_MPLAYER 1
-#define AS_GLOBAL 2
+#define AS_GLOBAL 2
#define AS_FAVORITES 3
+#define MAX_GLOBAL_SERVERS 4096
+#define MAX_OTHER_SERVERS 128
+#define MAX_PINGREQUESTS 32
+#define MAX_SERVERSTATUSREQUESTS 16
// cinematic states
typedef enum {
@@ -1425,13 +1200,6 @@ typedef enum _flag_status {
FLAG_DROPPED
} flagStatus_t;
-
-
-#define MAX_GLOBAL_SERVERS 4096
-#define MAX_OTHER_SERVERS 128
-#define MAX_PINGREQUESTS 32
-#define MAX_SERVERSTATUSREQUESTS 16
-
#define SAY_ALL 0
#define SAY_TEAM 1
#define SAY_TELL 2
@@ -1443,4 +1211,30 @@ typedef enum _flag_status {
#ifndef LERP
#define LERP( a, b, w ) ( ( a ) * ( 1.0f - ( w ) ) + ( b ) * ( w ) )
#endif
+
+// custom functions
+int BG_sprintf( char *buf, const char *format, ... );
+int Q_vsprintf( char *buffer, const char *fmt, va_list argptr );
+
+char *Q_stristr( const char * str1, const char * str2 );
+
+char *strtok( char *strToken, const char *strDelimit );
+char *EncodedString( const char *str );
+char *DecodedString( const char *str );
+
+void BG_CleanName( const char *in, char *out, int outSize, const char *blankString );
+char *BG_StripColor( char *string );
+
+void Q_strcpy( char *dst, const char *src );
+char *Q_stradd( char *dst, const char *src );
+int Q_sscanf( const char *buffer, const char *fmt, ... );
+
+int replace_s( char * str1, char * str2, char * src, int max_len );
+qboolean replace1( const char match, const char replace, char *str );
+
+qboolean BigEndian( void );
+
+
+float mp3dec_pow( float x, float y ); // BFP - Unpack music, memory management for ui_mp3decoder
+
#endif // __Q_SHARED_H
diff --git a/source/game/syn.h b/source/game/syn.h
index 5ffd9d4..b8eecea 100644
--- a/source/game/syn.h
+++ b/source/game/syn.h
@@ -26,9 +26,5 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CONTEXT_CTFREDTEAM 4
#define CONTEXT_CTFBLUETEAM 8
#define CONTEXT_REPLY 16
-#define CONTEXT_OBELISKREDTEAM 32
-#define CONTEXT_OBELISKBLUETEAM 64
-#define CONTEXT_HARVESTERREDTEAM 128
-#define CONTEXT_HARVESTERBLUETEAM 256
#define CONTEXT_NAMES 1024
diff --git a/source/q3_ui/ui.def b/source/q3_ui/ui.def
new file mode 100644
index 0000000..2ee748e
--- /dev/null
+++ b/source/q3_ui/ui.def
@@ -0,0 +1,3 @@
+EXPORTS
+ vmMain
+ dllEntry
diff --git a/source/q3_ui/ui_atoms.c b/source/q3_ui/ui_atoms.c
index 6fbba58..1193d6e 100644
--- a/source/q3_ui/ui_atoms.c
+++ b/source/q3_ui/ui_atoms.c
@@ -38,7 +38,7 @@ void QDECL Com_Error( int level, const char *error, ... ) {
char text[1024];
va_start (argptr, error);
- vsprintf (text, error, argptr);
+ Q_vsprintf (text, error, argptr);
va_end (argptr);
trap_Error( va("%s", text) );
@@ -49,7 +49,7 @@ void QDECL Com_Printf( const char *msg, ... ) {
char text[1024];
va_start (argptr, msg);
- vsprintf (text, msg, argptr);
+ Q_vsprintf (text, msg, argptr);
va_end (argptr);
trap_Print( va("%s", text) );
@@ -141,9 +141,10 @@ void UI_PopMenu (void)
trap_S_StartLocalSound( menu_out_sound, CHAN_LOCAL_SOUND );
uis.menusp--;
-
- if (uis.menusp < 0)
+ if (uis.menusp < 0) {
trap_Error ("UI_PopMenu: menu stack underflow");
+ return;
+ }
if (uis.menusp) {
uis.activemenu = uis.stack[uis.menusp-1];
@@ -358,11 +359,11 @@ static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color )
// draw the colored text
trap_R_SetColor( color );
- ax = x * uis.scale + uis.bias;
- ay = y * uis.scale;
+ ax = x * uis.scale + uis.biasX;
+ ay = y * uis.scale + uis.biasY;
s = str;
- while ( *s )
+ while ( s && *s )
{
ch = *s & 127;
if ( ch == ' ' ) {
@@ -394,7 +395,7 @@ void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color
// find the width of the drawn text
s = str;
width = 0;
- while ( *s ) {
+ while ( s && *s ) {
ch = *s;
if ( ch == ' ' ) {
width += PROPB_SPACE_WIDTH;
@@ -438,7 +439,7 @@ int UI_ProportionalStringWidth( const char* str ) {
s = str;
width = 0;
- while ( *s ) {
+ while ( s && *s ) {
ch = *s & 127;
charWidth = propMap[ch][2];
if ( charWidth != -1 ) {
@@ -468,11 +469,11 @@ static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t co
// draw the colored text
trap_R_SetColor( color );
- ax = x * uis.scale + uis.bias;
- ay = y * uis.scale;
+ ax = x * uis.scale + uis.biasX;
+ ay = y * uis.scale + uis.biasY;
s = str;
- while ( *s )
+ while ( s && *s )
{
ch = *s & 127;
if ( ch == ' ' ) {
@@ -562,7 +563,7 @@ void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t
drawcolor[0] = color[0];
drawcolor[1] = color[1];
drawcolor[2] = color[2];
- drawcolor[3] = 0.5 + 0.5 * sin( uis.realtime / PULSE_DIVISOR );
+ drawcolor[3] = 0.5 + 0.5 * sin( ( uis.realtime % TMOD_075 ) / PULSE_DIVISOR );
UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, uis.charsetPropGlow );
return;
}
@@ -659,13 +660,13 @@ static void UI_DrawString2( int x, int y, const char* str, vec4_t color, int cha
// draw the colored text
trap_R_SetColor( color );
- ax = x * uis.scale + uis.bias;
- ay = y * uis.scale;
+ ax = x * uis.scale + uis.biasX;
+ ay = y * uis.scale + uis.biasY;
aw = charw * uis.scale;
ah = charh * uis.scale;
s = str;
- while ( *s )
+ while ( s && *s )
{
if ( Q_IsColorString( s ) )
{
@@ -694,6 +695,7 @@ static void UI_DrawString2( int x, int y, const char* str, vec4_t color, int cha
trap_R_SetColor( NULL );
}
+
/*
=================
UI_DrawString
@@ -748,13 +750,13 @@ void UI_DrawString( int x, int y, const char* str, int style, vec4_t color )
{
case UI_CENTER:
// center justify at x
- len = (int)strlen(str);
+ len = strlen(str);
x = x - len*charw/2;
break;
case UI_RIGHT:
// right justify at x
- len = (int)strlen(str);
+ len = strlen(str);
x = x - len*charw;
break;
@@ -773,6 +775,7 @@ void UI_DrawString( int x, int y, const char* str, int style, vec4_t color )
UI_DrawString2(x,y,str,drawcolor,charw,charh);
}
+
/*
=================
UI_DrawChar
@@ -788,6 +791,7 @@ void UI_DrawChar( int x, int y, int ch, int style, vec4_t color )
UI_DrawString( x, y, buff, style, color );
}
+
qboolean UI_IsFullscreen( void ) {
if ( uis.activemenu && ( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
return uis.activemenu->fullscreen;
@@ -796,6 +800,7 @@ qboolean UI_IsFullscreen( void ) {
return qfalse;
}
+
static void NeedCDAction( qboolean result ) {
if ( !result ) {
trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
@@ -808,6 +813,7 @@ static void NeedCDKeyAction( qboolean result ) {
}
}
+
void UI_SetActiveMenu( uiMenuCommand_t menu ) {
// this should be the ONLY way the menu system is brought up
// enusure minumum menu data is cached
@@ -919,7 +925,7 @@ void UI_MouseEvent( int dx, int dy )
if (uis.activemenu->cursor != i)
{
Menu_SetCursor( uis.activemenu, i );
- ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor_prev]))->flags &= ~QMF_HASMOUSEFOCUS;
+ ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor_prev]))->flags &= (unsigned int)~QMF_HASMOUSEFOCUS;
if ( !(((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags & QMF_SILENT ) ) {
trap_S_StartLocalSound( menu_move_sound, CHAN_LOCAL_SOUND );
@@ -932,7 +938,7 @@ void UI_MouseEvent( int dx, int dy )
if (uis.activemenu->nitems > 0) {
// out of any region
- ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags &= ~QMF_HASMOUSEFOCUS;
+ ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags &= (unsigned int)~QMF_HASMOUSEFOCUS;
}
}
@@ -1060,12 +1066,173 @@ qboolean UI_ConsoleCommand( int realTime ) {
return qfalse;
}
+
+/*
+=================
+UI_ConvertHexKeyname
+
+Converts hexadecimal keynames to character representations
+=================
+*/
+static void UI_ConvertHexKeyname( char keyname[32] ) { // BFP - Converts any hexadecimal key into a symbol character, e.g. 0x7e = ~
+ int i;
+ typedef struct {
+ const char *hex, *replacement;
+ } keynameMap_t;
+
+ static const keynameMap_t KEYNAMEMAP[] = {
+ { "0x00", "NULL" }, { "0x01", "SOH" }, { "0x02", "STX" }, { "0x03", "ETX" },
+ { "0x04", "EOT" }, { "0x05", "ENQ" }, { "0x06", "ACK" }, { "0x07", "BEL" },
+ { "0x08", "BACKSPACE" }, { "0x0a", "LF" }, { "0x0b", "VT" }, { "0x0c", "FF" },
+ { "0x0e", "SO" }, { "0x0f", "SI" }, { "0x10", "DLE" }, { "0x11", "DC1" },
+ { "0x12", "DC2" }, { "0x13", "DC3" }, { "0x14", "DC4" }, { "0x15", "NAK" },
+ { "0x16", "SYN" }, { "0x17", "ETB" }, { "0x18", "CAN" }, { "0x19", "EM" },
+ { "0x1a", "SUB" }, { "0x1c", "FS" }, { "0x1d", "GS" }, { "0x1e", "RS" },
+ { "0x20", "SPACE" }, { "0x21", "!" }, { "0x22", "\"" }, { "0x23", "#" },
+ { "0x24", "$" }, { "0x25", "%" }, { "0x26", "&" }, { "0x27", "'" },
+ { "0x28", "(" }, { "0x29", ")" }, { "0x2a", "*" }, { "0x2b", "+" },
+ { "0x2c", "," }, { "0x2d", "-" }, { "0x2e", "." }, { "0x2f", "/" },
+ { "0x3a", ":" }, { "0x3b", ";" }, { "0x3c", "<" }, { "0x3d", "=" },
+ { "0x3e", ">" }, { "0x3f", "?" }, { "0x40", "@" }, { "0x5b", "[" },
+ { "0x5c", "\\" }, { "0x5d", "]" }, { "0x5e", "^" }, { "0x5f", "_" },
+ { "0x60", "`" }, { "0x7b", "{" }, { "0x7c", "|" }, { "0x7d", "}" },
+ { "0x7e", "~" }, { "0xb0", "°" }
+ };
+
+ for ( i = 0; i < ARRAY_LEN( KEYNAMEMAP ); ++i ) {
+ if ( !Q_stricmp( keyname, KEYNAMEMAP[i].hex ) ) {
+ Q_strncpyz( keyname, KEYNAMEMAP[i].replacement, sizeof(KEYNAMEMAP[i].replacement) );
+ return;
+ }
+ }
+ // if no mapping found, the keyname remains unchanged
+}
+
+
+/*
+=================
+UI_WriteBFPConfig
+=================
+*/
+static void UI_WriteBFPConfig( void ) { // BFP - Overwrites bfp.cfg
+ fileHandle_t f;
+ int i, len, bufLen = 0;
+ char buffer[BFP_CFG_BUFFER_SIZE];
+ char modelBuffer[MAX_STRING_CHARS], binding[MAX_STRING_CHARS];
+ char keyname[32];
+
+ // write model configuration first
+ trap_Cvar_VariableStringBuffer( "model", modelBuffer, sizeof(modelBuffer) );
+ if ( modelBuffer[0] ) {
+ bufLen += Com_sprintf( buffer + bufLen, sizeof(buffer), "c model %s\n", modelBuffer );
+ }
+
+ // write all key bindings by iterating through all possible keys
+ for ( i = 0; i < 256; ++i ) {
+ trap_Key_KeynumToStringBuf( i, keyname, sizeof(keyname) );
+ trap_Key_GetBindingBuf( i, binding, sizeof(binding) );
+
+ // convert any hexadecimal key into a symbol character, e.g. 0x7e = ~
+ UI_ConvertHexKeyname( keyname );
+
+ // only write non-empty bindings
+ if ( binding[0] ) {
+ bufLen += Com_sprintf( buffer + bufLen, sizeof(buffer), "b %s \"%s\"\n", keyname, binding );
+ }
+ }
+
+ // write all BFP-specific cvars
+ {
+ typedef struct {
+ const char *cvarName, *cvarValue;
+ } bfpCvars_t;
+
+ const bfpCvars_t BFPCVARS[] = {
+ { "cg_thirdPerson", "1" },
+ { "cg_thirdPersonRange", "110" },
+ { "cg_thirdPersonHeight", "-60" },
+ { "cg_fixedThirdPerson", "1" },
+ { "cg_lightAuras", "1" },
+ { "cg_lightExplosions", "1" },
+ { "cg_lightweightAuras", "0" },
+ { "cg_transformationAura", "1" },
+ { "cg_simpleHUD", "0" },
+ { "cg_drawOwnModel", "1" },
+ { "cg_polygonAura", "1" },
+ { "cg_spriteAura", "0" },
+ { "cg_highPolyAura", "1" },
+ { "cg_smallOwnAura", "1" },
+ { "cg_permaglowUltimate", "1" },
+ { "cg_superdeformed", "0" },
+ { "cg_flytilt", "1" },
+ { "cg_kiTrail", "50" },
+ { "cg_beamTrail", "25" },
+ { "cg_bigExplosions", "1" },
+ { "cg_chargeupAlert", "1" },
+ { "cg_stfu", "0" },
+ { "cg_lifekills", "0" },
+ { "cg_lifedeaths", "0" },
+ { "cg_drawKiWarning", "1" },
+ { "sv_pure", "0" },
+ { "cg_explosionShell", "1" },
+ { "cg_explosionSmoke", "1" },
+ { "cg_particles", "1" },
+ { "cg_crosshairColor", "7" },
+ { "cg_stableCrosshair", "0" },
+ { "cg_explosionRing", "1" },
+ { "cg_lowpolysphere", "0" },
+ { "cg_musicUnpacked", "1" },
+ { "cg_playHitSound", "0" },
+ { "g_hitStun", "1" }
+ };
+
+ int numCvars = sizeof(BFPCVARS) / sizeof(BFPCVARS[0]);
+
+ for ( i = 0; i < numCvars; ++i ) {
+ const char *cvarName = BFPCVARS[i].cvarName;
+ const char *defaultValue = BFPCVARS[i].cvarValue;
+ char cvarValue[MAX_STRING_CHARS];
+
+ // get current cvar value into a separate buffer
+ trap_Cvar_VariableStringBuffer( cvarName, cvarValue, sizeof(cvarValue) );
+
+ // if cvar doesn't exist or is empty, use default
+ if ( !cvarValue[0] ) {
+ Q_strncpyz( cvarValue, defaultValue, sizeof(cvarValue) );
+ }
+
+ // write cvar to buffer
+ bufLen += Com_sprintf( buffer + bufLen, sizeof(buffer), "c %s %s\n", cvarName, cvarValue );
+ }
+ }
+
+ // write "bfp" marker at the end of file
+ bufLen += Com_sprintf( buffer + bufLen, sizeof(buffer), "bfp\n" );
+
+ // check buffer size once at the end
+ if ( bufLen >= (int)sizeof(buffer) ) {
+ Com_Printf( "BFP config file too long\n" );
+ return;
+ }
+
+ // write the entire buffer to file
+ len = trap_FS_FOpenFile( "bfp.cfg", &f, FS_WRITE );
+ if ( len < 0 ) {
+ Com_Printf( "Could not open bfp.cfg for writing\n" );
+ return;
+ }
+
+ trap_FS_Write( buffer, bufLen, f );
+ trap_FS_FCloseFile( f );
+}
+
/*
=================
UI_Shutdown
=================
*/
void UI_Shutdown( void ) {
+ UI_WriteBFPConfig(); // BFP - Overwrites bfp.cfg
}
/*
@@ -1082,18 +1249,7 @@ void UI_Init( void ) {
UI_InitGameinfo();
// cache redundant calulations
- trap_GetGlconfig( &uis.glconfig );
-
- // for 640x480 virtualized screen
- uis.scale = uis.glconfig.vidHeight * (1.0/480.0);
- if ( uis.glconfig.vidWidth * 480 > uis.glconfig.vidHeight * 640 ) {
- // wide screen
- uis.bias = 0.5 * ( uis.glconfig.vidWidth - ( uis.glconfig.vidHeight * (640.0/480.0) ) );
- }
- else {
- // no wide screen
- uis.bias = 0;
- }
+ UI_VideoCheck( -99999 );
// initialize the menu system
Menu_Cache();
@@ -1102,6 +1258,7 @@ void UI_Init( void ) {
uis.menusp = 0;
}
+
/*
================
UI_AdjustFrom640
@@ -1111,12 +1268,13 @@ Adjusted for resolution and screen aspect ratio
*/
void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) {
// expect valid pointers
- *x = *x * uis.scale + uis.bias;
- *y *= uis.scale;
+ *x = *x * uis.scale + uis.biasX;
+ *y = *y * uis.scale + uis.biasY;
*w *= uis.scale;
*h *= uis.scale;
}
+
void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
qhandle_t hShader;
@@ -1125,6 +1283,7 @@ void UI_DrawNamedPic( float x, float y, float width, float height, const char *p
trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
}
+
void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) {
float s0;
float s1;
@@ -1155,6 +1314,14 @@ void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) {
trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, hShader );
}
+
+static void UI_DrawCursor( float x, float y, float w, float h ) {
+
+ UI_AdjustFrom640( &x, &y, &w, &h );
+ trap_R_DrawStretchPic( x, y, w, h, 0, 0, 1, 1, uis.cursor );
+}
+
+
/*
================
UI_FillRect
@@ -1215,17 +1382,17 @@ void UI_Refresh( int realtime )
UI_UpdateCvars();
+ UI_VideoCheck( realtime );
+
if ( uis.activemenu )
{
if (uis.activemenu->fullscreen)
{
// draw the background
- if( uis.activemenu->showlogo ) {
+ trap_R_DrawStretchPic( 0, 0, uis.glconfig.vidWidth, uis.glconfig.vidHeight, 0, 0, 1, 1, uis.menuBackNoLogoShader );
+ if ( uis.activemenu->showlogo ) {
UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackShader );
}
- else {
- UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackNoLogoShader );
- }
}
if (uis.activemenu->draw)
@@ -1241,13 +1408,13 @@ void UI_Refresh( int realtime )
// draw cursor
UI_SetColor( NULL );
- UI_DrawHandlePic( uis.cursorx-16, uis.cursory-16, 32, 32, uis.cursor);
+ UI_DrawCursor( uis.cursorx-16, uis.cursory-16, 32, 32 );
#ifndef NDEBUG
- if (uis.debug)
+ if ( uis.debug )
{
// cursor coordinates
- UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed );
+ UI_DrawString( 0, 0, va( "(%1.1f,%1.1f)", uis.cursorx, uis.cursory ), UI_LEFT|UI_SMALLFONT, colorRed );
}
#endif
diff --git a/source/q3_ui/ui_bfpoptions.c b/source/q3_ui/ui_bfpoptions.c
index 5eb3151..73dbfc5 100644
--- a/source/q3_ui/ui_bfpoptions.c
+++ b/source/q3_ui/ui_bfpoptions.c
@@ -10,56 +10,82 @@ BFP OPTIONS MENU
#include "ui_local.h"
-#define ART_BACK0 "menu/art/back_0"
-#define ART_BACK1 "menu/art/back_1"
-#define ART_MENUBG "menu/art/menubg"
-#define ART_BARLOG "menu/art/cap_barlog"
-
-#define BFPOPTIONS_X_POS 360
-
-#define ID_AURATYPE 138
-#define ID_EXPLOTYPE 139
-#define ID_VIEWPOINT 140
-#define ID_FIX3PERSON 141
-#define ID_PARTICLESFX 142
-#define ID_DYNAURALIGHT 143
-#define ID_DYNEXPLOLIGHT 144
-#define ID_KITRAILENGTH 145
-#define ID_BEAMCMPXY 146
-#define ID_TRANSAURA 147
-#define ID_SMALLAURA 148
-#define ID_SSJGLOW 149
-#define ID_ACCUCROSSHAIR 150
-#define ID_SIMPLEHUD 151
-#define ID_CHARGEALERT 152
-#define ID_Q3HITSFX 153
-#define ID_FLIGHTILT 154
-#define ID_BIGHEADS 155
-#define ID_DEFAULTSKINS 156
-#define ID_DISABLEVOICES 157
-#define ID_LOWPOLYSPHERE 158
-#define ID_BACK 159
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_MENUBG "menu/art/menubg"
+#define ART_BARLOG "menu/art/cap_barlog"
+
+#define BFPOPTIONS_X_POS 450
+#define BFPOPTIONS_SECTION_Y (BIGCHAR_HEIGHT * 2)
+
+#define ID_AURATYPE 138
+#define ID_EXPLOTYPE 139
+#define ID_VIEWPOINT 140
+#define ID_FIX3PERSON 141
+#define ID_PARTICLESFX 142
+#define ID_3DPARTICLESFX 143
+#define ID_DYNAURALIGHT 144
+#define ID_DYNEXPLOLIGHT 145
+#define ID_KITRAILENGTH 146
+#define ID_BEAMCMPXY 147
+#define ID_TRANSFORMATIONAURA 148
+#define ID_SMALLAURA 149
+#define ID_ULTIMAPERMAGLOW 150
+#define ID_ACCUCROSSHAIR 151
+#define ID_SIMPLEHUD 152
+#define ID_CHARGEALERT 153
+#define ID_Q3HITSFX 154
+#define ID_FLIGHTILT 155
+#define ID_BIGHEADS 156
+#define ID_DEFAULTSKINS 157
+#define ID_STFU 158
+#define ID_LOWPOLYSPHERE 159
+#define ID_BIGEXPLOSIONS 160
+#define ID_EXPLOSIONSHELL 161
+#define ID_EXPLOSIONSMOKE 162
+#define ID_EXPLOSIONRING 163
+#define ID_BACK 164
+#define ID_AURASCONFIG 165
+#define ID_EXPLOSIONSCONFIG 166
+#define ID_VIEWEFFSNDCONFIG 167
+
+// Macros to handle the cases in that order
+#define SPRITE_AURA 0
+#define LIGHTWEIGHT_AURA 1
+#define POLYGON_AURA 2
+#define HIGHPOLYCOUNT_AURA 3
+#define PARTICLE_AURA 4
+#define SHADER_AURA 5
+
+#define WIMPY_EXPLO 0
+#define WEAK_EXPLO 1
+#define SO_SO_EXPLO 2
+#define HARDCORE_EXPLO 3
+#define ULTRA_HARDCORE_EXPLO 4
static const char *auratype_items[] = {
- "High Polycount Aura",
+ "Sprite Aura",
+ "Lightweight Aura",
"Polygonal Aura",
+ "High Polycount Aura",
+ "Particle Aura (Particle Effects only)",
"Shader Aura",
- "Lightweight Aura",
NULL
};
static const char *viewpoint_items[] = {
"Third Person",
"First Person",
- "First Person Vis",
+ "First Person Vis Mode",
NULL
};
static const char* explotype_items[] = {
- "So-So",
- "Hardcore",
"Wimpy",
"Weak",
+ "So-So",
+ "Hardcore",
+ "Ultra Hardcore",
NULL
};
@@ -69,19 +95,28 @@ typedef struct {
menubitmap_s barlog;
menutext_s banner;
+ menutext_s aurasButton;
+ menutext_s explosionsButton;
+ menutext_s viewEffectsSoundsButton;
+
menulist_s auratype;
menulist_s explotype;
menulist_s viewpoint;
menuradiobutton_s fix3person;
menuradiobutton_s particlesfx;
+ menuradiobutton_s particles3dfx;
menuradiobutton_s dynauralight;
menuradiobutton_s dynamiclights;
menuradiobutton_s dynexplolights;
+ menuradiobutton_s bigexplosions;
+ menuradiobutton_s explosionshell;
+ menuradiobutton_s explosionsmoke;
+ menuradiobutton_s explosionring;
menuslider_s kitrailength;
menuslider_s beamcmpxy;
menuradiobutton_s transaura;
menuradiobutton_s smallaura;
- menuradiobutton_s ssjglow;
+ menuradiobutton_s ultpermaglow;
menuradiobutton_s accucrosshair;
menuradiobutton_s simplehud;
menuradiobutton_s chargealert;
@@ -89,114 +124,206 @@ typedef struct {
menuradiobutton_s flightilt;
menuradiobutton_s bigheads;
menuradiobutton_s defaultskins;
- menuradiobutton_s disablevoices;
+ menuradiobutton_s stfu;
menuradiobutton_s lowpolysphere;
menubitmap_s back;
-
} bfpoptions_t;
static bfpoptions_t s_bfpoptions;
+static int menuBarOption = ID_AURASCONFIG;
+
+void BFPAuraOptions_MenuInit( void );
+void BFPExplosionsOptions_MenuInit( void );
+void BFPViewEffSndsOptions_MenuInit( void );
+
+static void BFPOptions_MenuItem( int *menu_item_curvalue, const char *cvar, int value ) {
+ *menu_item_curvalue = trap_Cvar_VariableValue( cvar ) != value;
+}
+
static void BFPOptions_SetMenuItems( void ) {
- s_bfpoptions.fix3person.curvalue = trap_Cvar_VariableValue( "cg_fixedThirdPerson" ) != 0;
- s_bfpoptions.particlesfx.curvalue = trap_Cvar_VariableValue( "cg_particles" ) != 0;
- s_bfpoptions.dynauralight.curvalue = trap_Cvar_VariableValue( "cg_lightAuras" ) != 0;
- s_bfpoptions.dynexplolights.curvalue = trap_Cvar_VariableValue( "cg_lightExplosions" ) != 0;
- s_bfpoptions.transaura.curvalue = trap_Cvar_VariableValue( "cg_transformationAura" ) != 0;
- s_bfpoptions.smallaura.curvalue = trap_Cvar_VariableValue( "cg_smallOwnAura" ) != 0;
- s_bfpoptions.ssjglow.curvalue = trap_Cvar_VariableValue( "cg_permaglowUltimate" ) != 0;
- s_bfpoptions.accucrosshair.curvalue = trap_Cvar_VariableValue( "cg_stableCrosshair" ) != 0;
- s_bfpoptions.simplehud.curvalue = trap_Cvar_VariableValue( "cg_simpleHUD" ) != 0;
- s_bfpoptions.chargealert.curvalue = trap_Cvar_VariableValue( "cg_chargeupAlert" ) != 0;
- s_bfpoptions.q3hitsfx.curvalue = trap_Cvar_VariableValue( "cg_playHitSound" ) != 0;
- s_bfpoptions.flightilt.curvalue = trap_Cvar_VariableValue( "cg_flytilt" ) != 0;
- s_bfpoptions.bigheads.curvalue = trap_Cvar_VariableValue( "cg_superdeformed" ) != 0;
- s_bfpoptions.defaultskins.curvalue = trap_Cvar_VariableValue( "cg_forceSkin" ) != 0;
- s_bfpoptions.disablevoices.curvalue = trap_Cvar_VariableValue( "cg_stfu" ) != 0;
- s_bfpoptions.lowpolysphere.curvalue = trap_Cvar_VariableValue( "cg_lowpolysphere" ) != 0;
+ BFPOptions_MenuItem( &s_bfpoptions.fix3person.curvalue, "cg_fixedThirdPerson", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.particlesfx.curvalue, "cg_particles", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.particles3dfx.curvalue, "cg_3dparticles", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.dynauralight.curvalue, "cg_lightAuras", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.dynexplolights.curvalue, "cg_lightExplosions", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.bigexplosions.curvalue, "cg_bigExplosions", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.explosionshell.curvalue, "cg_explosionShell", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.explosionsmoke.curvalue, "cg_explosionSmoke", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.explosionring.curvalue, "cg_explosionRing", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.transaura.curvalue, "cg_transformationAura", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.smallaura.curvalue, "cg_smallOwnAura", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.ultpermaglow.curvalue, "cg_permaglowUltimate", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.accucrosshair.curvalue, "cg_stableCrosshair", 1 ); // doesn't make sense if the crosshair isn't accurate
+ BFPOptions_MenuItem( &s_bfpoptions.simplehud.curvalue, "cg_simpleHUD", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.chargealert.curvalue, "cg_chargeupAlert", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.q3hitsfx.curvalue, "cg_playHitSound", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.flightilt.curvalue, "cg_flytilt", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.bigheads.curvalue, "cg_superdeformed", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.defaultskins.curvalue, "cg_forceSkin", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.stfu.curvalue, "cg_stfu", 0 );
+ BFPOptions_MenuItem( &s_bfpoptions.lowpolysphere.curvalue, "cg_lowpolysphere", 0 );
+}
+
+static void BFPOptions_AuraType_Setup( int sprite, int highpoly, int poly, int light, int particle ) {
+ trap_Cvar_SetValue( "cg_spriteAura", sprite );
+ trap_Cvar_SetValue( "cg_highPolyAura", highpoly );
+ trap_Cvar_SetValue( "cg_polygonAura", poly );
+ trap_Cvar_SetValue( "cg_lightweightAuras", light );
+ trap_Cvar_SetValue( "cg_particleAura", particle );
+}
+
+static void BFPOptions_Viewpoint_Setup( int tp, int ownmodel ) {
+ trap_Cvar_SetValue( "cg_thirdPerson", tp );
+ trap_Cvar_SetValue( "cg_drawOwnModel", ownmodel );
}
+static void BFPOptions_ExploType_Setup( int expShell, int expSmoke, int particles, int expRing, int particles3d ) {
+ trap_Cvar_SetValue( "cg_explosionShell", expShell );
+ trap_Cvar_SetValue( "cg_explosionSmoke", expSmoke );
+ trap_Cvar_SetValue( "cg_explosionRing", expRing );
+ trap_Cvar_SetValue( "cg_particles", particles );
+ trap_Cvar_SetValue( "cg_3dparticles", particles3d );
+ s_bfpoptions.explosionshell.curvalue = expShell;
+ s_bfpoptions.explosionsmoke.curvalue = expSmoke;
+ s_bfpoptions.explosionring.curvalue = expRing;
+ s_bfpoptions.particlesfx.curvalue = particles;
+ s_bfpoptions.particles3dfx.curvalue = particles3d;
+}
+
+static void BFPOptions_ExplosionsTypeCheck( void ) {
+ int particles = s_bfpoptions.particlesfx.curvalue;
+ int particles3d = s_bfpoptions.particles3dfx.curvalue;
+ int explosionSmoke = s_bfpoptions.explosionsmoke.curvalue;
+ int explosionShell = s_bfpoptions.explosionshell.curvalue;
+ int explosionRing = s_bfpoptions.explosionring.curvalue;
+
+ if ( particles <= 0 && particles3d <= 0 && explosionSmoke <= 0 && explosionShell <= 0 && explosionRing <= 0 ) {
+ s_bfpoptions.explotype.curvalue = WIMPY_EXPLO;
+ }
+ if ( particles <= 0 && particles3d <= 0 && explosionSmoke <= 0 && explosionShell >= 1 && explosionRing >= 1 ) {
+ s_bfpoptions.explotype.curvalue = WEAK_EXPLO;
+ }
+ if ( particles >= 1 && particles3d <= 0 && explosionSmoke <= 0 && explosionShell >= 1 && explosionRing >= 1 ) {
+ s_bfpoptions.explotype.curvalue = SO_SO_EXPLO;
+ }
+ if ( particles >= 1 && particles3d <= 0 && explosionSmoke >= 1 && explosionShell >= 1 && explosionRing >= 1 ) {
+ s_bfpoptions.explotype.curvalue = HARDCORE_EXPLO;
+ }
+ if ( particles >= 1 && particles3d >= 1 && explosionSmoke >= 1 && explosionShell >= 1 && explosionRing >= 1 ) {
+ s_bfpoptions.explotype.curvalue = ULTRA_HARDCORE_EXPLO;
+ }
+}
+
+static void BFPOptions_ParticlesCheck( void ) {
+ int particles = s_bfpoptions.particlesfx.curvalue;
+ int particles3d = s_bfpoptions.particles3dfx.curvalue;
+
+ if ( particles <= 0 && particles3d >= 1 ) {
+ s_bfpoptions.particles3dfx.curvalue = 0;
+ trap_Cvar_SetValue( "cg_3dparticles", 0 );
+ }
+}
static void BFPOptions_Event( void* ptr, int notification ) {
- if( notification != QM_ACTIVATED ) {
+ if ( notification != QM_ACTIVATED ) {
return;
}
- switch( ((menucommon_s*)ptr)->id ) {
+ switch ( ((menucommon_s*)ptr)->id ) {
+ //---------------------------Show options---------------------------------//
+ case ID_AURASCONFIG:
+ if ( menuBarOption != ID_AURASCONFIG ) {
+ menuBarOption = ID_AURASCONFIG;
+ BFPAuraOptions_MenuInit();
+ }
+ break;
+
+ case ID_EXPLOSIONSCONFIG:
+ if ( menuBarOption != ID_EXPLOSIONSCONFIG ) {
+ menuBarOption = ID_EXPLOSIONSCONFIG;
+ BFPExplosionsOptions_MenuInit();
+ }
+ break;
+
+ case ID_VIEWEFFSNDCONFIG:
+ if ( menuBarOption != ID_VIEWEFFSNDCONFIG ) {
+ menuBarOption = ID_VIEWEFFSNDCONFIG;
+ BFPViewEffSndsOptions_MenuInit();
+ }
+ break;
//-----------------------------Aura list---------------------------------//
case ID_AURATYPE:
- if( s_bfpoptions.auratype.curvalue == 0 ) {
- trap_Cvar_SetValue( "cg_highPolyAura", 1 );
- trap_Cvar_SetValue( "cg_polygonAura", 1 );
- trap_Cvar_SetValue( "cg_lightweightAuras", 0 );
- }
- else if( s_bfpoptions.auratype.curvalue == 1 ) {
- trap_Cvar_SetValue( "cg_highPolyAura", 0 );
- trap_Cvar_SetValue( "cg_polygonAura", 1 );
- trap_Cvar_SetValue( "cg_lightweightAuras", 0 );
- }
- else if( s_bfpoptions.auratype.curvalue == 2 ) {
- trap_Cvar_SetValue( "cg_highPolyAura", 0 );
- trap_Cvar_SetValue( "cg_polygonAura", 0 );
- trap_Cvar_SetValue( "cg_lightweightAuras", 0 );
- }
- else if( s_bfpoptions.auratype.curvalue == 3 ) {
- trap_Cvar_SetValue( "cg_highPolyAura", 0 );
- trap_Cvar_SetValue( "cg_polygonAura", 0 );
- trap_Cvar_SetValue( "cg_lightweightAuras", 1 );
- }
- else if( s_bfpoptions.auratype.curvalue == 4 ) {
- trap_Cvar_SetValue( "cg_highPolyAura", 0 );
- trap_Cvar_SetValue( "cg_polygonAura", 0 );
- trap_Cvar_SetValue( "cg_lightweightAuras", 0 );
+ switch ( s_bfpoptions.auratype.curvalue ) {
+ case SPRITE_AURA:
+ BFPOptions_AuraType_Setup( 1, 0, 0, 0, 0 );
+ break;
+
+ case LIGHTWEIGHT_AURA:
+ BFPOptions_AuraType_Setup( 0, 0, 0, 1, 0 );
+ break;
+
+ case POLYGON_AURA:
+ BFPOptions_AuraType_Setup( 0, 0, 1, 0, 0 );
+ break;
+
+ case HIGHPOLYCOUNT_AURA:
+ BFPOptions_AuraType_Setup( 0, 1, 1, 0, 0 );
+ break;
+
+ case PARTICLE_AURA:
+ BFPOptions_AuraType_Setup( 0, 0, 0, 0, 1 );
+ break;
+
+ case SHADER_AURA:
+ BFPOptions_AuraType_Setup( 0, 0, 0, 0, 0 );
+ break;
}
break;
//---------------------------View point List---------------------------------------//
case ID_VIEWPOINT:
- if( s_bfpoptions.viewpoint.curvalue == 0 ) {
+ switch ( s_bfpoptions.viewpoint.curvalue ) {
+ case 0: // Third Person
trap_Cvar_SetValue( "cg_thirdPerson", 1 );
- trap_Cvar_SetValue( "cg_drawOwnModel", 1 );
- }
- else if( s_bfpoptions.viewpoint.curvalue == 1 ) {
- trap_Cvar_SetValue( "cg_thirdPerson", 0 );
- trap_Cvar_SetValue( "cg_drawOwnModel", 0 );
- }
- else if( s_bfpoptions.viewpoint.curvalue == 2 ) {
- trap_Cvar_SetValue( "cg_thirdPerson", 0 );
- trap_Cvar_SetValue( "cg_drawOwnModel", 1 );
+ break;
+
+ case 1: // First Person
+ BFPOptions_Viewpoint_Setup( 0, 0 );
+ break;
+
+ case 2: // First Person Vis Mode
+ BFPOptions_Viewpoint_Setup( 0, 1 );
+ break;
}
break;
//---------------------------Explosion type list---------------------------------------//
case ID_EXPLOTYPE:
- if ( s_bfpoptions.explotype.curvalue == 0 ) {
- trap_Cvar_SetValue( "cg_explosionShell", 1 );
- trap_Cvar_SetValue( "cg_explosionSmoke", 0 );
- trap_Cvar_SetValue( "cg_particles", 1 );
- trap_Cvar_SetValue( "cg_explosionRing", 1 );
- }
- else if ( s_bfpoptions.explotype.curvalue == 1 ) {
- trap_Cvar_SetValue( "cg_explosionShell", 1 );
- trap_Cvar_SetValue( "cg_explosionSmoke", 1 );
- trap_Cvar_SetValue( "cg_particles", 1 );
- trap_Cvar_SetValue( "cg_explosionRing", 1 );
- }
- else if ( s_bfpoptions.explotype.curvalue == 2 ) {
- trap_Cvar_SetValue( "cg_explosionShell", 0 );
- trap_Cvar_SetValue( "cg_explosionSmoke", 0 );
- trap_Cvar_SetValue( "cg_particles", 0 );
- trap_Cvar_SetValue( "cg_explosionRing", 0 );
- }
- else if ( s_bfpoptions.explotype.curvalue == 3 ) {
- trap_Cvar_SetValue( "cg_explosionShell", 1 );
- trap_Cvar_SetValue( "cg_explosionSmoke", 0 );
- trap_Cvar_SetValue( "cg_particles", 0 );
- trap_Cvar_SetValue( "cg_explosionRing", 1 );
+ switch ( s_bfpoptions.explotype.curvalue ) {
+ case WIMPY_EXPLO: // Wimpy
+ BFPOptions_ExploType_Setup( 0, 0, 0, 0, 0 );
+ break;
+
+ case WEAK_EXPLO: // Weak
+ BFPOptions_ExploType_Setup( 1, 0, 0, 1, 0 );
+ break;
+
+ case SO_SO_EXPLO: // So-So
+ BFPOptions_ExploType_Setup( 1, 0, 1, 1, 0 );
+ break;
+
+ case HARDCORE_EXPLO: // Hardcore
+ BFPOptions_ExploType_Setup( 1, 1, 1, 1, 0 );
+ break;
+
+ case ULTRA_HARDCORE_EXPLO: // Ultra Hardcore
+ BFPOptions_ExploType_Setup( 1, 1, 1, 1, 1 );
+ break;
}
break;
@@ -208,6 +335,17 @@ static void BFPOptions_Event( void* ptr, int notification ) {
case ID_PARTICLESFX:
trap_Cvar_SetValue( "cg_particles", s_bfpoptions.particlesfx.curvalue );
+ BFPOptions_ParticlesCheck();
+ BFPOptions_ExplosionsTypeCheck();
+ break;
+
+ case ID_3DPARTICLESFX:
+ if ( s_bfpoptions.particles3dfx.curvalue >= 1 && s_bfpoptions.particlesfx.curvalue <= 0 ) {
+ s_bfpoptions.particlesfx.curvalue = 1;
+ trap_Cvar_SetValue( "cg_particles", 1 );
+ }
+ trap_Cvar_SetValue( "cg_3dparticles", s_bfpoptions.particles3dfx.curvalue );
+ BFPOptions_ExplosionsTypeCheck();
break;
case ID_DYNAURALIGHT:
@@ -218,6 +356,25 @@ static void BFPOptions_Event( void* ptr, int notification ) {
trap_Cvar_SetValue( "cg_lightExplosions", s_bfpoptions.dynexplolights.curvalue );
break;
+ case ID_BIGEXPLOSIONS:
+ trap_Cvar_SetValue( "cg_bigExplosions", s_bfpoptions.bigexplosions.curvalue );
+ break;
+
+ case ID_EXPLOSIONSHELL:
+ trap_Cvar_SetValue( "cg_explosionShell", s_bfpoptions.explosionshell.curvalue );
+ BFPOptions_ExplosionsTypeCheck();
+ break;
+
+ case ID_EXPLOSIONSMOKE:
+ trap_Cvar_SetValue( "cg_explosionSmoke", s_bfpoptions.explosionsmoke.curvalue );
+ BFPOptions_ExplosionsTypeCheck();
+ break;
+
+ case ID_EXPLOSIONRING:
+ trap_Cvar_SetValue( "cg_explosionRing", s_bfpoptions.explosionring.curvalue );
+ BFPOptions_ExplosionsTypeCheck();
+ break;
+
//----------------------------Sliders------------------------------------//
@@ -233,7 +390,7 @@ static void BFPOptions_Event( void* ptr, int notification ) {
//-----------------------------------------------------------------------//
- case ID_TRANSAURA:
+ case ID_TRANSFORMATIONAURA:
trap_Cvar_SetValue( "cg_transformationAura", s_bfpoptions.transaura.curvalue );
break;
@@ -241,12 +398,12 @@ static void BFPOptions_Event( void* ptr, int notification ) {
trap_Cvar_SetValue( "cg_smallOwnAura", s_bfpoptions.smallaura.curvalue );
break;
- case ID_SSJGLOW:
- trap_Cvar_SetValue( "cg_permaglowUltimate", s_bfpoptions.ssjglow.curvalue );
+ case ID_ULTIMAPERMAGLOW:
+ trap_Cvar_SetValue( "cg_permaglowUltimate", s_bfpoptions.ultpermaglow.curvalue );
break;
case ID_ACCUCROSSHAIR:
- trap_Cvar_SetValue( "cg_stableCrosshair", s_bfpoptions.accucrosshair.curvalue );
+ trap_Cvar_SetValue( "cg_stableCrosshair", s_bfpoptions.accucrosshair.curvalue ? 0 : 1 ); // doesn't make sense if the crosshair isn't accurate
break;
case ID_SIMPLEHUD:
@@ -273,8 +430,8 @@ static void BFPOptions_Event( void* ptr, int notification ) {
trap_Cvar_SetValue( "cg_forceSkin", s_bfpoptions.defaultskins.curvalue );
break;
- case ID_DISABLEVOICES:
- trap_Cvar_SetValue( "cg_stfu", s_bfpoptions.disablevoices.curvalue );
+ case ID_STFU:
+ trap_Cvar_SetValue( "cg_stfu", s_bfpoptions.stfu.curvalue );
break;
case ID_LOWPOLYSPHERE:
@@ -288,21 +445,7 @@ static void BFPOptions_Event( void* ptr, int notification ) {
}
-static void BFPOptions_MenuInit( void ) {
- int y;
- int highpolyaura, polygonalaura, lightweightaura;
- int thirdperson, firstpersonvis;
- int explosionShell, explosionSmoke;
- int explosionring;
- int particles;
-
- memset( &s_bfpoptions, 0, sizeof(bfpoptions_t) );
-
- BFPOptions_Cache();
-
- s_bfpoptions.menu.wrapAround = qtrue;
- s_bfpoptions.menu.fullscreen = qtrue;
-
+static void BFPBarBackground_MenuSet( void ) {
s_bfpoptions.menubg.generic.type = MTYPE_BITMAP;
s_bfpoptions.menubg.generic.name = ART_MENUBG;
s_bfpoptions.menubg.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
@@ -327,33 +470,290 @@ static void BFPOptions_MenuInit( void ) {
s_bfpoptions.banner.color = color_white;
s_bfpoptions.banner.style = UI_CENTER|UI_BIGFONT;
- y = 90;
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.menubg );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.barlog );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.banner );
+}
+
+static void BFPButtonOptions_MenuSet( void ) {
+ BFPBarBackground_MenuSet();
+
+ s_bfpoptions.aurasButton.generic.type = MTYPE_PTEXT;
+ s_bfpoptions.aurasButton.generic.flags = ( menuBarOption == ID_AURASCONFIG ) ? QMF_RIGHT_JUSTIFY : (QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS);
+ s_bfpoptions.aurasButton.generic.id = ID_AURASCONFIG;
+ s_bfpoptions.aurasButton.generic.callback = BFPOptions_Event;
+ s_bfpoptions.aurasButton.generic.x = 216;
+ s_bfpoptions.aurasButton.generic.y = 240 - 2 * PROP_HEIGHT;
+ s_bfpoptions.aurasButton.string = "AURAS";
+ s_bfpoptions.aurasButton.style = UI_RIGHT;
+ s_bfpoptions.aurasButton.color = color_white;
+
+ s_bfpoptions.explosionsButton.generic.type = MTYPE_PTEXT;
+ s_bfpoptions.explosionsButton.generic.flags = ( menuBarOption == ID_EXPLOSIONSCONFIG ) ? QMF_RIGHT_JUSTIFY : (QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS);
+ s_bfpoptions.explosionsButton.generic.id = ID_EXPLOSIONSCONFIG;
+ s_bfpoptions.explosionsButton.generic.callback = BFPOptions_Event;
+ s_bfpoptions.explosionsButton.generic.x = 216;
+ s_bfpoptions.explosionsButton.generic.y = 240 - PROP_HEIGHT;
+ s_bfpoptions.explosionsButton.string = "EXPLOSIONS";
+ s_bfpoptions.explosionsButton.style = UI_RIGHT;
+ s_bfpoptions.explosionsButton.color = color_white;
+
+ s_bfpoptions.viewEffectsSoundsButton.generic.type = MTYPE_PTEXT;
+ s_bfpoptions.viewEffectsSoundsButton.generic.flags = ( menuBarOption == ID_VIEWEFFSNDCONFIG ) ? QMF_RIGHT_JUSTIFY : (QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS);
+ s_bfpoptions.viewEffectsSoundsButton.generic.id = ID_VIEWEFFSNDCONFIG;
+ s_bfpoptions.viewEffectsSoundsButton.generic.callback = BFPOptions_Event;
+ s_bfpoptions.viewEffectsSoundsButton.generic.x = 216;
+ s_bfpoptions.viewEffectsSoundsButton.generic.y = 240;
+ s_bfpoptions.viewEffectsSoundsButton.string = "VIEW & FX";
+ s_bfpoptions.viewEffectsSoundsButton.style = UI_RIGHT;
+ s_bfpoptions.viewEffectsSoundsButton.color = color_white;
+
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.aurasButton );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.explosionsButton );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.viewEffectsSoundsButton );
+}
+
+
+void BFPAuraOptions_MenuInit( void ) {
+ int y;
+ int highpolyaura, polygonalaura, lightweightaura, spriteaura, particleaura;
+
+ memset( &s_bfpoptions, 0, sizeof(bfpoptions_t) );
+
+ BFPOptions_Cache();
+
+ s_bfpoptions.menu.wrapAround = qtrue;
+ s_bfpoptions.menu.fullscreen = qtrue;
+
+ y = 240 - 3 * (BIGCHAR_HEIGHT+2);
s_bfpoptions.auratype.generic.type = MTYPE_SPINCONTROL;
s_bfpoptions.auratype.generic.name = "Aura Type:";
s_bfpoptions.auratype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_bfpoptions.auratype.generic.callback = BFPOptions_Event;
s_bfpoptions.auratype.generic.id = ID_AURATYPE;
- s_bfpoptions.auratype.generic.x = 320;
+ s_bfpoptions.auratype.generic.x = BFPOPTIONS_X_POS-130;
s_bfpoptions.auratype.generic.y = y;
s_bfpoptions.auratype.itemnames = auratype_items;
y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.dynauralight.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.dynauralight.generic.name = "Dynamic Aura Lights:";
+ s_bfpoptions.dynauralight.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.dynauralight.generic.callback = BFPOptions_Event;
+ s_bfpoptions.dynauralight.generic.id = ID_DYNAURALIGHT;
+ s_bfpoptions.dynauralight.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.dynauralight.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.transaura.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.transaura.generic.name = "Transformation Aura:";
+ s_bfpoptions.transaura.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.transaura.generic.callback = BFPOptions_Event;
+ s_bfpoptions.transaura.generic.id = ID_TRANSFORMATIONAURA;
+ s_bfpoptions.transaura.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.transaura.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.smallaura.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.smallaura.generic.name = "Small Own Aura:";
+ s_bfpoptions.smallaura.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.smallaura.generic.callback = BFPOptions_Event;
+ s_bfpoptions.smallaura.generic.id = ID_SMALLAURA;
+ s_bfpoptions.smallaura.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.smallaura.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.ultpermaglow.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.ultpermaglow.generic.name = "Ultimate Perma-Glow:";
+ s_bfpoptions.ultpermaglow.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.ultpermaglow.generic.callback = BFPOptions_Event;
+ s_bfpoptions.ultpermaglow.generic.id = ID_ULTIMAPERMAGLOW;
+ s_bfpoptions.ultpermaglow.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.ultpermaglow.generic.y = y;
+
+ s_bfpoptions.back.generic.type = MTYPE_BITMAP;
+ s_bfpoptions.back.generic.name = ART_BACK0;
+ s_bfpoptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_bfpoptions.back.generic.callback = BFPOptions_Event;
+ s_bfpoptions.back.generic.id = ID_BACK;
+ s_bfpoptions.back.generic.x = 0;
+ s_bfpoptions.back.generic.y = 480-80;
+ s_bfpoptions.back.width = 80;
+ s_bfpoptions.back.height = 80;
+ s_bfpoptions.back.focuspic = ART_BACK1;
+
+ BFPButtonOptions_MenuSet();
+
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.auratype );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.dynauralight );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.transaura );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.smallaura );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.ultpermaglow );
+
+ highpolyaura = trap_Cvar_VariableValue( "cg_highPolyAura" );
+ polygonalaura = trap_Cvar_VariableValue( "cg_polygonAura" );
+ lightweightaura = trap_Cvar_VariableValue( "cg_lightweightAuras" );
+ spriteaura = trap_Cvar_VariableValue( "cg_spriteAura" );
+ particleaura = trap_Cvar_VariableValue( "cg_particleAura" );
+
+ if ( highpolyaura >= 1 ) {
+ s_bfpoptions.auratype.curvalue = HIGHPOLYCOUNT_AURA;
+ } else if ( polygonalaura >= 1 ) {
+ s_bfpoptions.auratype.curvalue = POLYGON_AURA;
+ } else if ( lightweightaura >= 1 ) {
+ s_bfpoptions.auratype.curvalue = LIGHTWEIGHT_AURA;
+ } else if ( spriteaura >= 1 ) {
+ s_bfpoptions.auratype.curvalue = SPRITE_AURA;
+ } else if ( particleaura >= 1 ) {
+ s_bfpoptions.auratype.curvalue = PARTICLE_AURA;
+ } else {
+ s_bfpoptions.auratype.curvalue = SHADER_AURA;
+ }
+
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.back );
+
+ BFPOptions_SetMenuItems();
+}
+
+
+void BFPExplosionsOptions_MenuInit( void ) {
+ int y;
+ int explosionRing, explosionShell, explosionSmoke;
+ int particles, particles3d;
+
+ memset( &s_bfpoptions, 0, sizeof(bfpoptions_t) );
+
+ BFPOptions_Cache();
+
+ s_bfpoptions.menu.wrapAround = qtrue;
+ s_bfpoptions.menu.fullscreen = qtrue;
+
+ y = 240 - 4 * (BIGCHAR_HEIGHT+2);
s_bfpoptions.explotype.generic.type = MTYPE_SPINCONTROL;
s_bfpoptions.explotype.generic.name = "Explosion Type:";
s_bfpoptions.explotype.generic.flags = QMF_PULSEIFFOCUS | QMF_SMALLFONT;
s_bfpoptions.explotype.generic.callback = BFPOptions_Event;
s_bfpoptions.explotype.generic.id = ID_EXPLOTYPE;
- s_bfpoptions.explotype.generic.x = 320;
+ s_bfpoptions.explotype.generic.x = BFPOPTIONS_X_POS;
s_bfpoptions.explotype.generic.y = y;
s_bfpoptions.explotype.itemnames = explotype_items;
y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.bigexplosions.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.bigexplosions.generic.name = "Big Explosions:";
+ s_bfpoptions.bigexplosions.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.bigexplosions.generic.callback = BFPOptions_Event;
+ s_bfpoptions.bigexplosions.generic.id = ID_BIGEXPLOSIONS;
+ s_bfpoptions.bigexplosions.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.bigexplosions.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.dynexplolights.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.dynexplolights.generic.name = "Dynamic Explosion Lights:";
+ s_bfpoptions.dynexplolights.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.dynexplolights.generic.callback = BFPOptions_Event;
+ s_bfpoptions.dynexplolights.generic.id = ID_DYNEXPLOLIGHT;
+ s_bfpoptions.dynexplolights.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.dynexplolights.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.lowpolysphere.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.lowpolysphere.generic.name = "Low Polycount Sphere:";
+ s_bfpoptions.lowpolysphere.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.lowpolysphere.generic.callback = BFPOptions_Event;
+ s_bfpoptions.lowpolysphere.generic.id = ID_LOWPOLYSPHERE;
+ s_bfpoptions.lowpolysphere.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.lowpolysphere.generic.y = y;
+
+ y += BFPOPTIONS_SECTION_Y;
+ s_bfpoptions.explosionsmoke.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.explosionsmoke.generic.name = "Smoke:";
+ s_bfpoptions.explosionsmoke.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.explosionsmoke.generic.callback = BFPOptions_Event;
+ s_bfpoptions.explosionsmoke.generic.id = ID_EXPLOSIONSMOKE;
+ s_bfpoptions.explosionsmoke.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.explosionsmoke.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.explosionshell.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.explosionshell.generic.name = "Shell:";
+ s_bfpoptions.explosionshell.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.explosionshell.generic.callback = BFPOptions_Event;
+ s_bfpoptions.explosionshell.generic.id = ID_EXPLOSIONSHELL;
+ s_bfpoptions.explosionshell.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.explosionshell.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.explosionring.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.explosionring.generic.name = "Ring:";
+ s_bfpoptions.explosionring.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.explosionring.generic.callback = BFPOptions_Event;
+ s_bfpoptions.explosionring.generic.id = ID_EXPLOSIONRING;
+ s_bfpoptions.explosionring.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.explosionring.generic.y = y;
+
+ s_bfpoptions.back.generic.type = MTYPE_BITMAP;
+ s_bfpoptions.back.generic.name = ART_BACK0;
+ s_bfpoptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_bfpoptions.back.generic.callback = BFPOptions_Event;
+ s_bfpoptions.back.generic.id = ID_BACK;
+ s_bfpoptions.back.generic.x = 0;
+ s_bfpoptions.back.generic.y = 480-80;
+ s_bfpoptions.back.width = 80;
+ s_bfpoptions.back.height = 80;
+ s_bfpoptions.back.focuspic = ART_BACK1;
+
+ BFPButtonOptions_MenuSet();
+
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.explotype );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.bigexplosions );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.dynexplolights );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.lowpolysphere );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.explosionsmoke );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.explosionshell );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.explosionring );
+
+ explosionSmoke = trap_Cvar_VariableValue( "cg_explosionSmoke" );
+ explosionShell = trap_Cvar_VariableValue( "cg_explosionShell" );
+ explosionRing = trap_Cvar_VariableValue( "cg_explosionRing" );
+ particles = trap_Cvar_VariableValue( "cg_particles" );
+ particles3d = trap_Cvar_VariableValue( "cg_3dparticles" );
+
+ s_bfpoptions.bigexplosions.curvalue = trap_Cvar_VariableValue( "cg_bigExplosions" );
+ s_bfpoptions.explosionsmoke.curvalue = explosionSmoke;
+ s_bfpoptions.explosionshell.curvalue = explosionShell;
+ s_bfpoptions.explosionring.curvalue = explosionRing;
+
+ s_bfpoptions.particlesfx.curvalue = particles;
+ s_bfpoptions.particles3dfx.curvalue = particles3d;
+
+ BFPOptions_ExplosionsTypeCheck();
+
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.back );
+
+ BFPOptions_SetMenuItems();
+}
+
+
+void BFPViewEffSndsOptions_MenuInit( void ) {
+ int y;
+ int thirdperson, firstpersonvis;
+
+ memset( &s_bfpoptions, 0, sizeof(bfpoptions_t) );
+
+ BFPOptions_Cache();
+
+ s_bfpoptions.menu.wrapAround = qtrue;
+ s_bfpoptions.menu.fullscreen = qtrue;
+
+ // -------------------------------View & HUD---------------------------------
+ y = 240 - 7 * (BIGCHAR_HEIGHT+2);
s_bfpoptions.viewpoint.generic.type = MTYPE_SPINCONTROL;
s_bfpoptions.viewpoint.generic.name = "Viewpoint:";
s_bfpoptions.viewpoint.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
s_bfpoptions.viewpoint.generic.callback = BFPOptions_Event;
s_bfpoptions.viewpoint.generic.id = ID_VIEWPOINT;
- s_bfpoptions.viewpoint.generic.x = 320;
+ s_bfpoptions.viewpoint.generic.x = BFPOPTIONS_X_POS;
s_bfpoptions.viewpoint.generic.y = y;
s_bfpoptions.viewpoint.itemnames = viewpoint_items;
@@ -367,6 +767,35 @@ static void BFPOptions_MenuInit( void ) {
s_bfpoptions.fix3person.generic.y = y;
y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.simplehud.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.simplehud.generic.name = "Simple HUD:";
+ s_bfpoptions.simplehud.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.simplehud.generic.callback = BFPOptions_Event;
+ s_bfpoptions.simplehud.generic.id = ID_SIMPLEHUD;
+ s_bfpoptions.simplehud.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.simplehud.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.flightilt.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.flightilt.generic.name = "Flight Tilt:";
+ s_bfpoptions.flightilt.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.flightilt.generic.callback = BFPOptions_Event;
+ s_bfpoptions.flightilt.generic.id = ID_FLIGHTILT;
+ s_bfpoptions.flightilt.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.flightilt.generic.y = y;
+
+ y += BIGCHAR_HEIGHT + 2;
+ s_bfpoptions.accucrosshair.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.accucrosshair.generic.name = "Accurate Crosshair:";
+ s_bfpoptions.accucrosshair.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.accucrosshair.generic.callback = BFPOptions_Event;
+ s_bfpoptions.accucrosshair.generic.id = ID_ACCUCROSSHAIR;
+ s_bfpoptions.accucrosshair.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.accucrosshair.generic.y = y;
+
+
+ // -------------------------------Effects---------------------------------
+ y += BFPOPTIONS_SECTION_Y;
s_bfpoptions.particlesfx.generic.type = MTYPE_RADIOBUTTON;
s_bfpoptions.particlesfx.generic.name = "Particle Effects:";
s_bfpoptions.particlesfx.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
@@ -376,22 +805,13 @@ static void BFPOptions_MenuInit( void ) {
s_bfpoptions.particlesfx.generic.y = y;
y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.dynauralight.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.dynauralight.generic.name = "Dynamic Aura Lights:";
- s_bfpoptions.dynauralight.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.dynauralight.generic.callback = BFPOptions_Event;
- s_bfpoptions.dynauralight.generic.id = ID_DYNAURALIGHT;
- s_bfpoptions.dynauralight.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.dynauralight.generic.y = y;
-
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.dynexplolights.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.dynexplolights.generic.name = "Dynamic Explosion Lights:";
- s_bfpoptions.dynexplolights.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.dynexplolights.generic.callback = BFPOptions_Event;
- s_bfpoptions.dynexplolights.generic.id = ID_DYNEXPLOLIGHT;
- s_bfpoptions.dynexplolights.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.dynexplolights.generic.y = y;
+ s_bfpoptions.particles3dfx.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.particles3dfx.generic.name = "3D Particle Effects:";
+ s_bfpoptions.particles3dfx.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.particles3dfx.generic.callback = BFPOptions_Event;
+ s_bfpoptions.particles3dfx.generic.id = ID_3DPARTICLESFX;
+ s_bfpoptions.particles3dfx.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.particles3dfx.generic.y = y;
y += BIGCHAR_HEIGHT + 2;
s_bfpoptions.kitrailength.generic.type = MTYPE_SLIDER;
@@ -416,49 +836,33 @@ static void BFPOptions_MenuInit( void ) {
s_bfpoptions.beamcmpxy.maxvalue = 100;
y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.transaura.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.transaura.generic.name = "Transformation Aura:";
- s_bfpoptions.transaura.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.transaura.generic.callback = BFPOptions_Event;
- s_bfpoptions.transaura.generic.id = ID_TRANSAURA;
- s_bfpoptions.transaura.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.transaura.generic.y = y;
-
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.smallaura.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.smallaura.generic.name = "Small Own Aura:";
- s_bfpoptions.smallaura.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.smallaura.generic.callback = BFPOptions_Event;
- s_bfpoptions.smallaura.generic.id = ID_SMALLAURA;
- s_bfpoptions.smallaura.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.smallaura.generic.y = y;
+ s_bfpoptions.bigheads.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.bigheads.generic.name = "Superdeformed Heads:";
+ s_bfpoptions.bigheads.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.bigheads.generic.callback = BFPOptions_Event;
+ s_bfpoptions.bigheads.generic.id = ID_BIGHEADS;
+ s_bfpoptions.bigheads.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.bigheads.generic.y = y;
y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.ssjglow.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.ssjglow.generic.name = "SSJ Perma-Glow:";
- s_bfpoptions.ssjglow.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.ssjglow.generic.callback = BFPOptions_Event;
- s_bfpoptions.ssjglow.generic.id = ID_SSJGLOW;
- s_bfpoptions.ssjglow.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.ssjglow.generic.y = y;
+ s_bfpoptions.defaultskins.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.defaultskins.generic.name = "Force Default Skins:";
+ s_bfpoptions.defaultskins.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.defaultskins.generic.callback = BFPOptions_Event;
+ s_bfpoptions.defaultskins.generic.id = ID_DEFAULTSKINS;
+ s_bfpoptions.defaultskins.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.defaultskins.generic.y = y;
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.accucrosshair.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.accucrosshair.generic.name = "Accurate Crosshair:";
- s_bfpoptions.accucrosshair.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.accucrosshair.generic.callback = BFPOptions_Event;
- s_bfpoptions.accucrosshair.generic.id = ID_ACCUCROSSHAIR;
- s_bfpoptions.accucrosshair.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.accucrosshair.generic.y = y;
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.simplehud.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.simplehud.generic.name = "Simple HUD:";
- s_bfpoptions.simplehud.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.simplehud.generic.callback = BFPOptions_Event;
- s_bfpoptions.simplehud.generic.id = ID_SIMPLEHUD;
- s_bfpoptions.simplehud.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.simplehud.generic.y = y;
+ // -------------------------------Sounds---------------------------------
+ y += BFPOPTIONS_SECTION_Y;
+ s_bfpoptions.stfu.generic.type = MTYPE_RADIOBUTTON;
+ s_bfpoptions.stfu.generic.name = "Disable Voices:";
+ s_bfpoptions.stfu.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_bfpoptions.stfu.generic.callback = BFPOptions_Event;
+ s_bfpoptions.stfu.generic.id = ID_STFU;
+ s_bfpoptions.stfu.generic.x = BFPOPTIONS_X_POS;
+ s_bfpoptions.stfu.generic.y = y;
y += BIGCHAR_HEIGHT + 2;
s_bfpoptions.chargealert.generic.type = MTYPE_RADIOBUTTON;
@@ -478,51 +882,6 @@ static void BFPOptions_MenuInit( void ) {
s_bfpoptions.q3hitsfx.generic.x = BFPOPTIONS_X_POS;
s_bfpoptions.q3hitsfx.generic.y = y;
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.flightilt.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.flightilt.generic.name = "Flight Tilt:";
- s_bfpoptions.flightilt.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.flightilt.generic.callback = BFPOptions_Event;
- s_bfpoptions.flightilt.generic.id = ID_FLIGHTILT;
- s_bfpoptions.flightilt.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.flightilt.generic.y = y;
-
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.bigheads.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.bigheads.generic.name = "Superdeformed Heads:";
- s_bfpoptions.bigheads.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.bigheads.generic.callback = BFPOptions_Event;
- s_bfpoptions.bigheads.generic.id = ID_BIGHEADS;
- s_bfpoptions.bigheads.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.bigheads.generic.y = y;
-
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.defaultskins.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.defaultskins.generic.name = "Force Default Skins:";
- s_bfpoptions.defaultskins.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.defaultskins.generic.callback = BFPOptions_Event;
- s_bfpoptions.defaultskins.generic.id = ID_DEFAULTSKINS;
- s_bfpoptions.defaultskins.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.defaultskins.generic.y = y;
-
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.disablevoices.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.disablevoices.generic.name = "Disable Voices:";
- s_bfpoptions.disablevoices.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.disablevoices.generic.callback = BFPOptions_Event;
- s_bfpoptions.disablevoices.generic.id = ID_DISABLEVOICES;
- s_bfpoptions.disablevoices.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.disablevoices.generic.y = y;
-
- y += BIGCHAR_HEIGHT + 2;
- s_bfpoptions.lowpolysphere.generic.type = MTYPE_RADIOBUTTON;
- s_bfpoptions.lowpolysphere.generic.name = "Low Polycount Sphere:";
- s_bfpoptions.lowpolysphere.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
- s_bfpoptions.lowpolysphere.generic.callback = BFPOptions_Event;
- s_bfpoptions.lowpolysphere.generic.id = ID_LOWPOLYSPHERE;
- s_bfpoptions.lowpolysphere.generic.x = BFPOPTIONS_X_POS;
- s_bfpoptions.lowpolysphere.generic.y = y;
-
s_bfpoptions.back.generic.type = MTYPE_BITMAP;
s_bfpoptions.back.generic.name = ART_BACK0;
s_bfpoptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
@@ -534,83 +893,38 @@ static void BFPOptions_MenuInit( void ) {
s_bfpoptions.back.height = 80;
s_bfpoptions.back.focuspic = ART_BACK1;
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.menubg );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.barlog );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.banner );
+ BFPButtonOptions_MenuSet();
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.auratype );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.explotype);
Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.viewpoint );
Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.fix3person );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.particlesfx );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.dynauralight );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.dynexplolights);
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.kitrailength);
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.beamcmpxy);
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.transaura );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.smallaura );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.ssjglow );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.accucrosshair );
Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.simplehud );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.chargealert );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.q3hitsfx );
Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.flightilt );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.accucrosshair );
+
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.particlesfx );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.particles3dfx );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.kitrailength );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.beamcmpxy );
Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.bigheads );
Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.defaultskins );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.disablevoices );
- Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.lowpolysphere );
-
-//----------------------------Auras-------------------------------------//
- highpolyaura = trap_Cvar_VariableValue( "cg_highPolyAura" );
- polygonalaura = trap_Cvar_VariableValue( "cg_polygonAura" );
- lightweightaura = trap_Cvar_VariableValue( "cg_lightweightAuras" );
-
- if ( highpolyaura == 1 ) {
- s_bfpoptions.auratype.curvalue = 0;
- } else if ( polygonalaura == 1 ) {
- s_bfpoptions.auratype.curvalue = 1;
- } else if ( lightweightaura == 1 ) {
- s_bfpoptions.auratype.curvalue = 3;
- } else {
- s_bfpoptions.auratype.curvalue = 2;
- }
-
-//----------------------------Explosions-------------------------------------//
-
- explosionSmoke = trap_Cvar_VariableValue( "cg_explosionSmoke" );
- explosionShell = trap_Cvar_VariableValue( "cg_explosionShell" );
- particles = trap_Cvar_VariableValue( "cg_particles" );
- explosionring = trap_Cvar_VariableValue( "cg_explosionRing" );
-
- if ( explosionSmoke == 1 ) {
- s_bfpoptions.explotype.curvalue = 1;
- } else if ( particles == 1 ) {
- s_bfpoptions.explotype.curvalue = 0;
- } else if ( explosionShell == 1 ) {
- s_bfpoptions.explotype.curvalue = 3;
- } else if ( explosionring == 0 ) {
- s_bfpoptions.explotype.curvalue = 2;
- }
-
-//----------------------------Camera-------------------------------------//
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.chargealert );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.q3hitsfx );
+ Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.stfu );
thirdperson = trap_Cvar_VariableValue( "cg_thirdPerson" );
firstpersonvis = trap_Cvar_VariableValue( "cg_drawOwnModel" );
- if( thirdperson == 1 ) {
+ if ( thirdperson == 1 ) {
s_bfpoptions.viewpoint.curvalue = 0;
- } else if( firstpersonvis == 1 ) {
+ } else if ( firstpersonvis == 1 ) {
s_bfpoptions.viewpoint.curvalue = 2;
- } else if( firstpersonvis == 0 ) {
+ } else if ( firstpersonvis == 0 ) {
s_bfpoptions.viewpoint.curvalue = 1;
}
-//-----------------------------------------------------------------------//
-
s_bfpoptions.kitrailength.curvalue = trap_Cvar_VariableValue( "cg_kiTrail" );
s_bfpoptions.beamcmpxy.curvalue = trap_Cvar_VariableValue( "cg_beamTrail" );
-
Menu_AddItem( &s_bfpoptions.menu, &s_bfpoptions.back );
BFPOptions_SetMenuItems();
@@ -636,6 +950,7 @@ UI_BFPOptionsMenu
===============
*/
void UI_BFPOptionsMenu( void ) {
- BFPOptions_MenuInit();
+ menuBarOption = ID_AURASCONFIG;
+ BFPAuraOptions_MenuInit();
UI_PushMenu( &s_bfpoptions.menu );
}
diff --git a/source/q3_ui/ui_cdkey.c b/source/q3_ui/ui_cdkey.c
index f277e64..4e5154e 100644
--- a/source/q3_ui/ui_cdkey.c
+++ b/source/q3_ui/ui_cdkey.c
@@ -99,7 +99,7 @@ static int UI_CDKeyMenu_PreValidateKey( const char *key ) {
}
while( ( ch = *key++ ) ) {
- switch( ch ) {
+ switch( tolower( ch ) ) {
case '2':
case '3':
case '7':
diff --git a/source/q3_ui/ui_connect.c b/source/q3_ui/ui_connect.c
index 3209889..82c023b 100644
--- a/source/q3_ui/ui_connect.c
+++ b/source/q3_ui/ui_connect.c
@@ -42,11 +42,11 @@ static void UI_ReadableSize ( char *buf, int bufsize, int value )
{
if (value > 1024*1024*1024 ) { // gigs
Com_sprintf( buf, bufsize, "%d", value / (1024*1024*1024) );
- Com_sprintf( buf+(int)strlen(buf), bufsize-(int)strlen(buf), ".%02d GB",
+ Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d GB",
(value % (1024*1024*1024))*100 / (1024*1024*1024) );
} else if (value > 1024*1024 ) { // megs
Com_sprintf( buf, bufsize, "%d", value / (1024*1024) );
- Com_sprintf( buf+(int)strlen(buf), bufsize-(int)strlen(buf), ".%02d MB",
+ Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d MB",
(value % (1024*1024))*100 / (1024*1024) );
} else if (value > 1024 ) { // kilos
Com_sprintf( buf, bufsize, "%d KB", value / 1024 );
@@ -73,16 +73,19 @@ static void UI_DisplayDownloadInfo( const char *downloadName ) {
static char etaText[] = "Estimated time left:";
static char xferText[] = "Transfer rate:";
- int downloadSize, downloadCount, downloadTime;
- char dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64];
+ int downloadSize, downloadCount, downloadTime, percentage;
+ char dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64], buf[64];
int xferRate;
- int width, leftWidth;
+ int width, leftWidth, div;
int style = UI_LEFT|UI_SMALLFONT|UI_DROPSHADOW;
const char *s;
- downloadSize = trap_Cvar_VariableValue( "cl_downloadSize" );
- downloadCount = trap_Cvar_VariableValue( "cl_downloadCount" );
- downloadTime = trap_Cvar_VariableValue( "cl_downloadTime" );
+ trap_Cvar_VariableStringBuffer( "cl_downloadSize", buf, sizeof( buf ) );
+ downloadSize = atoi( buf );
+ trap_Cvar_VariableStringBuffer( "cl_downloadCount", buf, sizeof( buf ) );
+ downloadCount = atoi( buf );
+ trap_Cvar_VariableStringBuffer( "cl_downloadTime", buf, sizeof( buf ) );
+ downloadTime = atoi( buf );
#if 0 // bk010104
fprintf( stderr, "\n\n-----------------------------------------------\n");
@@ -105,7 +108,17 @@ static void UI_DisplayDownloadInfo( const char *downloadName ) {
UI_DrawProportionalString( 8, 224, xferText, style, color_white );
if (downloadSize > 0) {
- s = va( "%s (%d%%)", downloadName, downloadCount * 100 / downloadSize );
+ if ( downloadCount > 21474836 ) {// x100 could cause overflow!
+ div = downloadSize >> 8;
+ if ( div )
+ percentage = (downloadCount >> 8) * 100 / div;
+ else
+ percentage = 0;
+ } else
+ percentage = downloadCount * 100 / downloadSize;
+ if ( percentage > 100 )
+ percentage = 100;
+ s = va( "%s (%d%%)", downloadName, percentage );
} else {
s = downloadName;
}
@@ -182,6 +195,8 @@ void UI_DrawConnectScreen( qboolean overlay ) {
uiClientState_t cstate;
char info[MAX_INFO_VALUE];
+ UI_VideoCheck( trap_Milliseconds() );
+
Menu_Cache();
if ( !overlay ) {
diff --git a/source/q3_ui/ui_controls2.c b/source/q3_ui/ui_controls2.c
index 61dd546..91d69d7 100644
--- a/source/q3_ui/ui_controls2.c
+++ b/source/q3_ui/ui_controls2.c
@@ -161,18 +161,19 @@ typedef struct
#define ANIM_WEAPON9 20
#define ANIM_WEAPON10 21
#endif
-#define ANIM_KIATTACK1 12 // BFP - (before ANIM_ATTACK)
-#define ANIM_KIATTACK2 13 // BFP
-#define ANIM_KIATTACK3 14 // BFP
-#define ANIM_KIATTACK4 15 // BFP
-#define ANIM_KIATTACK5 16 // BFP
-#define ANIM_GESTURE 17
-#define ANIM_DIE 18
-#define ANIM_CHAT 19
-#define ANIM_FLY 20 // BFP
-#define ANIM_KICHARGE 21 // BFP
-#define ANIM_MELEE 22 // BFP
-#define ANIM_BLOCK 23 // BFP
+#define ANIM_ATTACK 12
+#define ANIM_KIATTACK1 13 // BFP
+#define ANIM_KIATTACK2 14 // BFP
+#define ANIM_KIATTACK3 15 // BFP
+#define ANIM_KIATTACK4 16 // BFP
+#define ANIM_KIATTACK5 17 // BFP
+#define ANIM_GESTURE 18
+#define ANIM_DIE 19
+#define ANIM_CHAT 20
+#define ANIM_FLY 21 // BFP
+#define ANIM_KICHARGE 22 // BFP
+#define ANIM_MELEE 23 // BFP
+#define ANIM_BLOCK 24 // BFP
typedef struct
{
@@ -271,60 +272,63 @@ static vec4_t controls_binding_color = {0.90f, 0.90f, 1.00f, 1.00f}; // BFP - C
static bind_t g_bindings[] =
{
- {"+scores", "show scores", ID_SHOWSCORES, ANIM_IDLE, K_TAB, -1, -1, -1},
- {"+button2", "use item", ID_USEITEM, ANIM_IDLE, K_ENTER, -1, -1, -1},
- {"+speed", "run / walk", ID_SPEED, ANIM_RUN, K_SHIFT, -1, -1, -1},
- {"+forward", "walk forward", ID_FORWARD, ANIM_WALK, K_UPARROW, -1, -1, -1},
- {"+back", "backpedal", ID_BACKPEDAL, ANIM_BACK, K_DOWNARROW, -1, -1, -1},
- {"+moveleft", "step left", ID_MOVELEFT, ANIM_STEPLEFT, ',', -1, -1, -1},
- {"+moveright", "step right", ID_MOVERIGHT, ANIM_STEPRIGHT, '.', -1, -1, -1},
- {"+moveup", "up / jump", ID_MOVEUP, ANIM_JUMP, K_SPACE, -1, -1, -1},
- {"+movedown", "down / crouch", ID_MOVEDOWN, ANIM_CROUCH, 'c', -1, -1, -1},
- {"+left", "turn left", ID_LEFT, ANIM_TURNLEFT, K_LEFTARROW, -1, -1, -1},
- {"+right", "turn right", ID_RIGHT, ANIM_TURNRIGHT, K_RIGHTARROW, -1, -1, -1},
- {"+strafe", "sidestep / turn", ID_STRAFE, ANIM_IDLE, K_ALT, -1, -1, -1},
- {"+button12", "fly", ID_ENABLEFLIGHT,ANIM_FLY, 'f', -1, -1, -1}, // BFP - flight control "fly" bind
- {"+lookup", "look up", ID_LOOKUP, ANIM_LOOKUP, K_PGDN, -1, -1, -1},
- {"+lookdown", "look down", ID_LOOKDOWN, ANIM_LOOKDOWN, K_DEL, -1, -1, -1},
- {"+mlook", "mouse look", ID_MOUSELOOK, ANIM_IDLE, '/', -1, -1, -1},
- {"centerview", "center view", ID_CENTERVIEW, ANIM_IDLE, K_END, -1, -1, -1},
- {"+zoom", "zoom view", ID_ZOOMVIEW, ANIM_IDLE, -1, -1, -1, -1},
- {"weapon 1", "ki attack 1", ID_KIATTACK1, ANIM_KIATTACK1, '1', -1, -1, -1}, // BFP - ki attack 1 (before gauntlet)
- {"weapon 2", "ki attack 2", ID_KIATTACK2, ANIM_KIATTACK2, '2', -1, -1, -1}, // BFP - ki attack 2 (before machinegun)
- {"weapon 3", "ki attack 3", ID_KIATTACK3, ANIM_KIATTACK3, '3', -1, -1, -1}, // BFP - ki attack 3 (before shotgun)
- {"weapon 4", "ki attack 4", ID_KIATTACK4, ANIM_KIATTACK4, '4', -1, -1, -1}, // BFP - ki attack 4 (before grenade launcher)
- {"weapon 5", "ki attack 5", ID_KIATTACK5, ANIM_KIATTACK5, '5', -1, -1, -1}, // BFP - ki attack 5 (before rocket launcher)
- //{"weapon 6", "lightning", ID_WEAPON6, ANIM_WEAPON6, '6', -1, -1, -1}, // BFP - unused
- //{"weapon 7", "railgun", ID_WEAPON7, ANIM_WEAPON7, '7', -1, -1, -1}, // BFP - unused
- //{"weapon 8", "plasma gun", ID_WEAPON8, ANIM_WEAPON8, '8', -1, -1, -1}, // BFP - unused
- //{"weapon 9", "BFG", ID_WEAPON9, ANIM_WEAPON9, '9', -1, -1, -1}, // BFP - unused
- {"+attack", "attack", ID_ATTACK, ANIM_KIATTACK1, K_CTRL, -1, -1, -1},
- {"+button7", "Melee Combat", ID_MELEE, ANIM_MELEE, K_ALT, -1, -1, -1}, // BFP - melee
- {"+button10", "Block", ID_BLOCK, ANIM_BLOCK, K_CTRL, -1, -1, -1}, // BFP - block
- {"weapprev", "prev weapon", ID_WEAPPREV, ANIM_IDLE, '[', -1, -1, -1},
- {"weapnext", "next weapon", ID_WEAPNEXT, ANIM_IDLE, ']', -1, -1, -1},
- {"+button3", "gesture", ID_GESTURE, ANIM_GESTURE, K_MOUSE3, -1, -1, -1},
- {"messagemode", "chat", ID_CHAT, ANIM_CHAT, 't', -1, -1, -1},
- {"messagemode2", "chat - team", ID_CHAT2, ANIM_CHAT, -1, -1, -1, -1},
- {"messagemode3", "chat - target", ID_CHAT3, ANIM_CHAT, -1, -1, -1, -1},
- {"messagemode4", "chat - attacker", ID_CHAT4, ANIM_CHAT, -1, -1, -1, -1},
- {"+button9", "Charge Ki", ID_KICHARGE, ANIM_KICHARGE, K_MOUSE2, -1, -1, -1}, // BFP - charge ki control
- {"kiusetoggle", "Use Ki (toggle)", ID_KIUSETOGGLE, ANIM_IDLE, 'e', -1, -1, -1}, // BFP - use ki toggle control
- {"+button8", "Use Ki", ID_KIUSE, ANIM_IDLE, K_SHIFT, -1, -1, -1}, // BFP - use ki control
- {(char*)NULL, (char*)NULL, 0, 0, -1, -1, -1, -1},
+ { "+scores", "show scores", ID_SHOWSCORES, ANIM_IDLE, K_TAB, -1, -1, -1 },
+ { "+button2", "use item", ID_USEITEM, ANIM_IDLE, K_ENTER, -1, -1, -1 },
+ { "+speed", "run / walk", ID_SPEED, ANIM_RUN, K_SHIFT, -1, -1, -1 },
+ { "+forward", "walk forward", ID_FORWARD, ANIM_WALK, K_UPARROW, -1, -1, -1 },
+ { "+back", "backpedal", ID_BACKPEDAL, ANIM_BACK, K_DOWNARROW, -1, -1, -1 },
+ { "+moveleft", "step left", ID_MOVELEFT, ANIM_STEPLEFT, ',', -1, -1, -1 },
+ { "+moveright", "step right", ID_MOVERIGHT, ANIM_STEPRIGHT, '.', -1, -1, -1 },
+ { "+moveup", "up / jump", ID_MOVEUP, ANIM_JUMP, K_SPACE, -1, -1, -1 },
+ { "+movedown", "down / crouch", ID_MOVEDOWN, ANIM_CROUCH, 'c', -1, -1, -1 },
+ { "+left", "turn left", ID_LEFT, ANIM_TURNLEFT, K_LEFTARROW, -1, -1, -1 },
+ { "+right", "turn right", ID_RIGHT, ANIM_TURNRIGHT, K_RIGHTARROW, -1, -1, -1 },
+ { "+strafe", "sidestep / turn", ID_STRAFE, ANIM_IDLE, K_ALT, -1, -1, -1 },
+ { "+button12", "fly", ID_ENABLEFLIGHT,ANIM_FLY, 'f', -1, -1, -1 }, // BFP - flight control "fly" bind
+ { "+lookup", "look up", ID_LOOKUP, ANIM_LOOKUP, K_PGDN, -1, -1, -1 },
+ { "+lookdown", "look down", ID_LOOKDOWN, ANIM_LOOKDOWN, K_DEL, -1, -1, -1 },
+ { "+mlook", "mouse look", ID_MOUSELOOK, ANIM_IDLE, '/', -1, -1, -1 },
+ { "centerview", "center view", ID_CENTERVIEW, ANIM_IDLE, K_END, -1, -1, -1 },
+ { "+zoom", "zoom view", ID_ZOOMVIEW, ANIM_IDLE, -1, -1, -1, -1 },
+ { "weapon 1", "ki attack 1", ID_KIATTACK1, ANIM_KIATTACK1, '1', -1, -1, -1 }, // BFP - ki attack 1 (before gauntlet)
+ { "weapon 2", "ki attack 2", ID_KIATTACK2, ANIM_KIATTACK2, '2', -1, -1, -1 }, // BFP - ki attack 2 (before machinegun)
+ { "weapon 3", "ki attack 3", ID_KIATTACK3, ANIM_KIATTACK3, '3', -1, -1, -1 }, // BFP - ki attack 3 (before shotgun)
+ { "weapon 4", "ki attack 4", ID_KIATTACK4, ANIM_KIATTACK4, '4', -1, -1, -1 }, // BFP - ki attack 4 (before grenade launcher)
+ { "weapon 5", "ki attack 5", ID_KIATTACK5, ANIM_KIATTACK5, '5', -1, -1, -1 }, // BFP - ki attack 5 (before rocket launcher)
+ // BFP - unused
+#if 0
+ { "weapon 6", "lightning", ID_WEAPON6, ANIM_WEAPON6, '6', -1, -1, -1 },
+ { "weapon 7", "railgun", ID_WEAPON7, ANIM_WEAPON7, '7', -1, -1, -1 },
+ { "weapon 8", "plasma gun", ID_WEAPON8, ANIM_WEAPON8, '8', -1, -1, -1 },
+ { "weapon 9", "BFG", ID_WEAPON9, ANIM_WEAPON9, '9', -1, -1, -1 },
+#endif
+ { "+attack", "attack", ID_ATTACK, ANIM_ATTACK, K_CTRL, -1, -1, -1 },
+ { "+button7", "Melee Combat", ID_MELEE, ANIM_MELEE, K_ALT, -1, -1, -1 }, // BFP - melee
+ { "+button10", "Block", ID_BLOCK, ANIM_BLOCK, K_CTRL, -1, -1, -1 }, // BFP - block
+ { "weapprev", "prev weapon", ID_WEAPPREV, ANIM_IDLE, '[', -1, -1, -1 },
+ { "weapnext", "next weapon", ID_WEAPNEXT, ANIM_IDLE, ']', -1, -1, -1 },
+ { "+button3", "gesture", ID_GESTURE, ANIM_GESTURE, K_MOUSE3, -1, -1, -1 },
+ { "messagemode", "chat", ID_CHAT, ANIM_CHAT, 't', -1, -1, -1 },
+ { "messagemode2", "chat - team", ID_CHAT2, ANIM_CHAT, -1, -1, -1, -1 },
+ { "messagemode3", "chat - target", ID_CHAT3, ANIM_CHAT, -1, -1, -1, -1 },
+ { "messagemode4", "chat - attacker", ID_CHAT4, ANIM_CHAT, -1, -1, -1, -1 },
+ { "+button9", "Charge Ki", ID_KICHARGE, ANIM_KICHARGE, K_MOUSE2, -1, -1, -1 }, // BFP - charge ki control
+ { "kiusetoggle", "Use Ki (toggle)", ID_KIUSETOGGLE, ANIM_IDLE, 'e', -1, -1, -1 }, // BFP - use ki toggle control
+ { "+button8", "Use Ki", ID_KIUSE, ANIM_IDLE, K_SHIFT, -1, -1, -1 }, // BFP - use ki control
+ { (char*)NULL, (char*)NULL, 0, 0, -1, -1, -1, -1 },
};
static configcvar_t g_configcvars[] =
{
- {"cl_run", 0, 0},
- {"m_pitch", 0, 0},
- {"cg_autoswitch", 0, 0},
- {"sensitivity", 0, 0},
- {"in_joystick", 0, 0},
- {"joy_threshold", 0, 0},
- {"m_filter", 0, 0},
- {"cl_freelook", 0, 0},
- {NULL, 0, 0}
+ { "cl_run", 0, 0 },
+ { "m_pitch", 0, 0 },
+ { "cg_autoswitch", 0, 0 },
+ { "sensitivity", 0, 0 },
+ { "in_joystick", 0, 0 },
+ { "joy_threshold", 0, 0 },
+ { "m_filter", 0, 0 },
+ { "cl_freelook", 0, 0 },
+ { NULL, 0, 0 }
};
static menucommon_s *g_movement_controls[] =
@@ -530,6 +534,10 @@ static void Controls_UpdateModel( int anim ) {
s_controls.playerTorso = TORSO_CHARGE;
break;
+ case ANIM_ATTACK:
+ s_controls.playerTorso = TORSO_ATTACK0_STRIKE;
+ break;
+
// BFP - Melee
case ANIM_MELEE:
s_controls.playerLegs = LEGS_MELEE_STRIKE;
@@ -676,7 +684,7 @@ static void Controls_Update( void ) {
// enable controls in active group (and count number of items for vertical centering)
// bk001204 - parentheses
for( j = 0; (control = controls[j]) != NULL ; j++ ) {
- control->flags &= ~(QMF_GRAYED|QMF_HIDDEN|QMF_INACTIVE);
+ control->flags &= (unsigned int)~(QMF_GRAYED|QMF_HIDDEN|QMF_INACTIVE);
}
// position controls
@@ -698,24 +706,24 @@ static void Controls_Update( void ) {
}
// enable action item
- ((menucommon_s*)(s_controls.menu.items[s_controls.menu.cursor]))->flags &= ~QMF_GRAYED;
+ ((menucommon_s*)(s_controls.menu.items[s_controls.menu.cursor]))->flags &= (unsigned int)~QMF_GRAYED;
// don't gray out player's name
- s_controls.name.generic.flags &= ~QMF_GRAYED;
+ s_controls.name.generic.flags &= (unsigned int)~QMF_GRAYED;
return;
}
// enable everybody
for( i = 0; i < s_controls.menu.nitems; i++ ) {
- ((menucommon_s*)(s_controls.menu.items[i]))->flags &= ~QMF_GRAYED;
+ ((menucommon_s*)(s_controls.menu.items[i]))->flags &= (unsigned int)~QMF_GRAYED;
}
// makes sure flags are right on the group selection controls
- s_controls.looking.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
- s_controls.movement.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
- s_controls.weapons.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
- s_controls.misc.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ s_controls.looking.generic.flags &= (unsigned int)~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ s_controls.movement.generic.flags &= (unsigned int)~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ s_controls.weapons.generic.flags &= (unsigned int)~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ s_controls.misc.generic.flags &= (unsigned int)~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
s_controls.looking.generic.flags |= QMF_PULSEIFFOCUS;
s_controls.movement.generic.flags |= QMF_PULSEIFFOCUS;
@@ -725,22 +733,22 @@ static void Controls_Update( void ) {
// set buttons
switch( s_controls.section ) {
case C_MOVEMENT:
- s_controls.movement.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.movement.generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
s_controls.movement.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
break;
case C_LOOKING:
- s_controls.looking.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.looking.generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
s_controls.looking.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
break;
case C_WEAPONS:
- s_controls.weapons.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.weapons.generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
s_controls.weapons.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
break;
case C_MISC:
- s_controls.misc.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.misc.generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
s_controls.misc.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
break;
}
@@ -799,13 +807,13 @@ static void Controls_DrawKeyBinding( void *self )
if (s_controls.waitingforkey)
{
UI_DrawChar( x, y, '=', UI_CENTER|UI_BLINK|UI_SMALLFONT, text_color_highlight);
- UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Waiting for new key ... ESCAPE to cancel", UI_SMALLFONT|UI_CENTER|UI_PULSE, colorWhite );
+ UI_DrawString( SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Waiting for new key ... ESCAPE to cancel", UI_SMALLFONT|UI_CENTER|UI_PULSE, colorWhite );
}
else
{
UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, text_color_highlight);
- UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.78, "Press ENTER or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite );
- UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.82, "Press BACKSPACE to clear", UI_SMALLFONT|UI_CENTER, colorWhite );
+ UI_DrawString( SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.78, "Press ENTER or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite );
+ UI_DrawString( SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.82, "Press BACKSPACE to clear", UI_SMALLFONT|UI_CENTER, colorWhite );
}
}
else
@@ -830,7 +838,7 @@ Controls_StatusBar
*/
static void Controls_StatusBar( void *self )
{
- UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Use Arrow Keys or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite );
+ UI_DrawString( SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Use Arrow Keys or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite );
}
diff --git a/source/q3_ui/ui_credits.c b/source/q3_ui/ui_credits.c
index 9c7cefd..00e3344 100644
--- a/source/q3_ui/ui_credits.c
+++ b/source/q3_ui/ui_credits.c
@@ -33,9 +33,7 @@ CREDITS
#define ART_BFPLOGO "menu/art/bfp_logol" // BFP - Logo
#define SCROLLSPEED 6.50 // The scrolling speed in pixels per second. Modify as appropriate for our credits
-// uncomment this to use a background shader:
-// #define BACKGROUND_SHADER
-#define ART_MENUBG "menu/art/menubg" // BFP - Menu background
+#define ART_MENUBG "" // "menu/art/menubg" // BFP - Menu background
typedef struct {
menuframework_s menu;
@@ -47,13 +45,12 @@ static creditsmenu_t s_credits;
int starttime; // game time at which credits are started
float mvolume; // records the original music volume level, as we will. Modify it for the credits
-qhandle_t BackgroundShader; // definition of the background shader pointer
+qhandle_t backgroundShader; // definition of the background shader pointer
-typedef struct
-{
- char *string;
- int style;
- vec4_t *colour;
+typedef struct {
+ char *string;
+ int style;
+ vec4_t *colour;
} cr_line;
// BFP - Macros for credits. This way is easier to add in the array
@@ -206,9 +203,9 @@ Draws header.
*/
static void Credits_DrawHeader( void ) // BFP - Header
{
- // BFP - NOTE: It would be cool using a fading box or not.
- // BFP vanilla uses a black box on it and pop ups when text alpha color is near to 0.90f
-#define BLACK_BOX_FADE 0
+ // BFP - NOTE: BFP vanilla uses a black box on it and pop ups when text alpha color is near to 0.90f
+ // But in that case, to make it better, black box fades initially
+#define BLACK_BOX_FADE 1
float fadetime;
vec4_t fadeBluecolour = { 0.00f, 0.00f, 1.00f, 0.00f };
#if BLACK_BOX_FADE
@@ -252,7 +249,7 @@ static void ScrollingCredits_Draw( void )
if ( !ysize ) // ysize not calculated, so calculate it dammit!
{
// loop through entire credits array
- for( n = 0; n <= sizeof(credits) - 1; n++ )
+ for( n = 0; credits[n].string != NULL; ++n )
{
// it is a small character
if ( credits[n].style & UI_SMALLFONT )
@@ -275,36 +272,17 @@ static void ScrollingCredits_Draw( void )
// first, fill the background with the specified colour/shader
// we are drawing a shader
-#ifdef BACKGROUND_SHADER
- UI_DrawHandlePic(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, BackgroundShader);
-#endif
+ if ( backgroundShader ) {
+ UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, backgroundShader );
+ }
// let's draw the stuff
// set initial y location
y = 480 - SCROLLSPEED * (float)( uis.realtime - starttime ) / 100;
// loop through the entire credits sequence
- for( n = 0; n <= sizeof(credits) - 1; n++ )
- {
- // this NULL string marks the end of the credits struct
- if ( credits[n].string == NULL )
- {
- endcount = y;
- if ( endcount <= 200 ) {
- endcount = 200;
- }
- UI_DrawNamedPic( 100, endcount, 465, 125, ART_BFPLOGO ); // BFP - Draw logo when it's the last credit and stop during 7 seconds
- if ( y < -(16*12) ) // credits sequence is completely off screen, note: 16 are 3 seconds if you multiply by 3, you add 1 second more to last (more or less)
- {
- trap_Cmd_ExecuteText(
- EXEC_APPEND,
- va( "s_musicvolume %f; quit\n", mvolume )
- );
- break; // end of credits
- }
- break;
- }
-
+ for( n = 0; credits[n].string != NULL; n++ )
+ {
if ( strlen( credits[n].string ) == 1 ) // spacer string, no need to draw
continue;
if ( y > -( PROP_HEIGHT * (1 / PROP_SMALL_SIZE_SCALE) ) ) {
@@ -327,6 +305,20 @@ static void ScrollingCredits_Draw( void )
break;
}
+ // end of credits
+ endcount = y;
+ if ( endcount <= 200 ) {
+ endcount = 200;
+ }
+ UI_DrawNamedPic( 100, endcount, 465, 125, ART_BFPLOGO ); // BFP - Draw logo when it's the last credit and stop during 7 seconds
+ if ( y < -(16*12) ) // credits sequence is completely off screen, note: 16 are 3 seconds if you multiply by 3, you add 1 second more to last (more or less)
+ {
+ trap_Cmd_ExecuteText(
+ EXEC_APPEND,
+ va( "s_musicvolume %f; quit\n", mvolume )
+ );
+ }
+
Credits_DrawHeader(); // BFP - Header
}
@@ -353,9 +345,5 @@ void UI_CreditMenu( void ) {
#endif
// load the background shader
-#ifdef BACKGROUND_SHADER
- BackgroundShader =
- trap_R_RegisterShaderNoMip( ART_MENUBG );
-#undef BACKGROUND_SHADER
-#endif
+ backgroundShader = trap_R_RegisterShaderNoMip( ART_MENUBG );
}
diff --git a/source/q3_ui/ui_demo2.c b/source/q3_ui/ui_demo2.c
index 81435c0..e00b24f 100644
--- a/source/q3_ui/ui_demo2.c
+++ b/source/q3_ui/ui_demo2.c
@@ -38,23 +38,28 @@ DEMOS MENU
#define ART_BACK1 "menu/art/back_1"
#define ART_GO0 "menu/art/play_0"
#define ART_GO1 "menu/art/play_1"
-#define ART_FRAMEL "menu/art/frame2_l"
-#define ART_FRAMER "menu/art/frame1_r"
-#define ART_ARROWS "menu/art/arrows_horz_0"
-#define ART_ARROWLEFT "menu/art/arrows_horz_left"
-#define ART_ARROWRIGHT "menu/art/arrows_horz_right"
+#define ART_ARROWS_VERT "menu/art/arrows_vert_0"
+#define ART_ARROWS_UP "menu/art/arrows_vert_top"
+#define ART_ARROWS_DOWN "menu/art/arrows_vert_bot"
-#define MAX_DEMOS 128
-#define NAMEBUFSIZE ( MAX_DEMOS * 16 )
+#define UI_DEMO_LENGTH 64
+#define UI_MAX_DEMOS 1024
+#define UI_MAX_ITEMS 18
+#define NAMEBUFSIZE 65536
#define ID_BACK 10
#define ID_GO 11
#define ID_LIST 12
-#define ID_RIGHT 13
-#define ID_LEFT 14
+#define ID_UP 13
+#define ID_DOWN 14
+#define ID_TIMEDEMO 15
+#define ID_SORT 16
+#define ID_FILTER 17
-#define ARROWS_WIDTH 128
-#define ARROWS_HEIGHT 64 // BFP - modified arrow height
+#define ARROWS_WIDTH 64
+#define ARROWS_HEIGHT 128 // BFP - modified arrow height
+#define ARROWS_TOP 240+4.75f
+#define ARROWS_LEFT 512+64
typedef struct {
@@ -65,20 +70,271 @@ typedef struct {
menutext_s banner;
menulist_s list;
+ menulist_s sort;
menubitmap_s arrows;
- menubitmap_s left;
- menubitmap_s right;
+ menubitmap_s up;
+ menubitmap_s down;
menubitmap_s back;
menubitmap_s go;
+ menuradiobutton_s timedemo;
+
+ menufield_s filter;
int numDemos;
- char names[NAMEBUFSIZE];
- char *demolist[MAX_DEMOS];
+ qboolean canPlay;
+ char *itemname[UI_MAX_DEMOS];
+ char namefilter[MAX_EDIT_LINE];
} demos_t;
static demos_t s_demos;
+typedef struct {
+ char *file_name;
+ int file_nlen;
+ char file_type;
+} demo_entry_t;
+
+static demo_entry_t dentry[UI_MAX_DEMOS]; // will be filled by UI_DemosReadDir()
+static demo_entry_t *dptr[UI_MAX_DEMOS]; // used for sorting
+static int num_files;
+static char dir[256] = { "demos" }; // current directory
+static int dirlevel = 0; // directory level
+static int listpos[64][2]; // saved list positions( curvalue, top )
+static char buffer[NAMEBUFSIZE]; // filename buffer
+static char show_names[UI_MAX_DEMOS][UI_DEMO_LENGTH*3];
+
+static const char* sorttype[] = {
+ "No",
+ "Name, asc",
+ "Name, desc",
+ 0
+};
+
+static void UI_DemosFillList( void );
+
+/*
+=================
+Demos_DrawFilter
+=================
+*/
+static void Demos_DrawFilter( void *self ) {
+ menufield_s *f;
+ qboolean focus;
+ int style;
+ char *txt;
+ char c;
+ float *color;
+ int basex, x, y;
+
+ f = (menufield_s*)self;
+ basex = f->generic.x;
+ y = f->generic.y;
+ focus = (f->generic.parent->cursor == f->generic.menuPosition);
+#if 0
+ UI_FillRect( f->generic.left, f->generic.top,
+ f->generic.right-f->generic.left,
+ f->generic.bottom-f->generic.top,
+ text_color_disabled );
+#endif
+ style = UI_LEFT|UI_SMALLFONT;
+ color = text_color_normal;
+
+ if( focus ) {
+ style |= UI_PULSE;
+ color = text_color_highlight;
+ }
+
+ UI_DrawString( basex, y, "Filter:", style, color );
+
+ basex += 64;
+ //y += PROP_HEIGHT;
+ txt = f->field.buffer;
+ color = g_color_table[ColorIndex(COLOR_WHITE)];
+ x = basex;
+
+ while ( (c = *txt) != 0 ) {
+ UI_DrawChar( x, y, c, style, color );
+ txt++;
+ x += SMALLCHAR_WIDTH;
+ }
+
+ if ( strcmp( s_demos.namefilter, f->field.buffer) ) {
+ strcpy( s_demos.namefilter, f->field.buffer );
+ UI_DemosFillList();
+ }
+
+ // draw cursor if we have focus
+ if( focus ) {
+ if ( trap_Key_GetOverstrikeMode() ) {
+ c = 11;
+ } else {
+ c = 10;
+ }
+
+ style &= ~UI_PULSE;
+ style |= UI_BLINK;
+
+ UI_DrawChar( basex + f->field.cursor * SMALLCHAR_WIDTH, y, c, style, color_white );
+ }
+}
+
+
+static void UI_DemosSavePosition( void ) {
+ if ( dirlevel < ARRAY_LEN( listpos ) ) {
+ listpos[dirlevel][0] = s_demos.list.curvalue;
+ listpos[dirlevel][1] = s_demos.list.top;
+ }
+}
+
+
+static void UI_DemosRestorePosition( void ) {
+ if ( dirlevel < ARRAY_LEN( listpos ) ) {
+ s_demos.list.curvalue = listpos[dirlevel][0];
+ s_demos.list.top = listpos[dirlevel][1];
+ }
+}
+
+
+/*
+===============
+UI_DemosReadDir
+===============
+*/
+static void UI_DemosReadDir( void )
+{
+ char extension[32], *s;
+ int i, len, n, c, m;
+
+ c = 0; // count of valid entries
+
+ // reserve room for ".."
+ if ( dirlevel > 0 )
+ c++;
+
+ // get directories first
+ n = trap_FS_GetFileList( dir, "/", buffer, sizeof( buffer ) );
+ if ( n > UI_MAX_DEMOS )
+ n = UI_MAX_DEMOS;
+ s = buffer;
+
+ for ( i = 0; i < n; i++ ) {
+ len = strlen( s );
+ // don't and "." and ".." entries for demo root dir
+ if ( !s[0] || ( s[0]== '.' && !s[1] ) || ( !strcmp( s, ".." ) && dirlevel == 0 ) ) {
+ s += len + 1;
+ continue;
+ }
+ if ( !strcmp( s, ".." ) ) {
+ dentry[0].file_type = 2;
+ dentry[0].file_name = s;
+ dentry[0].file_nlen = len;
+ } else {
+ dentry[c].file_type = 1;
+ dentry[c].file_name = s;
+ dentry[c].file_nlen = len;
+ c++;
+ }
+ s += len + 1;
+ }
+ num_files = c;
+ len = sizeof(buffer) - (s - buffer) - 1;
+ if ( len > 2 && num_files < UI_MAX_DEMOS-1 ) {
+ // count regular files
+ m = trap_FS_GetFileList( dir, "dm_??", s, len ); // try to perform pattern match in first place
+ if ( !m ) {
+ Com_sprintf( extension, sizeof( extension ), "dm_%d", (int)trap_Cvar_VariableValue( "protocol" ) );
+ m = trap_FS_GetFileList( dir, extension, s, len );
+ }
+ if ( num_files + m > UI_MAX_DEMOS )
+ m = UI_MAX_DEMOS - n;
+ for ( i = 0; i < m; i++, c++ ) {
+ len = strlen( s );
+ dentry[c].file_name = s;
+ dentry[c].file_nlen = len;
+ dentry[c].file_type = 0;
+ s += len + 1;
+ }
+ num_files += m;
+ }
+ s_demos.numDemos = num_files;
+}
+
+
+/*
+===============
+UI_UpdateDirectory
+===============
+*/
+static qboolean UI_UpdateDirectory( char *name ) {
+ char *s;
+
+ s_demos.namefilter[0] = '\0';
+ s_demos.filter.field.buffer[0] = '\0';
+ s_demos.filter.field.cursor = 0;
+
+ if ( !name || !*name ) {
+ s = Q_strrchr( dir, '/' );
+ if ( !s || s == dir ) {
+ dirlevel = 0;
+ return qfalse;
+ } else {
+ *s = '\0';
+ s = Q_strrchr( dir, '/' );
+ if ( dirlevel > 0 )
+ dirlevel--;
+ if ( !s || s == dir )
+ dirlevel = 0;
+ return qtrue;
+ }
+ } else {
+ Q_strcat( dir, sizeof( dir ), "/" );
+ Q_strcat( dir, sizeof( dir ), name );
+ UI_DemosSavePosition();
+ dirlevel++;
+ return qtrue;
+ }
+}
+
+
+/*
+===============
+UI_UpdateDirectory
+===============
+*/
+static void UI_DemoClick( void ) {
+ demo_entry_t *d;
+ char *s, *r;
+
+ if ( !s_demos.numDemos || !s_demos.canPlay )
+ return;
+
+ d = dptr[ s_demos.list.curvalue ];
+ if ( !d )
+ return;
+
+ if ( d->file_type > 0 ) {
+ if ( !strcmp( d->file_name, ".." ) ) {
+ UI_UpdateDirectory( NULL );
+ UI_DemosReadDir();
+ UI_DemosFillList();
+ UI_DemosRestorePosition();
+ } else {
+ UI_UpdateDirectory( d->file_name );
+ UI_DemosReadDir();
+ UI_DemosFillList();
+ }
+ } else {
+ UI_ForceMenuOff ();
+ s = strchr( dir, '/' );
+ if ( s ) {
+ r = va( "demo \"%s/%s\"\n", s + 1, d->file_name );
+ } else
+ r = va( "demo \"%s\"\n", d->file_name );
+ trap_Cmd_ExecuteText( EXEC_APPEND, r );
+ }
+}
+
/*
===============
@@ -92,24 +348,64 @@ static void Demos_MenuEvent( void *ptr, int event ) {
switch( ((menucommon_s*)ptr)->id ) {
case ID_GO:
- UI_ForceMenuOff ();
- trap_Cmd_ExecuteText( EXEC_APPEND, va( "demo %s\n",
- s_demos.list.itemnames[s_demos.list.curvalue]) );
+ UI_DemoClick();
break;
case ID_BACK:
UI_PopMenu();
break;
- case ID_LEFT:
- ScrollList_Key( &s_demos.list, K_LEFTARROW );
+ case ID_UP:
+ ScrollList_Key( &s_demos.list, K_UPARROW );
+ break;
+
+ case ID_DOWN:
+ ScrollList_Key( &s_demos.list, K_DOWNARROW );
break;
- case ID_RIGHT:
- ScrollList_Key( &s_demos.list, K_RIGHTARROW );
+ case ID_TIMEDEMO:
+ if ( s_demos.timedemo.curvalue )
+ trap_Cvar_Set( "timedemo", "1" );
+ else
+ trap_Cvar_Set( "timedemo", "0" );
break;
+
+ case ID_SORT:
+ trap_Cvar_Set( "ui_demoSort", va( "%i", s_demos.sort.curvalue ) );
+ UI_DemosFillList();
+ break;
+ }
+}
+
+static void UI_DemosDblclick( void *ptr ) {
+ UI_DemoClick();
+}
+
+#if 0
+static sfxHandle_t UI_DemosKeydown( void *ptr, int key ) {
+/*
+ if ( key == K_ENTER || key == K_KP_ENTER ) {
+ UI_DemoClick();
+ return menu_move_sound;
+ }
+ if ( key == K_BACKSPACE ) {
+ if ( UI_UpdateDirectory( NULL ) ) {
+ UI_DemosReadDir();
+ UI_DemosFillList();
+ UI_DemosRestorePosition();
+ }
+ return menu_move_sound;
}
+ if ( key == '/' ) {
+ Menu_SetCursorToItem( (menuframework_s *)&s_demos, &s_demos.filter );
+ s_demos.filter.field.skipKey = qtrue;
+ return menu_null_sound;
+ }
+*/
+ return 0;
+
}
+#endif
/*
@@ -122,19 +418,232 @@ static sfxHandle_t UI_DemosMenu_Key( int key ) {
item = Menu_ItemAtCursor( &s_demos.menu );
+ if ( item == (menucommon_s *)&s_demos.list ) {
+ if ( key == K_ENTER || key == K_KP_ENTER ) {
+ UI_DemoClick();
+ return menu_in_sound;
+ }
+ if ( key == K_BACKSPACE ) {
+ if ( UI_UpdateDirectory( NULL ) ) {
+ UI_DemosReadDir();
+ UI_DemosFillList();
+ UI_DemosRestorePosition();
+ }
+ return menu_in_sound;
+ }
+ if ( key == '/' ) {
+ Menu_SetCursorToItem( (menuframework_s *)&s_demos, &s_demos.filter );
+ s_demos.filter.field.skipKey = qtrue;
+ return menu_in_sound;
+ }
+ }
+
return Menu_DefaultKey( &s_demos.menu, key );
}
+// calculate length difference for color strings
+int UI_cstrdiff( char * str ) {
+ int diff;
+ if ( !str )
+ return 0;
+ diff = 0;
+ while ( *str ) {
+ if ( *str == Q_COLOR_ESCAPE && str[1] && str[2] ) {
+ diff += 2;
+ str += 2;
+ } else
+ str++;
+ }
+ return diff;
+}
+
+int UI_cstricmp( const char *src, const char *dst ) {
+ int ret = 0;
+ int c1 = 0, c2 = 0;
+ unsigned char ch1, ch2;
+ do {
+ if ( *src == '^' && src[1] ) {
+ c1 = src[1];
+ src += 2;
+ continue;
+ }
+
+ if ( *dst == '^' && dst[1] ) {
+ c2 = dst[1];
+ dst += 2;
+ continue;
+ }
+
+ ch1 = *src;
+ ch2 = *dst;
+
+ if ( ch1 >= 'a' && ch1 <= 'z' )
+ ch1 = ch1 - 'a' + 'A';
+ if ( ch2 >= 'a' && ch2 <= 'z' )
+ ch2 = ch2 - 'a' + 'A';
+
+ ret = ch1 - ch2;
+ if ( !ret )
+ ret = c1 - c2;
+
+ dst++;
+ if ( ret || !*dst )
+ break;
+
+ src++;
+
+ } while ( 1 );
+
+ if ( ret < 0 )
+ ret = -1;
+ else if ( ret > 0 )
+ ret = 1;
+
+ return( ret );
+}
+
+
+static int compare_entries( demo_entry_t * a, demo_entry_t *b, int mode )
+{
+ int t;
+ t = b->file_type - a->file_type;
+ if ( !t )
+ if ( mode == 1 )
+ return UI_cstricmp( a->file_name, b->file_name );
+ else
+ return -UI_cstricmp( a->file_name, b->file_name );
+
+ else
+ return t;
+}
+
+
+static void UI_demosort( demo_entry_t **a, int n, int mode )
+{
+ demo_entry_t * tmp;
+ int i = 0, j = n;
+ demo_entry_t *m = a[ n>>1 ];
+ do
+ {
+ while ( compare_entries( a[i], m, mode ) < 0 ) i++;
+ while ( compare_entries( a[j], m, mode ) > 0 ) j--;
+
+ if ( i <= j )
+ {
+ tmp = a[i];
+ a[i] = a[j];
+ a[j] = tmp;
+ i++;
+ j--;
+ }
+ }
+ while ( i <= j );
+ if ( j > 0 ) UI_demosort( a, j, mode );
+ if ( n > i ) UI_demosort( a+i, n-i, mode );
+}
+
+
+/*
+=================
+UI_DemosFillList
+=================
+*/
+static void UI_DemosFillList( void ) {
+ int i, diff, sort, len;
+ char matchname[256];
+ demo_entry_t *d;
+
+ if ( !s_demos.numDemos )
+ return;
+
+ sort = s_demos.sort.curvalue;
+
+ s_demos.list.numitems = 0;
+
+ for ( i = 0; i < s_demos.numDemos; i++ ) {
+ len = dentry[i].file_nlen;
+
+ if ( s_demos.namefilter[0] && strcmp( dentry[i].file_name, ".." ) ) {
+ strcpy( matchname, dentry[i].file_name );
+
+ // strip extension
+ if ( !Q_stricmp( matchname + len - 6, ".dm_68" ) )
+ matchname[ len-6 ] = '\0';
+
+ BG_StripColor( matchname );
+ if ( !Q_stristr( matchname, s_demos.namefilter ) ) {
+ continue;
+ }
+ }
+ dptr[ s_demos.list.numitems++ ] = &dentry[ i ];
+ if ( s_demos.list.numitems >= UI_MAX_DEMOS )
+ break;
+ }
+
+ if ( sort && s_demos.list.numitems > 1 )
+ UI_demosort( dptr, s_demos.list.numitems - 1, sort );
+
+ for ( i = 0; i < s_demos.list.numitems; i++ ) {
+ d = dptr[ i ];
+ s_demos.list.itemnames[i] = show_names[i];
+
+ Q_strncpyz( show_names[i], d->file_name, sizeof( show_names[i] ) );
+ if ( d->file_type > 0 ) {
+ Q_strcat( show_names[i], sizeof( show_names[0] ), "^7/" );
+ }
+
+ len = d->file_nlen;
+ // strip extension
+ if ( !Q_stricmp( show_names[i] + len - 6,".dm_68" ) ) {
+ memset( show_names[i] + len - 6, ' ', 6 );
+ len -= 6;
+ }
+
+ diff = UI_cstrdiff ( show_names[i] );
+
+ show_names[i][s_demos.list.width-1+diff] = '\0';
+
+ //Q_strupr( show_names[i] );
+
+ if ( len - diff > s_demos.list.width - 1 ) {
+ strcpy( &show_names[i][s_demos.list.width-1+diff], "^7>" );
+ }
+ }
+
+ s_demos.list.curvalue = 0;
+ s_demos.list.top = 0;
+
+ if ( !s_demos.list.numitems ) {
+ s_demos.list.itemnames[i] = show_names[0];
+ strcpy( show_names[0], "No files matching your request." );
+ s_demos.list.numitems = 1;
+ s_demos.list.curvalue = 1; // will remove selection
+ s_demos.canPlay = qfalse;
+ //degenerate case, not selectable
+ s_demos.list.generic.flags = QMF_INACTIVE;
+ s_demos.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE ;
+ } else {
+ s_demos.canPlay = qtrue;
+ s_demos.list.generic.flags = QMF_PULSEIFFOCUS;
+ s_demos.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ }
+
+ if ( s_demos.list.numitems > 1 ) {
+ s_demos.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_demos.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ } else {
+ s_demos.up.generic.flags = QMF_INACTIVE;
+ s_demos.down.generic.flags = QMF_INACTIVE;
+ }
+}
+
/*
===============
Demos_MenuInit
===============
*/
static void Demos_MenuInit( void ) {
- int i;
- int len;
- char *demoname, extension[32];
memset( &s_demos, 0 ,sizeof(demos_t) );
s_demos.menu.key = UI_DemosMenu_Key;
@@ -171,32 +680,32 @@ static void Demos_MenuInit( void ) {
s_demos.banner.style = UI_CENTER | UI_BIGFONT; // BFP - modified DEMOS style
s_demos.arrows.generic.type = MTYPE_BITMAP;
- s_demos.arrows.generic.name = ART_ARROWS;
+ s_demos.arrows.generic.name = ART_ARROWS_VERT;
s_demos.arrows.generic.flags = QMF_INACTIVE;
- s_demos.arrows.generic.x = 320-ARROWS_WIDTH/2;
- s_demos.arrows.generic.y = 400;
- s_demos.arrows.width = ARROWS_WIDTH;
- s_demos.arrows.height = ARROWS_HEIGHT;
-
- s_demos.left.generic.type = MTYPE_BITMAP;
- s_demos.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
- s_demos.left.generic.x = 320-ARROWS_WIDTH/2;
- s_demos.left.generic.y = 400;
- s_demos.left.generic.id = ID_LEFT;
- s_demos.left.generic.callback = Demos_MenuEvent;
- s_demos.left.width = ARROWS_WIDTH/2;
- s_demos.left.height = ARROWS_HEIGHT;
- s_demos.left.focuspic = ART_ARROWLEFT;
-
- s_demos.right.generic.type = MTYPE_BITMAP;
- s_demos.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
- s_demos.right.generic.x = 320;
- s_demos.right.generic.y = 400;
- s_demos.right.generic.id = ID_RIGHT;
- s_demos.right.generic.callback = Demos_MenuEvent;
- s_demos.right.width = ARROWS_WIDTH/2;
- s_demos.right.height = ARROWS_HEIGHT;
- s_demos.right.focuspic = ART_ARROWRIGHT;
+ s_demos.arrows.generic.x = 512+48+12;
+ s_demos.arrows.generic.y = 220-64+48;
+ s_demos.arrows.width = 64;
+ s_demos.arrows.height = 128;
+
+ s_demos.up.generic.type = MTYPE_BITMAP;
+ s_demos.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_demos.up.generic.x = 512+48+12;
+ s_demos.up.generic.y = 220-64+48;
+ s_demos.up.generic.id = ID_UP;
+ s_demos.up.generic.callback = Demos_MenuEvent;
+ s_demos.up.width = 64;
+ s_demos.up.height = 64;
+ s_demos.up.focuspic = ART_ARROWS_UP;
+
+ s_demos.down.generic.type = MTYPE_BITMAP;
+ s_demos.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_demos.down.generic.x = 512+48+12;
+ s_demos.down.generic.y = 220+48;
+ s_demos.down.generic.id = ID_DOWN;
+ s_demos.down.generic.callback = Demos_MenuEvent;
+ s_demos.down.width = 64;
+ s_demos.down.height = 64;
+ s_demos.down.focuspic = ART_ARROWS_DOWN;
s_demos.back.generic.type = MTYPE_BITMAP;
s_demos.back.generic.name = ART_BACK0;
@@ -220,41 +729,74 @@ static void Demos_MenuInit( void ) {
s_demos.go.height = 80; // BFP - modified GO button height
s_demos.go.focuspic = ART_GO1;
+ s_demos.timedemo.generic.type = MTYPE_RADIOBUTTON;
+ s_demos.timedemo.generic.name = "Time Demo:";
+ s_demos.timedemo.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_demos.timedemo.generic.callback = Demos_MenuEvent;
+ s_demos.timedemo.generic.id = ID_TIMEDEMO;
+ s_demos.timedemo.generic.x = 320+24-4;
+ s_demos.timedemo.generic.y = 480-42;
+
+ if ( (int)trap_Cvar_VariableValue("timedemo") )
+ s_demos.timedemo.curvalue = qtrue;
+ else
+ s_demos.timedemo.curvalue = qfalse;
+
+ s_demos.sort.generic.type = MTYPE_SPINCONTROL;
+ s_demos.sort.generic.name = "Sort:";
+ s_demos.sort.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_demos.sort.generic.callback = Demos_MenuEvent;
+ s_demos.sort.generic.id = ID_SORT;
+ //s_demos.sort.generic.x = 332;
+ //s_demos.sort.generic.y = 406;
+ s_demos.sort.generic.x = 485;
+ s_demos.sort.generic.y = 90;
+ s_demos.sort.itemnames = sorttype;
+ s_demos.sort.curvalue = (int)trap_Cvar_VariableValue( "ui_demoSort" );
+
+ if ( s_demos.sort.curvalue < 0 || s_demos.sort.curvalue > 2 )
+ s_demos.sort.curvalue = 0;
+
+ s_demos.filter.generic.type = MTYPE_FIELD;
+ s_demos.filter.generic.flags = QMF_NODEFAULTINIT;
+ s_demos.filter.generic.ownerdraw = Demos_DrawFilter;
+ s_demos.filter.generic.id = ID_FILTER;
+ s_demos.filter.field.widthInChars = 37;
+ s_demos.filter.field.maxchars = 37;
+ s_demos.filter.generic.x = 62;
+ s_demos.filter.generic.y = 90;
+ s_demos.filter.generic.left = 62;
+ //s_demos.filter.generic.right = 640 - 64;
+ s_demos.filter.generic.right = 430;
+ s_demos.filter.generic.top = 66;
+ s_demos.filter.generic.bottom = 94;
+
s_demos.list.generic.type = MTYPE_SCROLLLIST;
s_demos.list.generic.flags = QMF_PULSEIFFOCUS;
s_demos.list.generic.callback = Demos_MenuEvent;
s_demos.list.generic.id = ID_LIST;
- s_demos.list.generic.x = 118;
- s_demos.list.generic.y = 130;
- s_demos.list.width = 16;
- s_demos.list.height = 14;
- Com_sprintf(extension, sizeof(extension), "dm_%d", (int)trap_Cvar_VariableValue( "protocol" ) );
- s_demos.list.numitems = trap_FS_GetFileList( "demos", extension, s_demos.names, NAMEBUFSIZE );
- s_demos.list.itemnames = (const char **)s_demos.demolist;
- s_demos.list.columns = 3;
-
- if (!s_demos.list.numitems) {
- strcpy( s_demos.names, "No Demos Found." );
- s_demos.list.numitems = 1;
+ s_demos.list.generic.x = (640-UI_DEMO_LENGTH*SMALLCHAR_WIDTH)/2;
+ s_demos.list.generic.y = (510-UI_MAX_ITEMS*SMALLCHAR_HEIGHT)/2;
+ s_demos.list.width = UI_DEMO_LENGTH;
+ s_demos.list.height = UI_MAX_ITEMS;
+ s_demos.list.columns = 1;
+ s_demos.list.scroll = s_demos.list.height - 1;
+ s_demos.list.generic.dblclick = UI_DemosDblclick;
- //degenerate case, not selectable
- s_demos.go.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
- }
- else if (s_demos.list.numitems > MAX_DEMOS)
- s_demos.list.numitems = MAX_DEMOS;
+ s_demos.list.itemnames = (const char **)s_demos.itemname;
- demoname = s_demos.names;
- for ( i = 0; i < s_demos.list.numitems; i++ ) {
- s_demos.list.itemnames[i] = demoname;
-
- // strip extension
- len = (int)strlen( demoname );
- if (!Q_stricmp(demoname + len - 4,".dm3"))
- demoname[len-4] = '\0';
+ UI_DemosReadDir();
- Q_strupr(demoname);
+ if ( !s_demos.numDemos && dirlevel == 0 ) {
+ strcpy( buffer, "No Demos Found." );
+ s_demos.list.numitems = 1;
- demoname += len + 1;
+ //degenerate case, not selectable
+ s_demos.go.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ s_demos.up.generic.flags = QMF_INACTIVE;
+ s_demos.down.generic.flags = QMF_INACTIVE;
+ } else {
+ UI_DemosFillList();
}
Menu_AddItem( &s_demos.menu, &s_demos.menubg ); // BFP - Menu background
@@ -262,10 +804,13 @@ static void Demos_MenuInit( void ) {
Menu_AddItem( &s_demos.menu, &s_demos.banner ); // BFP - banner draws after
Menu_AddItem( &s_demos.menu, &s_demos.list );
Menu_AddItem( &s_demos.menu, &s_demos.arrows );
- Menu_AddItem( &s_demos.menu, &s_demos.left );
- Menu_AddItem( &s_demos.menu, &s_demos.right );
+ Menu_AddItem( &s_demos.menu, &s_demos.up );
+ Menu_AddItem( &s_demos.menu, &s_demos.down );
Menu_AddItem( &s_demos.menu, &s_demos.back );
Menu_AddItem( &s_demos.menu, &s_demos.go );
+ Menu_AddItem( &s_demos.menu, &s_demos.timedemo );
+ Menu_AddItem( &s_demos.menu, &s_demos.sort );
+ Menu_AddItem( &s_demos.menu, &s_demos.filter );
}
/*
@@ -280,9 +825,9 @@ void Demos_Cache( void ) {
trap_R_RegisterShaderNoMip( ART_GO1 );
trap_R_RegisterShaderNoMip( ART_MENUBG ); // BFP - Menu background
trap_R_RegisterShaderNoMip( ART_BARLOG ); // BFP - barlog
- trap_R_RegisterShaderNoMip( ART_ARROWS );
- trap_R_RegisterShaderNoMip( ART_ARROWLEFT );
- trap_R_RegisterShaderNoMip( ART_ARROWRIGHT );
+ trap_R_RegisterShaderNoMip( ART_ARROWS_VERT );
+ trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
+ trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
}
/*
diff --git a/source/q3_ui/ui_ingame.c b/source/q3_ui/ui_ingame.c
index e5b1e3f..5132402 100644
--- a/source/q3_ui/ui_ingame.c
+++ b/source/q3_ui/ui_ingame.c
@@ -48,6 +48,8 @@ INGAME MENU
#define ID_QUIT 17
#define ID_RESUME 18
#define ID_TEAMORDERS 19
+// https://github.com/clover-moe/mint-arena/pull/7
+#define ID_CREATE 20
typedef struct {
@@ -63,6 +65,7 @@ typedef struct {
menutext_s server;
menutext_s leave;
menutext_s restart;
+ menutext_s startnew;
menutext_s addbots;
menutext_s removebots;
menutext_s teamorders;
@@ -133,6 +136,10 @@ void InGame_Event( void *ptr, int notification ) {
UI_ConfirmMenu( "RESTART ARENA?", (voidfunc_f)NULL, InGame_RestartAction );
break;
+ case ID_CREATE:
+ UI_StartServerMenu( qtrue );
+ break;
+
case ID_QUIT:
UI_ConfirmMenu( "EXIT GAME?", (voidfunc_f)NULL, InGame_QuitAction );
break;
@@ -184,7 +191,7 @@ void InGame_MenuInit( void ) {
s_ingame.frame.generic.x = 320-233;//142;
s_ingame.frame.generic.y = 240-166;//118;
s_ingame.frame.width = 466;//359;
- s_ingame.frame.height = 332;//256;
+ s_ingame.frame.height = 332+24;//256;
// BFP - barlog
s_ingame.barlog.generic.type = MTYPE_BITMAP;
@@ -299,6 +306,20 @@ void InGame_MenuInit( void ) {
s_ingame.server.color = color_white; // BFP - modified SERVER INFO button color
s_ingame.server.style = UI_CENTER|UI_SMALLFONT;
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.startnew.generic.type = MTYPE_PTEXT;
+ s_ingame.startnew.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.startnew.generic.x = 320;
+ s_ingame.startnew.generic.y = y;
+ s_ingame.startnew.generic.id = ID_CREATE;
+ s_ingame.startnew.generic.callback = InGame_Event;
+ s_ingame.startnew.string = "CHANGE MAP";
+ s_ingame.startnew.color = color_white;
+ s_ingame.startnew.style = UI_CENTER|UI_SMALLFONT;
+ if( !trap_Cvar_VariableValue( "sv_running" ) ) {
+ s_ingame.startnew.generic.flags |= QMF_GRAYED;
+ }
+
y += INGAME_MENU_VERTICAL_SPACING;
s_ingame.restart.generic.type = MTYPE_PTEXT;
s_ingame.restart.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
@@ -356,6 +377,7 @@ void InGame_MenuInit( void ) {
Menu_AddItem( &s_ingame.menu, &s_ingame.teamorders );
Menu_AddItem( &s_ingame.menu, &s_ingame.setup );
Menu_AddItem( &s_ingame.menu, &s_ingame.server );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.startnew );
Menu_AddItem( &s_ingame.menu, &s_ingame.restart );
Menu_AddItem( &s_ingame.menu, &s_ingame.resume );
Menu_AddItem( &s_ingame.menu, &s_ingame.leave );
diff --git a/source/q3_ui/ui_local.h b/source/q3_ui/ui_local.h
index cb36f90..fca68e1 100644
--- a/source/q3_ui/ui_local.h
+++ b/source/q3_ui/ui_local.h
@@ -126,6 +126,7 @@ typedef struct
void (*callback)( void *self, int event );
void (*statusbar)( void *self );
void (*ownerdraw)( void *self );
+ void (*dblclick)( void *self );
} menucommon_s;
typedef struct {
@@ -134,6 +135,7 @@ typedef struct {
int widthInChars;
char buffer[MAX_EDIT_LINE];
int maxchars;
+ qboolean skipKey; // skip single key event
} mfield_t;
typedef struct
@@ -161,6 +163,8 @@ typedef struct
int curvalue;
int numitems;
int top;
+ int scroll;
+ int mouse1time;
const char **itemnames;
@@ -249,6 +253,11 @@ extern char *ui_medalPicNames[];
extern char *ui_medalSounds[];
#endif
+//
+// ui_main.c
+//
+extern void UI_VideoCheck( int time );
+
//
// ui_mfield.c
//
@@ -450,9 +459,10 @@ typedef struct {
animation_t animations[MAX_ANIMATIONS];
- qhandle_t weaponModel;
- qhandle_t barrelModel;
- qhandle_t flashModel;
+ // BFP - No handsModel, weaponModel and barrelModel
+ //qhandle_t weaponModel;
+ //qhandle_t barrelModel;
+ //qhandle_t flashModel;
vec3_t flashDlightColor;
int muzzleFlashTime;
@@ -495,8 +505,8 @@ qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName
typedef struct {
int frametime;
int realtime;
- int cursorx;
- int cursory;
+ float cursorx;
+ float cursory;
int menusp;
menuframework_s* activemenu;
menuframework_s* stack[MAX_MENUDEPTH];
@@ -512,10 +522,22 @@ typedef struct {
qhandle_t cursor;
qhandle_t rb_on;
qhandle_t rb_off;
+
float scale;
- float bias;
+ float biasX;
+ float biasY;
+
+ float cursorScaleR; // clamped 1/scale for mouse
+
+ float screenXmin;
+ float screenXmax;
+
+ float screenYmin;
+ float screenYmax;
+
qboolean demoversion;
qboolean firstdraw;
+ int lastVideoCheck;
} uiStatic_t;
extern void UI_Init( void );
@@ -611,7 +633,7 @@ void trap_FS_Read( void *buffer, int len, fileHandle_t f );
void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
void trap_FS_FCloseFile( fileHandle_t f );
int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize );
-int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t
+int trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin );
qhandle_t trap_R_RegisterModel( const char *name );
qhandle_t trap_R_RegisterSkin( const char *name );
qhandle_t trap_R_RegisterShaderNoMip( const char *name );
@@ -643,6 +665,8 @@ int trap_LAN_GetServerCount( int source );
void trap_LAN_GetServerAddressString( int source, int n, char *buf, int buflen );
void trap_LAN_GetServerInfo( int source, int n, char *buf, int buflen );
int trap_LAN_GetPingQueueCount( void );
+void trap_LAN_SaveCachedServers( void );
+void trap_LAN_LoadCachedServers( void );
int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen );
void trap_LAN_ClearPing( int n );
void trap_LAN_GetPing( int n, char *buf, int buflen, int *pingtime );
@@ -651,7 +675,7 @@ int trap_MemoryRemaining( void );
void trap_GetCDKey( char *buf, int buflen );
void trap_SetCDKey( char *buf );
-qboolean trap_VerifyCDKey( const char *key, const char *chksum); // bk001208 - RC4
+qboolean trap_VerifyCDKey( const char *key, const char *chksum); // bk001208 - RC4
void trap_SetPbClStatus( int status );
diff --git a/source/q3_ui/ui_main.c b/source/q3_ui/ui_main.c
index 0453a92..5c38b19 100644
--- a/source/q3_ui/ui_main.c
+++ b/source/q3_ui/ui_main.c
@@ -40,7 +40,7 @@ This is the only way control passes into the module.
This must be the very first function compiled into the .qvm file
================
*/
-intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) {
+DLLEXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) {
switch ( command ) {
case UI_GETAPIVERSION:
return UI_API_VERSION;
@@ -115,6 +115,10 @@ static cvarTable_t cvarTable[] = {
};
+// bk001129 - made static to avoid aliasing
+static int cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]);
+
+
/*
=================
UI_RegisterCvars
@@ -124,7 +128,7 @@ void UI_RegisterCvars( void ) {
int i;
cvarTable_t *cv;
- for ( i = 0, cv = cvarTable ; i < ARRAY_LEN( cvarTable ) ; i++, cv++ ) {
+ for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) {
trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags );
}
}
@@ -138,7 +142,53 @@ void UI_UpdateCvars( void ) {
int i;
cvarTable_t *cv;
- for ( i = 0, cv = cvarTable ; i < ARRAY_LEN( cvarTable ) ; i++, cv++ ) {
+ for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) {
trap_Cvar_Update( cv->vmCvar );
}
}
+
+
+/*
+=================
+UI_VideoCheck
+=================
+*/
+void UI_VideoCheck( int time )
+{
+ if ( abs( time - uis.lastVideoCheck ) > 1000 ) {
+
+ int oldWidth, oldHeight;
+ oldWidth = uis.glconfig.vidWidth;
+ oldHeight = uis.glconfig.vidHeight;
+
+ trap_GetGlconfig( &uis.glconfig );
+
+ if ( uis.glconfig.vidWidth != oldWidth || uis.glconfig.vidHeight != oldHeight ) {
+ uis.biasY = 0.0;
+ uis.biasX = 0.0;
+ // for 640x480 virtualized screen
+ if ( uis.glconfig.vidWidth * 480 > uis.glconfig.vidHeight * 640 ) {
+ // wide screen, scale by height
+ uis.scale = uis.glconfig.vidHeight * (1.0/480.0);
+ uis.biasX = 0.5 * ( uis.glconfig.vidWidth - ( uis.glconfig.vidHeight * (640.0/480.0) ) );
+ } else {
+ // no wide screen, scale by width
+ uis.scale = uis.glconfig.vidWidth * (1.0/640.0);
+ uis.biasY = 0.5 * ( uis.glconfig.vidHeight - ( uis.glconfig.vidWidth * (480.0/640) ) );
+ }
+
+ uis.screenXmin = 0.0 - (uis.biasX / uis.scale);
+ uis.screenXmax = 640.0 + (uis.biasX / uis.scale);
+
+ uis.screenYmin = 0.0 - (uis.biasY / uis.scale);
+ uis.screenYmax = 480.0 + (uis.biasY / uis.scale);
+
+ uis.cursorScaleR = 1.0 / uis.scale;
+ if ( uis.cursorScaleR < 0.5 ) {
+ uis.cursorScaleR = 0.5;
+ }
+ }
+
+ uis.lastVideoCheck = time;
+ }
+}
diff --git a/source/q3_ui/ui_mem.c b/source/q3_ui/ui_mem.c
index 18d0b98..a4fd45e 100644
--- a/source/q3_ui/ui_mem.c
+++ b/source/q3_ui/ui_mem.c
@@ -37,7 +37,7 @@ void *UI_AllocMem( int size )
char *endptr;
int *ptr;
- allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary
+ allocsize = ( size + sizeof(int) + ROUNDBITS ) & (unsigned long)~ROUNDBITS; // Round to 32-byte boundary
ptr = NULL;
smallest = NULL;
diff --git a/source/q3_ui/ui_menu.c b/source/q3_ui/ui_menu.c
index b5b315c..37eb1e7 100644
--- a/source/q3_ui/ui_menu.c
+++ b/source/q3_ui/ui_menu.c
@@ -53,11 +53,12 @@ MAIN MENU
#define ID_UNPACKMUSIC 14 // BFP - Unpack music, strange feature (· ·;) *curiosity sweat*
// BFP - That's the main banner 3d model of Quake 3 words, it would be a good idea to add a proper 3d banner model :P
-#if 0
+#define ENABLE_BANNER_MODEL 0
+#if ENABLE_BANNER_MODEL
#define MAIN_BANNER_MODEL "models/mapobjects/banner/banner5.md3"
-#define MAIN_MENU_VERTICAL_SPACING 34
#endif
-#define MAIN_MENU_VERTICAL_SPACING 65 // BFP - Set vertical spacing to 65
+
+#define MAIN_MENU_VERTICAL_SPACING 65 // BFP - Set vertical spacing to 65, before Q3: 34
typedef struct {
menuframework_s menu;
@@ -87,7 +88,9 @@ typedef struct {
menutext_s exit;
// BFP - As said before, idea for a proper 3d banner model
- // qhandle_t bannerModel;
+#if ENABLE_BANNER_MODEL
+ qhandle_t bannerModel;
+#endif
} mainmenu_t;
@@ -217,10 +220,12 @@ MainMenu_Cache
*/
void MainMenu_Cache( void ) {
// BFP - Here's where sets the 3d banner model :P
- // s_main.bannerModel = trap_R_RegisterModel( MAIN_BANNER_MODEL );
- trap_R_RegisterShaderNoMip( ART_MENUBG ); // BFP - Menu background
- trap_R_RegisterShaderNoMip( ART_BFPLOGO ); // BFP - Logo
- trap_R_RegisterShaderNoMip( ART_CRBANNER ); // BFP - copyright banner
+#if ENABLE_BANNER_MODEL
+ s_main.bannerModel = trap_R_RegisterModel( MAIN_BANNER_MODEL );
+#endif
+ s_main.menubg.shader = trap_R_RegisterShaderNoMip( ART_MENUBG ); // BFP - Menu background
+ s_main.bfplogo.shader = trap_R_RegisterShaderNoMip( ART_BFPLOGO ); // BFP - Logo
+ s_main.crbanner.shader = trap_R_RegisterShaderNoMip( ART_CRBANNER ); // BFP - copyright banner
}
sfxHandle_t ErrorMessage_Key(int key)
@@ -230,26 +235,24 @@ sfxHandle_t ErrorMessage_Key(int key)
return (menu_null_sound);
}
+
+// BFP - 3d banner model :P
+#if ENABLE_BANNER_MODEL
/*
-===============
-Main_MenuDraw
-TTimo: this function is common to the main menu and errorMessage menu
-===============
+========================
+Main_MenuBannerModelDraw
+========================
*/
-
-static void Main_MenuDraw( void ) {
+static void Main_MenuBannerModelDraw( void )
+{
refdef_t refdef;
-
- // BFP - 3d banner model disabled :P
-#if 0
refEntity_t ent;
vec3_t origin;
vec3_t angles;
vec4_t color = {0.5, 0, 0, 1};
-#endif
-
float adjust;
float x, y, w, h;
+ const int LOW_MEMORY = 5242880;
// setup the refdef
@@ -260,55 +263,88 @@ static void Main_MenuDraw( void ) {
AxisClear( refdef.viewaxis );
x = 0;
- y = 0;
+ y = -25; // BFP - Q3: 0;
w = 640;
- h = 120;
+ h = 480; // BFP - Q3: 120;
UI_AdjustFrom640( &x, &y, &w, &h );
refdef.x = x;
refdef.y = y;
refdef.width = w;
refdef.height = h;
+ if ( trap_MemoryRemaining() <= LOW_MEMORY ) {
+ UI_DrawProportionalString( x, y + h / 2, "LOW MEMORY", UI_LEFT, color_white ); // BFP - modified LOW MEMORY warning color
+ return;
+ }
+
adjust = 0; // JDC: Kenneth asked me to stop this 1.0 * sin( (float)uis.realtime / 1000 );
- refdef.fov_x = 60 + adjust;
- refdef.fov_y = 19.6875 + adjust;
+ refdef.fov_x = 20; // BFP - Q3: 60 + adjust;
+ refdef.fov_y = 15 + adjust; // BFP - Q3: 19.6875 + adjust;
refdef.time = uis.realtime;
- // BFP - 3d banner model disabled :P
-#if 0
- origin[0] = 300;
- origin[1] = 0;
- origin[2] = -32;
+ if ( s_main.bannerModel ) {
+ origin[0] = 640; // BFP - Q3: 300;
+ origin[1] = 0;
+ origin[2] = 0; // BFP - Q3: -32;
- trap_R_ClearScene();
+ trap_R_ClearScene();
- // add the model
+ // add the model
- memset( &ent, 0, sizeof(ent) );
+ memset( &ent, 0, sizeof(ent) );
- adjust = 5.0 * sin( (float)uis.realtime / 5000 );
- VectorSet( angles, 0, 180 + adjust, 0 );
- AnglesToAxis( angles, ent.axis );
- ent.hModel = s_main.bannerModel;
- VectorCopy( origin, ent.origin );
- VectorCopy( origin, ent.lightingOrigin );
- ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
- VectorCopy( ent.origin, ent.oldorigin );
+ adjust = 0; // BFP - Q3: 5.0 * sin( (float)uis.realtime / 5000 );
+ VectorSet( angles, 0, 180 + adjust, 0 );
+ AnglesToAxis( angles, ent.axis );
+ ent.hModel = s_main.bannerModel;
+ VectorCopy( origin, ent.origin );
+ VectorCopy( origin, ent.lightingOrigin );
+ ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
+ VectorCopy( ent.origin, ent.oldorigin );
- trap_R_AddRefEntityToScene( &ent );
+ trap_R_AddRefEntityToScene( &ent );
- trap_R_RenderScene( &refdef );
+ trap_R_RenderScene( &refdef );
+ }
+}
#endif
- if (strlen(s_errorMessage.errorMessage))
+/*
+===============
+Main_MenuDraw
+TTimo: this function is common to the main menu and errorMessage menu
+===============
+*/
+
+static void Main_MenuDraw( void ) {
+ if (s_errorMessage.errorMessage[0] != '\0')
{
UI_DrawProportionalString_AutoWrapped( 320, 192, 600, 20, s_errorMessage.errorMessage, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color );
}
else
{
+ // BFP - 3d banner model :P
+#if ENABLE_BANNER_MODEL
+ // BFP - Menu background
+ s_main.menubg.generic.type = MTYPE_BITMAP;
+ s_main.menubg.generic.name = ART_MENUBG;
+ s_main.menubg.generic.flags = QMF_LEFT_JUSTIFY | QMF_INACTIVE;
+ s_main.menubg.generic.x = 0;
+ s_main.menubg.generic.y = 0;
+ s_main.menubg.width = 640;
+ s_main.menubg.height = 480;
+
+ // draw background first
+ UI_DrawHandlePic( s_main.menubg.generic.x, s_main.menubg.generic.y,
+ s_main.menubg.width, s_main.menubg.height,
+ s_main.menubg.shader );
+
+ // BFP - Draw 3d BFP banner model behind buttons and title
+ Main_MenuBannerModelDraw();
+#endif
// standard menu drawing
- Menu_Draw( &s_main.menu );
+ Menu_Draw( &s_main.menu );
}
// BFP - Demo version disabled, possibly we may remove that :P
@@ -359,7 +395,7 @@ void UI_MainMenu( void ) {
MainMenu_Cache();
trap_Cvar_VariableStringBuffer( "com_errorMessage", s_errorMessage.errorMessage, sizeof(s_errorMessage.errorMessage) );
- if (strlen(s_errorMessage.errorMessage))
+ if (s_errorMessage.errorMessage[0] != '\0')
{
s_errorMessage.menu.draw = Main_MenuDraw;
s_errorMessage.menu.key = ErrorMessage_Key;
@@ -551,10 +587,16 @@ void UI_MainMenu( void ) {
s_main.unpackmusic.color = color_white;
s_main.unpackmusic.style = style;
+ // BFP - For 3d banner model :P
+#if !ENABLE_BANNER_MODEL
Menu_AddItem( &s_main.menu, &s_main.menubg ); // BFP - Menu background
+#endif
Menu_AddItem( &s_main.menu, &s_main.bfplogo ); // BFP - Logo
- Menu_AddItem( &s_main.menu, &s_main.crbanner ); // BFP - copyright banner
+ // BFP - copyright banner
+ if ( s_main.crbanner.shader ) { // if doesn't exist, don't show that box
+ Menu_AddItem( &s_main.menu, &s_main.crbanner );
+ }
// Menu_AddItem( &s_main.menu, &s_main.singleplayer ); // BFP - As said before, if anyone has an idea in their mind about Single Player stuff
Menu_AddItem( &s_main.menu, &s_main.capbar0 ); // BFP - cap bar for PLAY(MULTIPLAYER) button
diff --git a/source/q3_ui/ui_mfield.c b/source/q3_ui/ui_mfield.c
index 0d096c6..6130df5 100644
--- a/source/q3_ui/ui_mfield.c
+++ b/source/q3_ui/ui_mfield.c
@@ -39,7 +39,7 @@ void MField_Draw( mfield_t *edit, int x, int y, int style, vec4_t color ) {
char str[MAX_STRING_CHARS];
drawLen = edit->widthInChars;
- len = (int)strlen( edit->buffer ) + 1;
+ len = strlen( edit->buffer ) + 1;
// guarantee that cursor will be visible
if ( len <= drawLen ) {
@@ -96,12 +96,12 @@ void MField_Draw( mfield_t *edit, int x, int y, int style, vec4_t color ) {
if (style & UI_CENTER)
{
- len = (int)strlen(str);
+ len = strlen(str);
x = x - len*charw/2;
}
else if (style & UI_RIGHT)
{
- len = (int)strlen(str);
+ len = strlen(str);
x = x - len*charw;
}
@@ -120,7 +120,7 @@ void MField_Paste( mfield_t *edit ) {
trap_GetClipboardData( pasteBuffer, 64 );
// send as if typed, so insert / overstrike works properly
- pasteLen = (int)strlen( pasteBuffer );
+ pasteLen = strlen( pasteBuffer );
for ( i = 0 ; i < pasteLen ; i++ ) {
MField_CharEvent( edit, pasteBuffer[i] );
}
@@ -145,7 +145,7 @@ void MField_KeyDownEvent( mfield_t *edit, int key ) {
return;
}
- len = (int)strlen( edit->buffer );
+ len = strlen( edit->buffer );
if ( key == K_DEL || key == K_KP_DEL ) {
if ( edit->cursor < len ) {
@@ -217,7 +217,7 @@ void MField_CharEvent( mfield_t *edit, int ch ) {
return;
}
- len = (int)strlen( edit->buffer );
+ len = strlen( edit->buffer );
if ( ch == 'h' - 'a' + 1 ) { // ctrl-h is backspace
if ( edit->cursor > 0 ) {
@@ -249,11 +249,11 @@ void MField_CharEvent( mfield_t *edit, int ch ) {
//
// ignore any other non printable chars
//
- if ( ch < 32 ) {
+ if ( ch < ' ' ) {
return;
}
- if ( !trap_Key_GetOverstrikeMode() ) {
+ if ( trap_Key_GetOverstrikeMode() ) {
if ((edit->cursor == MAX_EDIT_LINE - 1) || (edit->maxchars && edit->cursor >= edit->maxchars))
return;
} else {
@@ -273,7 +273,7 @@ void MField_CharEvent( mfield_t *edit, int ch ) {
}
if ( edit->cursor == len + 1) {
- edit->buffer[edit->cursor] = 0;
+ edit->buffer[edit->cursor] = '\0';
}
}
@@ -283,7 +283,7 @@ MField_Clear
==================
*/
void MField_Clear( mfield_t *edit ) {
- edit->buffer[0] = 0;
+ edit->buffer[0] = '\0';
edit->cursor = 0;
edit->scroll = 0;
}
@@ -312,7 +312,7 @@ void MenuField_Init( menufield_s* m ) {
}
if (m->generic.name) {
- l = ((int)strlen( m->generic.name )+1) * w;
+ l = (strlen( m->generic.name )+1) * w;
}
else {
l = 0;
@@ -334,7 +334,7 @@ void MenuField_Draw( menufield_s *f )
int x;
int y;
int w;
- int h;
+ //int h;
int style;
qboolean focus;
float *color;
@@ -345,13 +345,13 @@ void MenuField_Draw( menufield_s *f )
if (f->generic.flags & QMF_SMALLFONT)
{
w = SMALLCHAR_WIDTH;
- h = SMALLCHAR_HEIGHT;
+ //h = SMALLCHAR_HEIGHT;
style = UI_SMALLFONT;
}
else
{
w = BIGCHAR_WIDTH;
- h = BIGCHAR_HEIGHT;
+ //h = BIGCHAR_HEIGHT;
style = UI_BIGFONT;
}
@@ -395,6 +395,11 @@ sfxHandle_t MenuField_Key( menufield_s* m, int* key )
keycode = *key;
+ if ( m->field.skipKey ) {
+ m->field.skipKey = qfalse;
+ return (0);
+ }
+
switch ( keycode )
{
case K_KP_ENTER:
diff --git a/source/q3_ui/ui_mp3decoder.c b/source/q3_ui/ui_mp3decoder.c
index da95f19..47e89dd 100644
--- a/source/q3_ui/ui_mp3decoder.c
+++ b/source/q3_ui/ui_mp3decoder.c
@@ -120,10 +120,10 @@ void initLayer3( int down_sample_sblimit )
int i, j, k, l;
for( i = -256; i < 118 + 4 ; i++ )
- gainpow2[ i + 256 ] = pow( (float)2.0, -0.25 * (float)( i + 210 ) );
+ gainpow2[ i + 256 ] = mp3dec_pow( (float)2.0, -0.25 * (float)( i + 210 ) );
for( i = 0; i < 8207; i++ )
- ispow[ i ] = pow( (float)i, (float)4.0 / 3.0 );
+ ispow[ i ] = mp3dec_pow( (float)i, (float)4.0 / 3.0 );
for ( i = 0; i < 8; i++ )
{
@@ -191,15 +191,15 @@ void initLayer3( int down_sample_sblimit )
for( j = 0 ; j < 2; j++ )
{
- float base = pow( 2.0, -0.25 * ( j + 1.0 ) );
+ float base = mp3dec_pow( 2.0, -0.25 * ( j + 1.0 ) );
float p1 = 1.0, p2 = 1.0;
if( i > 0 )
{
if( i & 1 )
- p1 = pow( base, ( i + 1.0 ) * 0.5 );
+ p1 = mp3dec_pow( base, ( i + 1.0 ) * 0.5 );
else
- p2 = pow( base, i * 0.5 );
+ p2 = mp3dec_pow( base, i * 0.5 );
}
pow1_1[ j ][ i ] = p1;
pow2_1[ j ][ i ] = p2;
@@ -1223,7 +1223,7 @@ static decoderError_t III_dequantize_sample( float xr[ SBLIMIT ][ SSLIMIT ], int
/*
* zero part
*/
- for( i = ( &xr[ SBLIMIT ][ 0 ] - xrpnt ) >> 1; i; i-- )
+ for( i = ( (xr[0] + SBLIMIT * SSLIMIT) - xrpnt ) >> 1; i; i-- )
{
*xrpnt++ = 0.0;
*xrpnt++ = 0.0;
@@ -2617,31 +2617,40 @@ static decoderError_t decodeMP3( mpegPacket_t *mp, char *in, int isize, char *ou
if( osize < 4608 )
{
Com_Printf( "mp3dec: not enough data to decode\n" );
+ gmp = NULL;
return DE_ERR;
}
if( in )
{
- if( addBuffer( mp, in, isize ) == NULL)
+ if( addBuffer( mp, in, isize ) == NULL) {
+ gmp = NULL;
return DE_ERR;
+ }
}
/* First decode header */
if( mp->framesize == 0 )
{
- if( mp->bsize < 4 )
+ if( mp->bsize < 4 ) {
+ gmp = NULL;
return DE_NEED_MORE_DATA;
+ }
readHead( mp );
- if( decodeHeader( &mp->fr, mp->header ) != DE_OK )
+ if( decodeHeader( &mp->fr, mp->header ) != DE_OK ) {
+ gmp = NULL;
return DE_ERR;
+ }
mp->framesize = mp->fr.framesize;
}
- if( mp->fr.framesize > mp->bsize )
+ if( mp->fr.framesize > mp->bsize ) {
+ gmp = NULL;
return DE_NEED_MORE_DATA;
+ }
wordpointer = mp->bsspace[ mp->bsnum ] + 512;
mp->bsnum = ( mp->bsnum + 1 ) & 0x1;
@@ -2671,12 +2680,14 @@ static decoderError_t decodeMP3( mpegPacket_t *mp, char *in, int isize, char *ou
if( mp->fr.error_protection )
getbits( 16 );
- if( doLayer3( &mp->fr, (unsigned char *)out, done ) != DE_OK )
+ if( doLayer3( &mp->fr, (unsigned char *)out, done ) != DE_OK ) {
+ gmp = NULL;
return DE_ERR;
+ }
mp->fsizeold = mp->framesize;
mp->framesize = 0;
-
+ gmp = NULL;
return DE_OK;
}
@@ -2756,11 +2767,11 @@ static void wavWriteHeader( fileHandle_t wavFh, long int size, unsigned int bits
wave.common.wBitsPerSample = bits;
wave.common.dwSamplesPerSec = rate;
- strncpy( (char *)wave.riff.id, "RIFF", 4 );
+ Q_strncpyz( (char *)wave.riff.id, "RIFF", 4 );
wave.riff.len = size - 8;
- strncpy( (char *)wave.riff.wave_id, "WAVE", 4 );
+ Q_strncpyz( (char *)wave.riff.wave_id, "WAVE", 4 );
- strncpy( (char *)wave.format.id, "fmt ", 4 );
+ Q_strncpyz( (char *)wave.format.id, "fmt ", 4 );
wave.format.len = 16;
wave.common.wFormatTag = WAVE_FORMAT_PCM;
@@ -2771,14 +2782,14 @@ static void wavWriteHeader( fileHandle_t wavFh, long int size, unsigned int bits
wave.common.wBlockAlign = wave.common.wChannels *
( wave.common.wBitsPerSample >> 3 );
- strncpy( (char *)wave.data.id, "data", 4 );
+ Q_strncpyz( (char *)wave.data.id, "data", 4 );
wave.data.len = size - 44;
- strncpy( (char *)buf, (char *)wave.riff.id, 4 );
+ Q_strncpyz( (char *)buf, (char *)wave.riff.id, 4 );
WRITE_U32( buf + 4, wave.riff.len );
- strncpy( (char *)buf + 8, (char *)wave.riff.wave_id, 4 );
- strncpy( (char *)buf + 12, (char *)wave.format.id, 4 );
+ Q_strncpyz( (char *)buf + 8, (char *)wave.riff.wave_id, 4 );
+ Q_strncpyz( (char *)buf + 12, (char *)wave.format.id, 4 );
WRITE_U32( buf + 16, wave.format.len );
WRITE_U16( buf + 20, wave.common.wFormatTag );
WRITE_U16( buf + 22, wave.common.wChannels );
@@ -2786,7 +2797,7 @@ static void wavWriteHeader( fileHandle_t wavFh, long int size, unsigned int bits
WRITE_U32( buf + 28, wave.common.dwAvgBytesPerSec );
WRITE_U16( buf + 32, wave.common.wBlockAlign );
WRITE_U16( buf + 34, wave.common.wBitsPerSample );
- strncpy( (char *)buf + 36, (char *)wave.data.id, 4 );
+ Q_strncpyz( (char *)buf + 36, (char *)wave.data.id, 4 );
WRITE_U32( buf + 40, wave.data.len );
//seek to beginning of file
diff --git a/source/q3_ui/ui_playermodel.c b/source/q3_ui/ui_playermodel.c
index 3989b55..42d7bbf 100644
--- a/source/q3_ui/ui_playermodel.c
+++ b/source/q3_ui/ui_playermodel.c
@@ -188,7 +188,7 @@ static void PlayerModel_UpdateGrid( void )
{
// model/skin portrait
s_playermodel.pics[i].generic.name = s_playermodel.modelnames[j];
- s_playermodel.picbuttons[i].generic.flags &= ~QMF_INACTIVE;
+ s_playermodel.picbuttons[i].generic.flags &= (unsigned int)~QMF_INACTIVE;
}
else
{
@@ -197,7 +197,7 @@ static void PlayerModel_UpdateGrid( void )
s_playermodel.picbuttons[i].generic.flags |= QMF_INACTIVE;
}
- s_playermodel.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ s_playermodel.pics[i].generic.flags &= (unsigned int)~QMF_HIGHLIGHT;
s_playermodel.pics[i].shader = 0;
s_playermodel.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
}
@@ -208,18 +208,18 @@ static void PlayerModel_UpdateGrid( void )
i = s_playermodel.selectedmodel % MAX_MODELSPERPAGE;
s_playermodel.pics[i].generic.flags |= QMF_HIGHLIGHT;
- s_playermodel.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_playermodel.picbuttons[i].generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
}
if (s_playermodel.numpages > 1)
{
if (s_playermodel.modelpage > 0)
- s_playermodel.left.generic.flags &= ~QMF_INACTIVE;
+ s_playermodel.left.generic.flags &= (unsigned int)~QMF_INACTIVE;
else
s_playermodel.left.generic.flags |= QMF_INACTIVE;
if (s_playermodel.modelpage < s_playermodel.numpages-1)
- s_playermodel.right.generic.flags &= ~QMF_INACTIVE;
+ s_playermodel.right.generic.flags &= (unsigned int)~QMF_INACTIVE;
else
s_playermodel.right.generic.flags |= QMF_INACTIVE;
}
@@ -231,6 +231,23 @@ static void PlayerModel_UpdateGrid( void )
}
}
+/*
+=================
+PlayerModel_ShowPages
+=================
+*/
+static void PlayerModel_ShowPages( void ) { // BFP - For pagination
+ char pageIndicator[64];
+
+ Menu_Draw( &s_playermodel.menu );
+
+ if ( s_playermodel.numpages > 1 ) {
+ Com_sprintf( pageIndicator, sizeof(pageIndicator), "Page %d of %d",
+ s_playermodel.modelpage + 1, s_playermodel.numpages );
+ UI_DrawString( 320, 440, pageIndicator, UI_CENTER | UI_SMALLFONT, color_white );
+ }
+}
+
/*
=================
PlayerModel_UpdateModel
@@ -406,7 +423,7 @@ static void PlayerModel_SetKiAttacks( void ) // BFP - Set ki attack pics
int numdirs;
char dirlist[2048], modelselected[2048];
char* dirptr;
- int i, j, dirlen, bfpnumber;
+ int i, dirlen, bfpnumber;
// BFP - NOTE: BFP vanilla uses the static icons for every character from bfp1 to bfp6.
// It would be cool to parse bfp_attacksets.cfg file and set every character their own ki attacks.
@@ -514,14 +531,14 @@ static void PlayerModel_PicEvent( void* ptr, int event )
for (i=0; iid - ID_PLAYERPIC0;
s_playermodel.pics[i].generic.flags |= QMF_HIGHLIGHT;
- s_playermodel.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_playermodel.picbuttons[i].generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
// get model and strip icon_
modelnum = s_playermodel.modelpage*MAX_MODELSPERPAGE + i;
@@ -611,7 +628,15 @@ static void PlayerModel_BuildList( void )
if (!strcmp(dirptr,".") || !strcmp(dirptr,".."))
continue;
-
+
+ // BFP - Skip monster model used on Monster gamemode
+ if ( !Q_stricmp( dirptr, MONSTER_NAME ) )
+ continue;
+
+ // BFP - Skip player models without prefix
+ if ( !strchr( dirptr, '-' ) )
+ continue;
+
// iterate all skin files in directory
numfiles = trap_FS_GetFileList( va("models/players/%s",dirptr), "tga", filelist, 2048 );
fileptr = filelist;
@@ -619,7 +644,7 @@ static void PlayerModel_BuildList( void )
{
filelen = (int)strlen(fileptr);
- COM_StripExtension(fileptr,skinname);
+ COM_StripExtension( fileptr, skinname, sizeof(skinname) );
// look for icon_????
if (!Q_stricmpn(skinname,"icon_",5))
@@ -637,6 +662,14 @@ static void PlayerModel_BuildList( void )
}
}
+ // BFP - To debug the loaded player models
+#if 0
+ for ( i = 0; i < s_playermodel.nummodels; ++i ) {
+ Com_Printf( "^5%d - player model: ^3%s\n", i, s_playermodel.modelnames[i] );
+ }
+ Com_Printf( "^2UI LOADED PLAYER MODELS: ^5%d\n", s_playermodel.nummodels );
+#endif
+
//APSFIXME - Degenerate no models case
s_playermodel.numpages = s_playermodel.nummodels/MAX_MODELSPERPAGE;
@@ -722,6 +755,7 @@ static void PlayerModel_MenuInit( void )
PlayerModel_Cache();
s_playermodel.menu.key = PlayerModel_MenuKey;
+ s_playermodel.menu.draw = PlayerModel_ShowPages; // BFP - For pagination
s_playermodel.menu.wrapAround = qtrue;
s_playermodel.menu.fullscreen = qtrue;
diff --git a/source/q3_ui/ui_players.c b/source/q3_ui/ui_players.c
index f0bbe1b..f5e177b 100644
--- a/source/q3_ui/ui_players.c
+++ b/source/q3_ui/ui_players.c
@@ -45,6 +45,8 @@ static int dp_realtime;
static float jumpHeight;
+// BFP - No player info set weapon
+#if 0
/*
===============
UI_PlayerInfo_SetWeapon
@@ -89,13 +91,13 @@ static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) {
if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) {
strcpy( path, item->world_model[0] );
- COM_StripExtension( path, path );
+ COM_StripExtension( path, path, sizeof(path) );
strcat( path, "_barrel.md3" );
pi->barrelModel = trap_R_RegisterModel( path );
}
strcpy( path, item->world_model[0] );
- COM_StripExtension( path, path );
+ COM_StripExtension( path, path, sizeof(path) );
strcat( path, "_flash.md3" );
pi->flashModel = trap_R_RegisterModel( path );
@@ -145,6 +147,7 @@ static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) {
break;
}
}
+#endif
/*
@@ -160,7 +163,8 @@ static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) {
}
}
-
+// BFP - Unused Q3 function
+#if 0
/*
===============
UI_SetLegsAnim
@@ -173,6 +177,7 @@ static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) {
}
UI_ForceLegsAnim( pi, anim );
}
+#endif
/*
@@ -218,8 +223,6 @@ static void UI_TorsoSequencing( playerInfo_t *pi ) {
currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
- // BFP doesn't use TORSO_DROP animation
-
if ( pi->weapon != pi->currentWeapon ) {
if ( currentAnim != TORSO_ATTACK0_PREPARE ) { // BFP - before TORSO_DROP
pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
@@ -235,26 +238,6 @@ static void UI_TorsoSequencing( playerInfo_t *pi ) {
UI_SetTorsoAnim( pi, TORSO_STAND );
return;
}
-
- if( currentAnim == TORSO_ATTACK0_PREPARE || currentAnim == TORSO_ATTACK0_STRIKE ) {
- UI_SetTorsoAnim( pi, TORSO_STAND );
- return;
- }
-
- // BFP doesn't use these animations
- /*
- if ( currentAnim == TORSO_DROP ) {
- UI_PlayerInfo_SetWeapon( pi, pi->weapon );
- pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
- UI_ForceTorsoAnim( pi, TORSO_RAISE );
- return;
- }
-
- if ( currentAnim == TORSO_RAISE ) {
- UI_SetTorsoAnim( pi, TORSO_STAND );
- return;
- }
- */
}
@@ -292,6 +275,8 @@ static void UI_LegsSequencing( playerInfo_t *pi ) {
}
+// BFP - No UI_PositionEntityOnTag
+#if 0
/*
======================
UI_PositionEntityOnTag
@@ -316,6 +301,7 @@ static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *pare
MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis );
entity->backlerp = parent->backlerp;
}
+#endif
/*
@@ -653,6 +639,8 @@ static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t sha
}
+// BFP - No machine gun spin angle
+#if 0
/*
======================
UI_MachinegunSpinAngle
@@ -688,6 +676,7 @@ float UI_MachinegunSpinAngle( playerInfo_t *pi ) {
return angle;
}
+#endif
/*
@@ -700,9 +689,12 @@ void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int ti
refEntity_t legs;
refEntity_t torso;
refEntity_t head;
+ // BFP - No gun, barrel and flash
+#if 0
refEntity_t gun;
refEntity_t barrel;
refEntity_t flash;
+#endif
vec3_t origin;
int renderfx;
vec3_t mins = {-16, -16, -24};
@@ -821,6 +813,8 @@ void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int ti
trap_R_AddRefEntityToScene( &head );
+ // BFP - No gun, spinning barrel and muzzle flash
+#if 0
//
// add the gun
//
@@ -877,6 +871,7 @@ void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int ti
pi->flashDlightColor[1], pi->flashDlightColor[2] );
}
}
+#endif
//
// add the chat icon
@@ -1172,7 +1167,7 @@ void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_
// BFP - 5th parameter for the function has been disabled/removed
-#if 1
+#if 0
if ( weaponNumber != -1 ) {
pi->weapon = weaponNumber;
pi->currentWeapon = weaponNumber;
@@ -1205,7 +1200,8 @@ void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_
if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) {
torsoAnim = legsAnim = BOTH_DEATH1;
pi->weapon = pi->currentWeapon = WP_NONE;
- UI_PlayerInfo_SetWeapon( pi, pi->weapon );
+ // BFP - No player info set weapon
+ //UI_PlayerInfo_SetWeapon( pi, pi->weapon );
jumpHeight = 0;
pi->pendingLegsAnim = 0;
diff --git a/source/q3_ui/ui_qmenu.c b/source/q3_ui/ui_qmenu.c
index 9328295..33e9585 100644
--- a/source/q3_ui/ui_qmenu.c
+++ b/source/q3_ui/ui_qmenu.c
@@ -341,7 +341,7 @@ void Bitmap_Draw( menubitmap_s *b )
}
else
color = pulse_color;
- color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR);
+ color[3] = 0.5+0.5*sin( ( uis.realtime % TMOD_075) / PULSE_DIVISOR );
trap_R_SetColor( color );
UI_DrawHandlePic( x, y, w, h, b->focusshader );
@@ -372,7 +372,7 @@ static void Action_Init( menuaction_s *a )
// calculate bounds
if (a->generic.name)
- len = (int)strlen(a->generic.name);
+ len = strlen(a->generic.name);
else
len = 0;
@@ -438,7 +438,7 @@ static void RadioButton_Init( menuradiobutton_s *rb )
// calculate bounds
if (rb->generic.name)
- len = (int)strlen(rb->generic.name);
+ len = strlen(rb->generic.name);
else
len = 0;
@@ -549,7 +549,7 @@ static void Slider_Init( menuslider_s *s )
// calculate bounds
if (s->generic.name)
- len = (int)strlen(s->generic.name);
+ len = strlen(s->generic.name);
else
len = 0;
@@ -766,7 +766,7 @@ static void SpinControl_Init( menulist_s *s ) {
const char* str;
if (s->generic.name)
- len = (int)strlen(s->generic.name) * SMALLCHAR_WIDTH;
+ len = strlen(s->generic.name) * SMALLCHAR_WIDTH;
else
len = 0;
@@ -775,7 +775,7 @@ static void SpinControl_Init( menulist_s *s ) {
len = s->numitems = 0;
while ( (str = s->itemnames[s->numitems]) != 0 )
{
- l = (int)strlen(str);
+ l = strlen(str);
if (l > len)
len = l;
@@ -930,6 +930,7 @@ sfxHandle_t ScrollList_Key( menulist_s *l, int key )
int cursory;
int column;
int index;
+ int time;
switch (key)
{
@@ -954,6 +955,22 @@ sfxHandle_t ScrollList_Key( menulist_s *l, int key )
l->oldvalue = l->curvalue;
l->curvalue = l->top + index;
+ // doubleclick
+ if ( l->generic.dblclick ) {
+ if ( l->oldvalue == l->curvalue ) {
+ if ( l->mouse1time ) {
+ time = trap_Milliseconds();
+ if ( time - l->mouse1time < 250 ) {
+ //Com_Printf("doubleclick\n");
+ l->generic.dblclick( l );
+ l->mouse1time = 0;
+ return (menu_in_sound);
+ }
+ }
+ }
+ l->mouse1time = trap_Milliseconds();
+ }
+
if (l->oldvalue != l->curvalue && l->generic.callback)
{
l->generic.callback( l, QM_GOTFOCUS );
@@ -1047,6 +1064,60 @@ sfxHandle_t ScrollList_Key( menulist_s *l, int key )
}
return (menu_buzz_sound);
+ case K_MWHEELUP:
+ if ( !l->scroll )
+ return (menu_null_sound);
+
+ if (l->curvalue > 0)
+ {
+ l->oldvalue = l->curvalue;
+ l->curvalue -= l->scroll;
+ if (l->curvalue < 0) {
+ l->curvalue = 0;
+ l->top = 0;
+ }
+ if ( l->curvalue < l->top )
+ l->top -= l->height;
+
+ if (l->top < 0)
+ l->top = 0;
+
+ if (l->generic.callback)
+ l->generic.callback( l, QM_GOTFOCUS );
+
+ return (menu_move_sound);
+ }
+ return (menu_buzz_sound);
+
+ case K_MWHEELDOWN:
+ if ( !l->scroll )
+ return (menu_null_sound);
+
+ if (l->curvalue < l->numitems-1)
+ {
+ l->oldvalue = l->curvalue;
+ l->curvalue += l->scroll;
+
+ if (l->curvalue > l->numitems-1)
+ l->curvalue = l->numitems-1;
+
+ if ( l->curvalue - l->top > l->height - 1 ) {
+ l->top = l->top + l->height;
+ if ( l->numitems - l->top < l->height * l->columns ) {
+ l->top = l->numitems - l->height * l->columns;
+ }
+ }
+
+ if (l->top < 0)
+ l->top = 0;
+
+ if (l->generic.callback)
+ l->generic.callback( l, QM_GOTFOCUS );
+
+ return (menu_move_sound);
+ }
+ return (menu_buzz_sound);
+
case K_KP_UPARROW:
case K_UPARROW:
if( l->curvalue == 0 ) {
@@ -1273,7 +1344,7 @@ void Menu_AddItem( menuframework_s *menu, void *item )
menu->items[menu->nitems] = item;
((menucommon_s*)menu->items[menu->nitems])->parent = menu;
((menucommon_s*)menu->items[menu->nitems])->menuPosition = menu->nitems;
- ((menucommon_s*)menu->items[menu->nitems])->flags &= ~QMF_HASMOUSEFOCUS;
+ ((menucommon_s*)menu->items[menu->nitems])->flags &= (unsigned int)~QMF_HASMOUSEFOCUS;
// perform any item specific initializations
itemptr = (menucommon_s*)item;
@@ -1554,7 +1625,7 @@ Menu_ItemAtCursor
void *Menu_ItemAtCursor( menuframework_s *m )
{
if ( m->cursor < 0 || m->cursor >= m->nitems )
- return 0;
+ return NULL;
return m->items[m->cursor];
}
@@ -1636,11 +1707,11 @@ sfxHandle_t Menu_DefaultKey( menuframework_s *m, int key )
{
#ifndef NDEBUG
case K_F11:
- uis.debug ^= 1;
+ trap_Cmd_ExecuteText( EXEC_APPEND, "screenshot\n" );
break;
case K_F12:
- trap_Cmd_ExecuteText(EXEC_APPEND, "screenshot\n");
+ uis.debug ^= 1;
break;
#endif
case K_KP_UPARROW:
@@ -1722,7 +1793,12 @@ void Menu_Cache( void )
uis.rb_off = trap_R_RegisterShaderNoMip( "menu/art/switch_off" );
uis.whiteShader = trap_R_RegisterShaderNoMip( "white" );
- uis.menuBackShader = trap_R_RegisterShaderNoMip( "menuback" );
+ if ( uis.glconfig.hardwareType == GLHW_RAGEPRO ) {
+ // the blend effect turns to shit with the normal
+ uis.menuBackShader = trap_R_RegisterShaderNoMip( "menubackRagePro" );
+ } else {
+ uis.menuBackShader = trap_R_RegisterShaderNoMip( "menuback" );
+ }
uis.menuBackNoLogoShader = trap_R_RegisterShaderNoMip( "menubacknologo" );
menu_in_sound = trap_S_RegisterSound( "sound/misc/menu1.wav", qfalse );
diff --git a/source/q3_ui/ui_serverinfo.c b/source/q3_ui/ui_serverinfo.c
index 4ca1268..095f879 100644
--- a/source/q3_ui/ui_serverinfo.c
+++ b/source/q3_ui/ui_serverinfo.c
@@ -29,16 +29,33 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define SERVERINFO_BACK0 "menu/art/back_0"
#define SERVERINFO_BACK1 "menu/art/back_1"
+// BFP - Arrows
+#define ART_ARROWS "menu/art/gs_arrows_0"
+#define ART_ARROWSL "menu/art/gs_arrows_l"
+#define ART_ARROWSR "menu/art/gs_arrows_r"
+
static char* serverinfo_artlist[] =
{
SERVERINFO_BACK0,
SERVERINFO_BACK1,
+
+ // BFP - Add arrows inside that
+ ART_ARROWS,
+ ART_ARROWSL,
+ ART_ARROWSR,
NULL
};
#define ID_ADD 100
#define ID_BACK 101
+// BFP - ID for arrows
+#define ID_NEXT 102
+#define ID_PREV 103
+
+// BFP - Lines per page
+#define LINES_PER_PAGE 15
+
typedef struct
{
menuframework_s menu;
@@ -49,6 +66,11 @@ typedef struct
menutext_s add;
char info[MAX_INFO_STRING];
int numlines;
+ int currentPage;
+ int totalPages;
+ menubitmap_s arrows;
+ menubitmap_s next;
+ menubitmap_s prev;
} serverinfo_t;
static serverinfo_t s_serverinfo;
@@ -115,6 +137,27 @@ static void ServerInfo_Event( void* ptr, int event )
UI_PopMenu();
break;
+
+ // BFP - Prev and next events
+ case ID_PREV:
+ if (event != QM_ACTIVATED)
+ break;
+
+ if (s_serverinfo.currentPage > 0) {
+ s_serverinfo.currentPage--;
+ Menu_Draw( &s_serverinfo.menu );
+ }
+ break;
+
+ case ID_NEXT:
+ if (event != QM_ACTIVATED)
+ break;
+
+ if (s_serverinfo.currentPage < s_serverinfo.totalPages - 1) {
+ s_serverinfo.currentPage++;
+ Menu_Draw( &s_serverinfo.menu );
+ }
+ break;
}
}
@@ -129,26 +172,79 @@ static void ServerInfo_MenuDraw( void )
char key[MAX_INFO_KEY];
char value[MAX_INFO_VALUE];
int y;
+ // BFP - For pagination
+ char pageIndicator[64];
+ int startLine, endLine;
+ int lineIndex;
+ int len, wrappedLen, i;
+ char wrappedValue[MAX_INFO_VALUE];
+ const int LIMIT_CHARACTERS = 39;
+
+ // BFP - NOTE: On original BFP, Menu_Draw is used at the end of the function, so hides completely the server info (·_·)
+ // To avoid the hidden info issue, the function needs to be called at the beginning, just here:
+ Menu_Draw( &s_serverinfo.menu );
- y = SCREEN_HEIGHT/2 - s_serverinfo.numlines*(SMALLCHAR_HEIGHT)/2 - 20;
+ // BFP - NOTE: Pagination is implemented here, also looks better when the user needs to see all info avoiding letters going off the screen
+
+ startLine = s_serverinfo.currentPage * LINES_PER_PAGE;
+ endLine = startLine + LINES_PER_PAGE;
+ if ( endLine > s_serverinfo.numlines )
+ endLine = s_serverinfo.numlines;
+
+ y = SCREEN_HEIGHT/2 - ( endLine - startLine ) * ( SMALLCHAR_HEIGHT )/2 - 20;
s = s_serverinfo.info;
- while ( s ) {
- Info_NextPair( &s, key, value );
+ lineIndex = 0;
+
+ while ( s && *s ) {
+ s = Info_NextPair( s, key, value );
if ( !key[0] ) {
break;
}
- Q_strcat( key, MAX_INFO_KEY, ":" );
-
- UI_DrawString(SCREEN_WIDTH*0.50 - 8,y,key,UI_RIGHT|UI_SMALLFONT,color_white); // BFP - modified color
- UI_DrawString(SCREEN_WIDTH*0.50 + 8,y,value,UI_LEFT|UI_SMALLFONT,text_color_normal);
+ // show only these lines in the page
+ if ( lineIndex >= startLine && lineIndex < endLine ) {
+ Q_strcat( key, MAX_INFO_KEY, ":" );
+
+ UI_DrawString( SCREEN_WIDTH*0.50 - 2, y, key, UI_RIGHT|UI_SMALLFONT, color_white );
+
+ // wrap the value if it exceeds the limit of characters
+ len = strlen( value );
+ wrappedLen = 0;
+ while ( len > 0 ) {
+ if ( len > LIMIT_CHARACTERS ) {
+ // find the last occurrence of ".", ",", "-", "_", or " " within the limit
+ for ( i = LIMIT_CHARACTERS; i > 0; i-- ) {
+ if ( value[wrappedLen + i] == '.' || value[wrappedLen + i] == ','
+ || value[wrappedLen + i] == '-' || value[wrappedLen + i] == '_'
+ || value[wrappedLen + i] == ' ' ) {
+ break;
+ }
+ }
+ if ( i == 0 ) {
+ i = LIMIT_CHARACTERS; // no delimiter found, use the limit
+ }
+ // include the delimiter in the first part
+ strncpy( wrappedValue, value + wrappedLen, i + 1 );
+ wrappedValue[i + 1] = '\0';
+ wrappedLen += i + 1;
+ len -= i + 1;
+ } else {
+ strcpy( wrappedValue, value + wrappedLen );
+ len = 0;
+ }
+ UI_DrawString( SCREEN_WIDTH*0.50 + 2, y, wrappedValue, UI_LEFT|UI_SMALLFONT, text_color_normal );
+ y += SMALLCHAR_HEIGHT;
+ }
+ }
- y += SMALLCHAR_HEIGHT;
+ lineIndex++;
}
-
- Menu_Draw( &s_serverinfo.menu );
+ Com_sprintf( pageIndicator, sizeof(pageIndicator), "Page %d of %d", s_serverinfo.currentPage + 1, s_serverinfo.totalPages );
+ UI_DrawString( 320, 420, pageIndicator, UI_CENTER|UI_SMALLFONT, color_white );
}
+
+
/*
=================
ServerInfo_MenuKey
@@ -229,7 +325,7 @@ void UI_ServerInfoMenu( void )
s_serverinfo.add.generic.callback = ServerInfo_Event;
s_serverinfo.add.generic.id = ID_ADD;
s_serverinfo.add.generic.x = 320;
- s_serverinfo.add.generic.y = 400; // BFP - modified ADD TO FAVORITES button y position
+ s_serverinfo.add.generic.y = 450; // BFP - (originally 400 on BFP) modified ADD TO FAVORITES button y position
s_serverinfo.add.string = "ADD TO FAVORITES";
s_serverinfo.add.style = UI_CENTER|UI_SMALLFONT;
s_serverinfo.add.color = color_white; // BFP - modified ADD TO FAVORITES button color
@@ -248,27 +344,69 @@ void UI_ServerInfoMenu( void )
s_serverinfo.back.height = 80; // BFP - modified BACK button height
s_serverinfo.back.focuspic = SERVERINFO_BACK1;
+ // BFP - Arrows to go prev or next
+#define ART_ARROWS_X 260
+#define ART_ARROWS_Y 375
+ s_serverinfo.arrows.generic.type = MTYPE_BITMAP;
+ s_serverinfo.arrows.generic.name = ART_ARROWS;
+ s_serverinfo.arrows.generic.flags = QMF_INACTIVE;
+ s_serverinfo.arrows.generic.x = ART_ARROWS_X;
+ s_serverinfo.arrows.generic.y = ART_ARROWS_Y;
+ s_serverinfo.arrows.width = 128;
+ s_serverinfo.arrows.height = 32;
+
+ s_serverinfo.prev.generic.type = MTYPE_BITMAP;
+ s_serverinfo.prev.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_serverinfo.prev.generic.callback = ServerInfo_Event;
+ s_serverinfo.prev.generic.id = ID_PREV;
+ s_serverinfo.prev.generic.x = ART_ARROWS_X;
+ s_serverinfo.prev.generic.y = ART_ARROWS_Y;
+ s_serverinfo.prev.width = 64;
+ s_serverinfo.prev.height = 32;
+ s_serverinfo.prev.focuspic = ART_ARROWSL;
+
+ s_serverinfo.next.generic.type = MTYPE_BITMAP;
+ s_serverinfo.next.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_serverinfo.next.generic.callback = ServerInfo_Event;
+ s_serverinfo.next.generic.id = ID_NEXT;
+ s_serverinfo.next.generic.x = ART_ARROWS_X+61;
+ s_serverinfo.next.generic.y = ART_ARROWS_Y;
+ s_serverinfo.next.width = 64;
+ s_serverinfo.next.height = 32;
+ s_serverinfo.next.focuspic = ART_ARROWSR;
+#undef ART_ARROWS_X
+#undef ART_ARROWS_Y
+ // BFP - End of arrows to go prev or next ^
+
trap_GetConfigString( CS_SERVERINFO, s_serverinfo.info, MAX_INFO_STRING );
s_serverinfo.numlines = 0;
s = s_serverinfo.info;
- while ( s ) {
- Info_NextPair( &s, key, value );
+ while ( s && *s ) {
+ s = Info_NextPair( s, key, value );
if ( !key[0] ) {
break;
}
s_serverinfo.numlines++;
}
- if (s_serverinfo.numlines > 16)
- s_serverinfo.numlines = 16;
+ // BFP - Handle total pages
+ s_serverinfo.totalPages = (s_serverinfo.numlines + LINES_PER_PAGE - 1) / LINES_PER_PAGE;
- Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.barlog ); // BFP - barlog
- // BFP - NOTE: That also happens in original BFP. Adding the background, hides completely the server info, it would be advisable to find an alternative or... remove that background hidding info issue (·_·)
Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.menubg ); // BFP - Menu background
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.barlog ); // BFP - barlog
Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.banner );
Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.add );
Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.back );
+ // BFP - Arrows to handle the pages
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.arrows );
+ if ( s_serverinfo.totalPages > 1 ) {
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.next );
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.prev );
+ }
+
+ // BFP - Initialize the current page to zero
+ s_serverinfo.currentPage = 0;
UI_PushMenu( &s_serverinfo.menu );
}
diff --git a/source/q3_ui/ui_servers2.c b/source/q3_ui/ui_servers2.c
index bdb7e1f..55bcc8d 100644
--- a/source/q3_ui/ui_servers2.c
+++ b/source/q3_ui/ui_servers2.c
@@ -34,6 +34,7 @@ MULTIPLAYER MENU (SERVER BROWSER)
#define MAX_GLOBALSERVERS 128
#define MAX_PINGREQUESTS 32
+#define MAX_PINGLISTSIZE MAX_PINGREQUESTS*8
#define MAX_ADDRESSLENGTH 64
#define MAX_HOSTNAMELENGTH 22
#define MAX_MAPNAMELENGTH 16
@@ -115,11 +116,11 @@ static const char *master_items[] = {
static const char *servertype_items[] = {
"All",
"Free For All",
- "Tournament", // BFP - TODO: Tournament gametype, remove TODO and this comment if it has been added successfully
- "Survival", // BFP - TODO: Survival gametype, remove TODO and this comment if it has been added successfully
- "Oozaru", // BFP - TODO: Oozaru gametype, remove TODO and this comment if it has been added successfully
+ "Tournament",
+ "Survival", // BFP - Survival gametype
+ "Monster", // BFP - Monster gametype
"Team Deathmatch",
- "Last Man Standing", // BFP - TODO: LMS gametype, remove TODO and this comment if it has been added successfully
+ "Team Last Man Standing", // BFP - Team Last Man Standing gametype
"Capture the Flag",
0
};
@@ -140,9 +141,9 @@ static char* gamenames[] = {
"1v1", // tournament
"sp", // single player
"survival", // survival
- "oozaru", // giant monkey
+ "monster", // monster
"tdm", // team deathmatch
- "lms", // last man standing
+ "tlms", // team last man standing
"ctf", // capture the flag
#else
// BFP - 12 gametypes from Q3 and mods
@@ -171,7 +172,7 @@ static char* netnames[] = {
};
static char bfpMessage[] = "Visit www.bidforpower.com"; // BFP
-static char otherMessage[] = "Visit www.globalrankings.com - you kick ass, we'll take names"; // BFP - unused message :P
+// static char otherMessage[] = "Visit www.globalrankings.com - you kick ass, we'll take names"; // BFP - unused message :P
typedef struct {
char adrstr[MAX_ADDRESSLENGTH];
@@ -226,7 +227,9 @@ typedef struct {
menubitmap_s create;
menubitmap_s go;
- pinglist_t pinglist[MAX_PINGREQUESTS];
+ menufield_s filter;
+
+ pinglist_t pinglist[MAX_PINGLISTSIZE];
table_t table[MAX_LISTBOXITEMS];
char* items[MAX_LISTBOXITEMS];
int numqueriedservers;
@@ -239,6 +242,8 @@ typedef struct {
int refreshtime;
char favoriteaddresses[MAX_FAVORITESERVERS][MAX_ADDRESSLENGTH];
int numfavoriteaddresses;
+
+ char serverfilter[ MAX_EDIT_LINE ];
} arenaservers_t;
static arenaservers_t g_arenaservers;
@@ -401,14 +406,14 @@ static void ArenaServers_UpdateMenu( void ) {
}
else {
// all servers pinged - enable controls
- g_arenaservers.master.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.list.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.go.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.master.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.gametype.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.sortkey.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.showempty.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.showfull.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.list.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.refresh.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.go.generic.flags &= (unsigned int)~QMF_GRAYED;
// update status bar
if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
@@ -453,13 +458,13 @@ static void ArenaServers_UpdateMenu( void ) {
}
// end of refresh - set control state
- g_arenaservers.master.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED;
- g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.master.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.gametype.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.sortkey.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.showempty.generic.flags &= (unsigned int)~QMF_GRAYED;
+ g_arenaservers.showfull.generic.flags &= (unsigned int)~QMF_GRAYED;
g_arenaservers.list.generic.flags |= QMF_GRAYED;
- g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.refresh.generic.flags &= (unsigned int)~QMF_GRAYED;
g_arenaservers.go.generic.flags |= QMF_GRAYED;
}
@@ -751,13 +756,13 @@ void ArenaServers_LoadFavorites( void )
int i;
int j;
int numtempitems;
- char emptyinfo[MAX_INFO_STRING];
+ // char emptyinfo[MAX_INFO_STRING];
char adrstr[MAX_ADDRESSLENGTH];
servernode_t templist[MAX_FAVORITESERVERS];
qboolean found;
found = qfalse;
- emptyinfo[0] = '\0';
+ // emptyinfo[0] = '\0';
// copy the old
memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
@@ -1124,14 +1129,11 @@ ArenaServers_SetType
int ArenaServers_SetType( int type ) // BFP - Hacky way to get the correct server type :P
{
int value; // BFP - Return the correct value in order to select server type
-#if ENABLE_MPLAYER
- g_servertype = value = type; // BFP - Let's go! value, you too!
-#else // Q3 default
+#if ( !ENABLE_MPLAYER ) /* Q3 default */
if (g_servertype == type)
return type;
-
- g_servertype = value = type; // BFP - Let's go! value, you too!
#endif
+ g_servertype = value = type; // BFP - Let's go! value, you too!
switch( type ) {
default:
@@ -1152,7 +1154,7 @@ int ArenaServers_SetType( int type ) // BFP - Hacky way to get the correct serve
break;
case AS_FAVORITES:
- g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
+ g_arenaservers.remove.generic.flags &= (unsigned int)~(QMF_INACTIVE|QMF_HIDDEN);
g_arenaservers.serverlist = g_favoriteserverlist;
g_arenaservers.numservers = &g_numfavoriteservers;
g_arenaservers.maxservers = MAX_FAVORITESERVERS;
@@ -1191,7 +1193,9 @@ ArenaServers_Event
*/
static void ArenaServers_Event( void* ptr, int event ) {
int id;
+#if ( !ENABLE_MPLAYER ) /* Q3 default */
int value;
+#endif
id = ((menucommon_s*)ptr)->id;
@@ -1205,7 +1209,7 @@ static void ArenaServers_Event( void* ptr, int event ) {
#if ENABLE_MPLAYER
g_arenaservers.master.curvalue = ArenaServers_SetType( g_arenaservers.master.curvalue );
trap_Cvar_SetValue( "ui_browserMaster", g_arenaservers.master.curvalue );
-#else // Q3 default
+#else /* Q3 default */
value = g_arenaservers.master.curvalue;
if (value >= 1)
{
@@ -1321,6 +1325,11 @@ static sfxHandle_t ArenaServers_MenuKey( int key ) {
ArenaServers_SaveChanges();
}
+ if ( key == '/' && Menu_ItemAtCursor( &g_arenaservers.menu ) == (menucommon_s *)&g_arenaservers.list ) {
+ Menu_SetCursorToItem( (menuframework_s *)&g_arenaservers, &g_arenaservers.filter );
+ g_arenaservers.filter.field.skipKey = qtrue;
+ return menu_in_sound;
+ }
return Menu_DefaultKey( &g_arenaservers.menu, key );
}
@@ -1333,10 +1342,12 @@ ArenaServers_MenuInit
*/
static void ArenaServers_MenuInit( void ) {
int i;
- int type;
int y;
int value;
static char statusbuffer[MAX_STATUSLENGTH];
+#if ( !ENABLE_MPLAYER ) /* Q3 default */
+ int type;
+#endif
// zero set all our globals
memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) );
@@ -1496,8 +1507,8 @@ static void ArenaServers_MenuInit( void ) {
g_arenaservers.remove.generic.id = ID_REMOVE;
g_arenaservers.remove.generic.x = 450;
g_arenaservers.remove.generic.y = 86;
- g_arenaservers.remove.width = 96; // BFP - NOTE: Originally, DELETE button uses this value. It should be 80
- g_arenaservers.remove.height = 48; // BFP - NOTE: Originally, DELETE button uses this value. It should be 80
+ g_arenaservers.remove.width = 80;
+ g_arenaservers.remove.height = 80;
g_arenaservers.remove.focuspic = ART_REMOVE1;
g_arenaservers.back.generic.type = MTYPE_BITMAP;
diff --git a/source/q3_ui/ui_setup.c b/source/q3_ui/ui_setup.c
index e06ef5d..3442c3d 100644
--- a/source/q3_ui/ui_setup.c
+++ b/source/q3_ui/ui_setup.c
@@ -45,7 +45,8 @@ SETUP MENU
#define ID_SYSTEMCONFIG 13
#define ID_GAME 14
#define ID_CDKEY 15
-#define ID_DEFAULTS 16
+// BFP - No defaults available
+//#define ID_DEFAULTS 16
#define ID_BACK 17
@@ -64,13 +65,15 @@ typedef struct {
menutext_s cdkey;
// menutext_s load;
// menutext_s save;
- menutext_s defaults;
+// BFP - No defaults available
+// menutext_s defaults;
menubitmap_s back;
} setupMenuInfo_t;
static setupMenuInfo_t setupMenuInfo;
-
+// BFP - No defaults available
+#if 0
/*
=================
Setup_ResetDefaults_Action
@@ -95,7 +98,7 @@ static void Setup_ResetDefaults_Draw( void ) {
UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This will reset *ALL*", UI_CENTER|UI_SMALLFONT, color_yellow );
UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "options to their default values.", UI_CENTER|UI_SMALLFONT, color_yellow );
}
-
+#endif
/*
===============
@@ -140,9 +143,10 @@ static void UI_SetupMenu_Event( void *ptr, int event ) {
// UI_SaveConfigMenu();
// break;
- case ID_DEFAULTS:
- UI_ConfirmMenu( "SET TO DEFAULTS?", Setup_ResetDefaults_Draw, Setup_ResetDefaults_Action );
- break;
+// BFP - No defaults available
+// case ID_DEFAULTS:
+// UI_ConfirmMenu( "SET TO DEFAULTS?", Setup_ResetDefaults_Draw, Setup_ResetDefaults_Action );
+// break;
case ID_BACK:
UI_PopMenu();
@@ -286,6 +290,8 @@ static void UI_SetupMenu_Init( void ) {
}
y += SETUP_MENU_VERTICAL_SPACING;
+ // BFP - No defaults available
+#if 0
setupMenuInfo.defaults.generic.type = MTYPE_PTEXT;
setupMenuInfo.defaults.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
setupMenuInfo.defaults.generic.x = 320;
@@ -295,6 +301,7 @@ static void UI_SetupMenu_Init( void ) {
setupMenuInfo.defaults.string = "DEFAULTS";
setupMenuInfo.defaults.color = color_white; // BFP - modified DEFAULTS color
setupMenuInfo.defaults.style = UI_CENTER;
+#endif
setupMenuInfo.back.generic.type = MTYPE_BITMAP;
setupMenuInfo.back.generic.name = ART_BACK0;
@@ -323,8 +330,8 @@ static void UI_SetupMenu_Init( void ) {
if( !trap_Cvar_VariableValue( "cl_paused" ) ) {
Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.defaults );
}
-#endif
Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.defaults );
+#endif
Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.back );
}
diff --git a/source/q3_ui/ui_startserver.c b/source/q3_ui/ui_startserver.c
index 721acaa..fc3bfde 100644
--- a/source/q3_ui/ui_startserver.c
+++ b/source/q3_ui/ui_startserver.c
@@ -70,6 +70,7 @@ typedef struct {
menutext_s banner;
menulist_s gametype;
+ menutext_s gametypedesc; // BFP - Gametype description
menubitmap_s mappics[MAX_MAPSPERPAGE];
menubitmap_s mapbuttons[MAX_MAPSPERPAGE];
menubitmap_s arrows;
@@ -86,22 +87,64 @@ typedef struct {
int nummaps;
int page;
int maxpages;
+
+ // BFP - Maps pagination
+ menutext_s mapspagetext;
+ char mapspagetextbuffer[64];
+
char maplist[MAX_SERVERMAPS][MAX_NAMELENGTH];
int mapGamebits[MAX_SERVERMAPS];
} startserver_t;
static startserver_t s_startserver;
+// BFP - Gametypes have been sorted this way
static const char *gametype_items[] = {
"Free For All",
- "Team Deathmatch",
"Tournament",
+ "Survival", // BFP - Survival
+ "Monster", // BFP - Monster
"Capture the Flag",
+ "Team Deathmatch",
+ "Team Last Man Standing", // BFP - Team Last Man Standing
0
};
-static int gametype_remap[] = {GT_FFA, GT_TEAM, GT_TOURNAMENT, GT_CTF};
-static int gametype_remap2[] = {0, 2, 0, 1, 3};
+static int gametype_remap[] = {
+ GT_FFA, // "Free For All"
+ GT_TOURNAMENT, // "Tournament"
+ GT_SURVIVAL, // "Survival"
+ GT_MONSTER, // "Monster"
+ GT_CTF, // "Capture the Flag"
+ GT_TEAM, // "Team Deathmatch"
+ GT_TLMS // "Team Last Man Standing"
+};
+static int gametype_remap2[] = { // Take a look on gametype_items indexes
+ 0, // GT_FFA (0) -> "Free For All"
+ 1, // GT_TOURNAMENT (1) -> "Tournament"
+ 0, // GT_SINGLE_PLAYER (2) (not in menu, default to 0)
+ 2, // GT_SURVIVAL (3) -> "Survival"
+ 3, // GT_MONSTER (4) -> "Monster"
+ 5, // GT_TEAM (5) -> "Team Deathmatch"
+ 6, // GT_TLMS (6) -> "Team Last Man Standing"
+ 4 // GT_CTF (7) -> "Capture the Flag"
+};
+static char* gametype_description[] = { // BFP - Gametype description
+ // "Free For All"
+ "Basic deathmatch game but with use of all the attacks and characters. Players are allowed to perform 20 different attacks as well as overpower opponents by engaging in power struggles.",
+ // "Tournament"
+ "Players fight 1 vs 1 battles while others wait as spectators.",
+ // "Survival"
+ "Faster paced tournament-style game. Two players fight. When one player dies, he is removed from the fight and the spectator who has been waiting longest enters the game. The winner does not get healed in between rounds, and the scores of spectators are visible. The first player to reach the fraglimit is the winner, and the map restarts in order to set the spawn power level back to the g_basePL setting.",
+ // "Monster"
+ "A deathmatch but with an extra: a monster who has more ki and health than the rest of the players. The one with the most frags wins.",
+ // "Capture the Flag"
+ "Two teams compete to capture both flags and return them to their bases for points.",
+ // "Team Deathmatch"
+ "Two teams to fight on a deathmatch but with use of all the attacks and characters.",
+ // "Team Last Man Standing"
+ "The players in a last man standing game start with all available attacks. Once a player is killed, he must wait until the end of the round before restarting."
+};
static void UI_ServerOptionsMenu( qboolean multiplayer );
@@ -139,6 +182,18 @@ static int GametypeBits( char *string ) {
continue;
}
+ // BFP - Survival
+ if( Q_stricmp( token, "survival" ) == 0 ) {
+ bits |= 1 << GT_SURVIVAL;
+ continue;
+ }
+
+ // BFP - Monster
+ if( Q_stricmp( token, "monster" ) == 0 || Q_stricmp( token, "oozaru" ) == 0 ) {
+ bits |= 1 << GT_MONSTER;
+ continue;
+ }
+
if( Q_stricmp( token, "team" ) == 0 ) {
bits |= 1 << GT_TEAM;
continue;
@@ -148,6 +203,12 @@ static int GametypeBits( char *string ) {
bits |= 1 << GT_CTF;
continue;
}
+
+ // BFP - Team Last Man Standing
+ if( Q_stricmp( token, "lms" ) == 0 || Q_stricmp( token, "tlms" ) == 0 ) {
+ bits |= 1 << GT_TLMS;
+ continue;
+ }
}
return bits;
@@ -173,23 +234,23 @@ static void StartServer_Update( void ) {
Com_sprintf( picname[i], sizeof(picname[i]), "levelshots/%s", s_startserver.maplist[top+i] );
- s_startserver.mappics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ s_startserver.mappics[i].generic.flags &= (unsigned int)~QMF_HIGHLIGHT;
s_startserver.mappics[i].generic.name = picname[i];
s_startserver.mappics[i].shader = 0;
// reset
s_startserver.mapbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
- s_startserver.mapbuttons[i].generic.flags &= ~QMF_INACTIVE;
+ s_startserver.mapbuttons[i].generic.flags &= (unsigned int)~QMF_INACTIVE;
}
for (; i=0 && i < MAX_MAPSPERPAGE )
{
s_startserver.mappics[i].generic.flags |= QMF_HIGHLIGHT;
- s_startserver.mapbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_startserver.mapbuttons[i].generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
}
// set the map name
strcpy( s_startserver.mapname.string, s_startserver.maplist[s_startserver.currentmap] );
}
+
+ // BFP - Maps pagination
+ if ( s_startserver.nummaps > 0 ) {
+ Com_sprintf( s_startserver.mapspagetextbuffer, sizeof(s_startserver.mapspagetextbuffer),
+ "Page %d of %d", s_startserver.page + 1, s_startserver.maxpages );
+ } else {
+ s_startserver.mapspagetextbuffer[0] = '\0';
+ }
Q_strupr( s_startserver.mapname.string );
}
@@ -256,6 +325,10 @@ static void StartServer_GametypeEvent( void* ptr, int event ) {
if( gametype_remap[s_startserver.gametype.curvalue] == GT_FFA ) {
matchbits |= ( 1 << GT_SINGLE_PLAYER );
}
+ // BFP - Survival maps are also part of "tourney" maps
+ if ( gametype_remap[s_startserver.gametype.curvalue] == GT_SURVIVAL ) {
+ matchbits |= ( 1 << GT_TOURNAMENT );
+ }
for( i = 0; i < count; i++ ) {
info = UI_GetArenaInfoByNumber( i );
@@ -371,6 +444,91 @@ static void StartServer_LevelshotDraw( void *self ) {
}
+/*
+==================================
+StartServer_GametypeDescriptionScrollDraw
+==================================
+*/
+static void StartServer_GametypeDescriptionScrollDraw( void *self ) { // BFP - Gametype description scroll
+ static int previousGametype = -1;
+ static int currentDescWidth = 0;
+ static int descStartTime = 0;
+
+ const char *desc = gametype_description[s_startserver.gametype.curvalue];
+
+ int offset;
+ const int SCROLLSPEED = 140;
+ int elapsedTime;
+ int xPos;
+ const int YPOS = 383;
+
+ // update text width and timer when gametype changes
+ if ( s_startserver.gametype.curvalue != previousGametype ) {
+ // BFP - NOTE: When adding a new gametype and its own description,
+ // first debug using:
+#if 0
+ currentDescWidth = UI_ProportionalStringWidth( desc );
+ Com_Printf( "currentDescWidth: %d\n", currentDescWidth );
+ if ( s_startserver.gametype.curvalue == GT_NEWGT ) {
+ currentDescWidth = value; // value to adjust
+ }
+#endif
+ // and set the value when the last character ends up off-screen,
+ // adding the new GT_* in the switch with the new value.
+ // Make sure what number of GT_* while assigning in s_startserver.gametype.curvalue
+
+ // handle proportional strings to keep the scroll animation correctly
+ switch ( s_startserver.gametype.curvalue ) {
+ case GT_FFA:
+ currentDescWidth = 2106;
+ break;
+ case GT_TOURNAMENT:
+ currentDescWidth = 1125;
+ break;
+ case ( GT_SURVIVAL - 1 ):
+ currentDescWidth = 3890;
+ break;
+ case ( GT_MONSTER - 1 ):
+ currentDescWidth = 1700;
+ break;
+ case ( GT_CTF - 3 ):
+ currentDescWidth = 1302;
+ break;
+ case GT_TEAM:
+ currentDescWidth = 1302;
+ break;
+ case GT_TLMS:
+ currentDescWidth = 1904;
+ break;
+ }
+
+ descStartTime = uis.realtime;
+ previousGametype = s_startserver.gametype.curvalue;
+ }
+
+ // calculate scroll offset
+ elapsedTime = uis.realtime - descStartTime;
+ offset = ( elapsedTime * SCROLLSPEED / 1000 ) % currentDescWidth;
+
+ // determine starting x position (right edge of screen)
+ xPos = 640 - offset;
+
+ UI_DrawString( xPos, YPOS, desc, UI_LEFT|UI_SMALLFONT, text_color_highlight );
+}
+
+
+/*
+=================
+StartServer_MapsPaginationDraw
+=================
+*/
+static void StartServer_MapsPaginationDraw( void *self ) { // BFP - Maps pagination
+ if ( s_startserver.mapspagetextbuffer[0] ) {
+ UI_DrawString( 432, 408, s_startserver.mapspagetextbuffer, UI_CENTER|UI_SMALLFONT, color_white );
+ }
+}
+
+
/*
=================
StartServer_MenuInit
@@ -424,6 +582,11 @@ static void StartServer_MenuInit( void ) {
s_startserver.gametype.generic.y = 368;
s_startserver.gametype.itemnames = gametype_items;
+ // BFP - Gametype description
+ s_startserver.gametypedesc.generic.type = MTYPE_PTEXT;
+ s_startserver.gametypedesc.string = ""; // handle string, without that, DLL/SO crashes
+ s_startserver.gametypedesc.generic.ownerdraw = StartServer_GametypeDescriptionScrollDraw;
+
for (i=0; i= GT_TEAM ) {
+#if 0
Q_strncpyz( s_serveroptions.playerNameBuffers[1], "grunt", 16 );
Q_strncpyz( s_serveroptions.playerNameBuffers[2], "major", 16 );
if( s_serveroptions.gametype == GT_TEAM ) {
@@ -1081,6 +1259,17 @@ static void ServerOptions_InitBotNames( void ) {
}
s_serveroptions.playerType[10].curvalue = 2;
s_serveroptions.playerType[11].curvalue = 2;
+#endif
+ // BFP - That means that these slots are closed (just "---"), although these can be toggled to open or set as bot
+ // blue
+ s_serveroptions.playerType[3].curvalue = 2;
+ s_serveroptions.playerType[4].curvalue = 2;
+ s_serveroptions.playerType[5].curvalue = 2;
+
+ // red
+ s_serveroptions.playerType[9].curvalue = 2;
+ s_serveroptions.playerType[10].curvalue = 2;
+ s_serveroptions.playerType[11].curvalue = 2;
return;
}
@@ -1255,7 +1444,8 @@ static void ServerOptions_MenuInit( qboolean multiplayer ) {
memset( &s_serveroptions, 0 ,sizeof(serveroptions_t) );
s_serveroptions.multiplayer = multiplayer;
- s_serveroptions.gametype = (int)Com_Clamp( 0, 5, trap_Cvar_VariableValue( "g_gameType" ) );
+ // BFP - Modified from 0 to max gametype value, before Q3: 0 to 5
+ s_serveroptions.gametype = (int)Com_Clamp( 0, GT_MAX_GAME_TYPE, trap_Cvar_VariableValue( "g_gametype" ) );
ServerOptions_Cache();
@@ -1667,19 +1857,33 @@ UI_BotSelectMenu_BuildList
*/
static void UI_BotSelectMenu_BuildList( void ) {
int n;
+ int count = 0;
+ const char *info;
+ char model[MAX_QPATH];
botSelectInfo.modelpage = 0;
botSelectInfo.numBots = UI_GetNumBots();
+
+ // BFP - A modified list that excludes the monster model used on Monster gamemode
+ for( n = 0; n < botSelectInfo.numBots; n++ ) {
+ info = UI_GetBotInfoByNumber( n );
+ Q_strncpyz( model, Info_ValueForKey( info, "model" ), sizeof(model) );
+
+ // skip monster model
+ if ( !Q_stricmp( model, MONSTER_NAME ) ) {
+ continue;
+ }
+
+ botSelectInfo.sortedBotNums[count] = n;
+ count++;
+ }
+
+ botSelectInfo.numBots = count; // Update with filtered count
botSelectInfo.numpages = botSelectInfo.numBots / MAX_MODELSPERPAGE;
if( botSelectInfo.numBots % MAX_MODELSPERPAGE ) {
botSelectInfo.numpages++;
}
- // initialize the array
- for( n = 0; n < botSelectInfo.numBots; n++ ) {
- botSelectInfo.sortedBotNums[n] = n;
- }
-
// now sort it
qsort( botSelectInfo.sortedBotNums, botSelectInfo.numBots, sizeof(botSelectInfo.sortedBotNums[0]), UI_BotSelectMenu_SortCompare );
}
@@ -1741,7 +1945,7 @@ static void UI_BotSelectMenu_UpdateGrid( void ) {
botSelectInfo.picnames[i].color = color_white;
#endif
- botSelectInfo.picbuttons[i].generic.flags &= ~QMF_INACTIVE;
+ botSelectInfo.picbuttons[i].generic.flags &= (unsigned int)~QMF_INACTIVE;
}
else {
// dead slot
@@ -1750,7 +1954,7 @@ static void UI_BotSelectMenu_UpdateGrid( void ) {
botSelectInfo.botnames[i][0] = 0;
}
- botSelectInfo.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ botSelectInfo.pics[i].generic.flags &= (unsigned int)~QMF_HIGHLIGHT;
botSelectInfo.pics[i].shader = 0;
botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
}
@@ -1758,18 +1962,18 @@ static void UI_BotSelectMenu_UpdateGrid( void ) {
// set selected model
i = botSelectInfo.selectedmodel % MAX_MODELSPERPAGE;
botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
- botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ botSelectInfo.picbuttons[i].generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
if( botSelectInfo.numpages > 1 ) {
if( botSelectInfo.modelpage > 0 ) {
- botSelectInfo.left.generic.flags &= ~QMF_INACTIVE;
+ botSelectInfo.left.generic.flags &= (unsigned int)~QMF_INACTIVE;
}
else {
botSelectInfo.left.generic.flags |= QMF_INACTIVE;
}
if( botSelectInfo.modelpage < (botSelectInfo.numpages - 1) ) {
- botSelectInfo.right.generic.flags &= ~QMF_INACTIVE;
+ botSelectInfo.right.generic.flags &= (unsigned int)~QMF_INACTIVE;
}
else {
botSelectInfo.right.generic.flags |= QMF_INACTIVE;
@@ -1867,14 +2071,14 @@ static void UI_BotSelectMenu_BotEvent( void* ptr, int event ) {
}
for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++ ) {
- botSelectInfo.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ botSelectInfo.pics[i].generic.flags &= (unsigned int)~QMF_HIGHLIGHT;
botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
}
// set selected
i = ((menucommon_s*)ptr)->id;
botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
- botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ botSelectInfo.picbuttons[i].generic.flags &= (unsigned int)~QMF_PULSEIFFOCUS;
botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE + i;
}
diff --git a/source/q3_ui/ui_syscalls.c b/source/q3_ui/ui_syscalls.c
index 890bd0c..c5cd079 100644
--- a/source/q3_ui/ui_syscalls.c
+++ b/source/q3_ui/ui_syscalls.c
@@ -30,7 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1;
-void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) {
+DLLEXPORT void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) {
syscall = syscallptr;
}
@@ -122,7 +122,7 @@ int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf
return syscall( UI_FS_GETFILELIST, path, extension, listbuf, bufsize );
}
-int trap_FS_Seek( fileHandle_t f, long offset, int origin ) {
+int trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin ) {
return syscall( UI_FS_SEEK, f, offset, origin );
}
@@ -266,11 +266,11 @@ int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int ma
return syscall( UI_LAN_SERVERSTATUS, serverAddress, serverStatus, maxLen );
}
-void trap_LAN_SaveCachedServers() {
+void trap_LAN_SaveCachedServers( void ) {
syscall( UI_LAN_SAVECACHEDSERVERS );
}
-void trap_LAN_LoadCachedServers() {
+void trap_LAN_LoadCachedServers( void ) {
syscall( UI_LAN_LOADCACHEDSERVERS );
}
diff --git a/source/q3_ui/ui_team.c b/source/q3_ui/ui_team.c
index 5e14aca..ce13023 100644
--- a/source/q3_ui/ui_team.c
+++ b/source/q3_ui/ui_team.c
@@ -190,6 +190,7 @@ void TeamMain_MenuInit( void ) {
case GT_SINGLE_PLAYER:
case GT_FFA:
case GT_TOURNAMENT:
+ case GT_SURVIVAL: // BFP - Survival
s_teammain.joinred.generic.flags |= QMF_GRAYED;
s_teammain.joinblue.generic.flags |= QMF_GRAYED;
break;
diff --git a/source/q3_ui/ui_video.c b/source/q3_ui/ui_video.c
index 496ad23..14144e6 100644
--- a/source/q3_ui/ui_video.c
+++ b/source/q3_ui/ui_video.c
@@ -38,6 +38,11 @@ DRIVER INFORMATION MENU
#define DRIVERINFO_BACK0 "menu/art/back_0"
#define DRIVERINFO_BACK1 "menu/art/back_1"
+// BFP - Arrows
+#define ART_ARROWS "menu/art/gs_arrows_0"
+#define ART_ARROWSL "menu/art/gs_arrows_l"
+#define ART_ARROWSR "menu/art/gs_arrows_r"
+
static char* driverinfo_artlist[] =
{
DRIVERINFO_BACK0,
@@ -47,6 +52,16 @@ static char* driverinfo_artlist[] =
#define ID_DRIVERINFOBACK 100
+// BFP - ID for arrows
+#define ID_DRIVERINFOPREV 101
+#define ID_DRIVERINFONEXT 102
+
+// BFP - Limit of extensions length and UI extension strings
+// uis.glconfig.extensions_string length is 8192
+#define MAX_EXTENSIONS_STRING_LENGTH 2048
+// when surpassing 64 extension strings, UI starts to break crazily the next strings after 64
+#define MAX_UI_EXTENSION_STRINGS 64
+
typedef struct
{
menuframework_s menu;
@@ -54,9 +69,14 @@ typedef struct
menubitmap_s menubg; // BFP - Menu background
menubitmap_s barlog; // BFP - barlog
menubitmap_s back;
- char stringbuff[1024];
+ char stringbuff[MAX_EXTENSIONS_STRING_LENGTH];
char* strings[64];
int numstrings;
+ // BFP - For pagination
+ int currentPage, totalPages, totalLines;
+ menubitmap_s arrows;
+ menubitmap_s next;
+ menubitmap_s prev;
} driverinfo_t;
static driverinfo_t s_driverinfo;
@@ -71,11 +91,25 @@ static void DriverInfo_Event( void* ptr, int event )
if (event != QM_ACTIVATED)
return;
- switch (((menucommon_s*)ptr)->id)
- {
- case ID_DRIVERINFOBACK:
- UI_PopMenu();
- break;
+ switch ( ((menucommon_s*)ptr)->id ) {
+ // BFP - Prev and next events
+ case ID_DRIVERINFOPREV:
+ if ( s_driverinfo.currentPage > 0 ) {
+ s_driverinfo.currentPage--;
+ Menu_Draw( &s_driverinfo.menu );
+ }
+ break;
+
+ case ID_DRIVERINFONEXT:
+ if ( s_driverinfo.currentPage < s_driverinfo.totalPages - 1 ) {
+ s_driverinfo.currentPage++;
+ Menu_Draw( &s_driverinfo.menu );
+ }
+ break;
+
+ case ID_DRIVERINFOBACK:
+ UI_PopMenu();
+ break;
}
}
@@ -88,28 +122,70 @@ static void DriverInfo_MenuDraw( void )
{
int i;
int y;
+ // BFP - For pagination
+ char pageIndicator[64];
+ int startLine, endLine;
+ int len, wrappedLen;
+#define DRIVERINFO_LINES_PER_PAGE 20
+ const int LIMIT_CHARACTERS = 39;
+ char wrappedExtension[40]; // 39 characters + 1 null terminator
+ int j = LIMIT_CHARACTERS;
Menu_Draw( &s_driverinfo.menu );
- UI_DrawString( 320, 80, "VENDOR", UI_CENTER|UI_SMALLFONT, color_white ); // BFP - modified VENDOR title color
- UI_DrawString( 320, 152, "PIXELFORMAT", UI_CENTER|UI_SMALLFONT, color_white ); // BFP - modified PIXELFORMAT title color
- UI_DrawString( 320, 192, "EXTENSIONS", UI_CENTER|UI_SMALLFONT, color_white ); // BFP - modified EXTENSIONS title color
-
- UI_DrawString( 320, 80+16, uis.glconfig.vendor_string, UI_CENTER|UI_SMALLFONT, text_color_normal );
- UI_DrawString( 320, 96+16, uis.glconfig.version_string, UI_CENTER|UI_SMALLFONT, text_color_normal );
- UI_DrawString( 320, 112+16, uis.glconfig.renderer_string, UI_CENTER|UI_SMALLFONT, text_color_normal );
- UI_DrawString( 320, 152+16, va ("color(%d-bits) Z(%d-bits) stencil(%d-bits)", uis.glconfig.colorBits, uis.glconfig.depthBits, uis.glconfig.stencilBits), UI_CENTER|UI_SMALLFONT, text_color_normal );
-
- // double column
- y = 192+16;
- for (i=0; i s_driverinfo.numstrings ) {
+ endLine = s_driverinfo.numstrings;
}
- if (s_driverinfo.numstrings & 1)
- UI_DrawString( 320, y, s_driverinfo.strings[s_driverinfo.numstrings-1], UI_CENTER|UI_SMALLFONT, text_color_normal );
+ // display paginated EXTENSIONS strings
+ y = 192 + 16; // start after the EXTENSIONS title
+ for ( i = startLine; i < endLine; i++ ) {
+ len = (int)strlen( s_driverinfo.strings[i] );
+ wrappedLen = 0;
+
+ while ( len > 0 ) {
+ if ( len > LIMIT_CHARACTERS ) {
+ // copy the first part of the string and wrap it
+ strncpy( wrappedExtension, s_driverinfo.strings[i] + wrappedLen, j );
+ wrappedExtension[j] = '\0';
+ wrappedLen += j;
+ len -= j;
+ } else {
+ strcpy( wrappedExtension, s_driverinfo.strings[i] + wrappedLen );
+ len = 0;
+ }
+
+ // display strings in double columns
+ if ( i % 2 == 0 ) {
+ UI_DrawString( 320 - 4, y, wrappedExtension, UI_RIGHT | UI_SMALLFONT, text_color_normal );
+ } else {
+ UI_DrawString( 320 + 4, y, wrappedExtension, UI_LEFT | UI_SMALLFONT, text_color_normal );
+ y += SMALLCHAR_HEIGHT;
+ }
+ }
+ }
+
+ // if odd number of strings, draw the last one centered
+ if ( s_driverinfo.numstrings & 1 && i == s_driverinfo.numstrings ) {
+ UI_DrawString( 320, y, s_driverinfo.strings[s_driverinfo.numstrings - 1], UI_CENTER | UI_SMALLFONT, text_color_normal );
+ }
+
+ Com_sprintf( pageIndicator, sizeof(pageIndicator), "EXTENSIONS - Page %d of %d", s_driverinfo.currentPage + 1, s_driverinfo.totalPages );
+ UI_DrawString( 320, 445, pageIndicator, UI_CENTER | UI_SMALLFONT, color_white );
}
/*
@@ -138,8 +214,6 @@ UI_DriverInfo_Menu
static void UI_DriverInfo_Menu( void )
{
char* eptr;
- int i;
- int len;
// zero set all our globals
memset( &s_driverinfo, 0 ,sizeof(driverinfo_t) );
@@ -185,40 +259,94 @@ static void UI_DriverInfo_Menu( void )
s_driverinfo.back.height = 80; // BFP - modified BACK button height
s_driverinfo.back.focuspic = DRIVERINFO_BACK1;
+ // BFP - Arrows to go prev or next
+#define ART_ARROWS_X 260
+#define ART_ARROWS_Y 400
+ s_driverinfo.arrows.generic.type = MTYPE_BITMAP;
+ s_driverinfo.arrows.generic.name = ART_ARROWS;
+ s_driverinfo.arrows.generic.flags = QMF_INACTIVE;
+ s_driverinfo.arrows.generic.x = ART_ARROWS_X;
+ s_driverinfo.arrows.generic.y = ART_ARROWS_Y;
+ s_driverinfo.arrows.width = 128;
+ s_driverinfo.arrows.height = 32;
+
+ s_driverinfo.prev.generic.type = MTYPE_BITMAP;
+ s_driverinfo.prev.generic.flags = QMF_LEFT_JUSTIFY | QMF_PULSEIFFOCUS;
+ s_driverinfo.prev.generic.callback = DriverInfo_Event;
+ s_driverinfo.prev.generic.id = ID_DRIVERINFOPREV;
+ s_driverinfo.prev.generic.x = ART_ARROWS_X;
+ s_driverinfo.prev.generic.y = ART_ARROWS_Y;
+ s_driverinfo.prev.width = 64;
+ s_driverinfo.prev.height = 32;
+ s_driverinfo.prev.focuspic = ART_ARROWSL;
+
+ s_driverinfo.next.generic.type = MTYPE_BITMAP;
+ s_driverinfo.next.generic.flags = QMF_LEFT_JUSTIFY | QMF_PULSEIFFOCUS;
+ s_driverinfo.next.generic.callback = DriverInfo_Event;
+ s_driverinfo.next.generic.id = ID_DRIVERINFONEXT;
+ s_driverinfo.next.generic.x = ART_ARROWS_X+61;
+ s_driverinfo.next.generic.y = ART_ARROWS_Y;
+ s_driverinfo.next.width = 64;
+ s_driverinfo.next.height = 32;
+ s_driverinfo.next.focuspic = ART_ARROWSR;
+#undef ART_ARROWS_X
+#undef ART_ARROWS_Y
+ // BFP - End of arrows to go prev or next ^
+
+
+ // BFP - The following stuff has been modified for pagination
+
// TTimo: overflow with particularly long GL extensions (such as the gf3)
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399
// NOTE: could have pushed the size of stringbuff, but the list is already out of the screen
// (no matter what your resolution)
- Q_strncpyz(s_driverinfo.stringbuff, uis.glconfig.extensions_string, 1024);
+ Q_strncpyz(s_driverinfo.stringbuff, uis.glconfig.extensions_string, MAX_EXTENSIONS_STRING_LENGTH);
// build null terminated extension strings
eptr = s_driverinfo.stringbuff;
- while ( s_driverinfo.numstrings<40 && *eptr )
+ while ( s_driverinfo.numstrings < MAX_UI_EXTENSION_STRINGS && *eptr )
{
- while ( *eptr && *eptr == ' ' )
- *eptr++ = '\0';
+ // skip leading spaces
+ while ( *eptr == ' ' ) {
+ eptr++;
+ }
+
+ // if we reach the end of the string, break
+ if ( *eptr == '\0' ) {
+ break;
+ }
// track start of valid string
- if (*eptr && *eptr != ' ')
- s_driverinfo.strings[s_driverinfo.numstrings++] = eptr;
+ s_driverinfo.strings[s_driverinfo.numstrings] = eptr;
- while ( *eptr && *eptr != ' ' )
+ // find the end of the current extension string
+ while ( *eptr && *eptr != ' ' ) {
eptr++;
- }
+ }
- // safety length strings for display
- for (i=0; i 32) {
- s_driverinfo.strings[i][len-1] = '>';
- s_driverinfo.strings[i][len] = '\0';
+ // null-terminate the string
+ if ( *eptr != '\0' ) {
+ *eptr = '\0'; // null-terminate the current extension string
+ eptr++; // move past the null terminator
}
+
+ s_driverinfo.numstrings++;
}
+ // BFP - Calculate total pages
+ s_driverinfo.totalLines = s_driverinfo.numstrings;
+ s_driverinfo.totalPages = (s_driverinfo.totalLines + DRIVERINFO_LINES_PER_PAGE - 1) / DRIVERINFO_LINES_PER_PAGE;
+
Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.menubg ); // BFP - Menu background
Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.barlog ); // BFP - barlog
Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.banner );
Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.back );
+ // BFP - Arrows to handle the pages
+ Menu_AddItem( &s_driverinfo.menu, (void*) &s_driverinfo.arrows );
+ if ( s_driverinfo.totalPages > 1 ) {
+ Menu_AddItem( &s_driverinfo.menu, (void*) &s_driverinfo.next );
+ Menu_AddItem( &s_driverinfo.menu, (void*) &s_driverinfo.prev );
+ }
UI_PushMenu( &s_driverinfo.menu );
}
@@ -388,7 +516,7 @@ static void GraphicsOptions_UpdateMenuItems( void )
}
else
{
- s_graphicsoptions.fs.generic.flags &= ~QMF_GRAYED;
+ s_graphicsoptions.fs.generic.flags &= (unsigned int)~QMF_GRAYED;
}
if ( s_graphicsoptions.fs.curvalue == 0 || s_graphicsoptions.driver.curvalue == 1 )
@@ -398,7 +526,7 @@ static void GraphicsOptions_UpdateMenuItems( void )
}
else
{
- s_graphicsoptions.colordepth.generic.flags &= ~QMF_GRAYED;
+ s_graphicsoptions.colordepth.generic.flags &= (unsigned int)~QMF_GRAYED;
}
if ( s_graphicsoptions.allow_extensions.curvalue == 0 )
@@ -413,43 +541,43 @@ static void GraphicsOptions_UpdateMenuItems( void )
if ( s_ivo.mode != s_graphicsoptions.mode.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.fullscreen != s_graphicsoptions.fs.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.extensions != s_graphicsoptions.allow_extensions.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.tq != s_graphicsoptions.tq.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.lighting != s_graphicsoptions.lighting.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.colordepth != s_graphicsoptions.colordepth.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.driver != s_graphicsoptions.driver.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.texturebits != s_graphicsoptions.texturebits.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.geometry != s_graphicsoptions.geometry.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
if ( s_ivo.filter != s_graphicsoptions.filter.curvalue )
{
- s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ s_graphicsoptions.apply.generic.flags &= (unsigned int)~(QMF_HIDDEN|QMF_INACTIVE);
}
GraphicsOptions_CheckConfig();
diff --git a/tools/bin/7za.exe b/tools/bin/7za.exe
index d935b01..a9b9813 100644
Binary files a/tools/bin/7za.exe and b/tools/bin/7za.exe differ
diff --git a/win32-msvc/bfpq3.sln b/win32-msvc/bfpq3.sln
new file mode 100644
index 0000000..b523533
--- /dev/null
+++ b/win32-msvc/bfpq3.sln
@@ -0,0 +1,46 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cgame", "cgame.vcproj", "{FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "game", "game.vcproj", "{25F3B624-E060-4DAD-93D6-479AB6CD782F}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "q3_ui", "q3_ui.vcproj", "{96D17D51-0760-4732-89C1-26DFD5B581BF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|Win64 = Debug|Win64
+ Release|Win32 = Release|Win32
+ Release|Win64 = Release|Win64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Debug|Win32.Build.0 = Debug|Win32
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Debug|Win64.ActiveCfg = Debug|x64
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Debug|Win64.Build.0 = Debug|x64
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Release|Win32.ActiveCfg = Release|Win32
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Release|Win32.Build.0 = Release|Win32
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Release|Win64.ActiveCfg = Release|x64
+ {FF0DC79F-C1D1-4DE3-BBD0-91B6146FD69C}.Release|Win64.Build.0 = Release|x64
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Debug|Win32.Build.0 = Debug|Win32
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Debug|Win64.ActiveCfg = Debug|x64
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Debug|Win64.Build.0 = Debug|x64
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Release|Win32.ActiveCfg = Release|Win32
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Release|Win32.Build.0 = Release|Win32
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Release|Win64.ActiveCfg = Release|x64
+ {25F3B624-E060-4DAD-93D6-479AB6CD782F}.Release|Win64.Build.0 = Release|x64
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Debug|Win32.ActiveCfg = Debug|Win32
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Debug|Win32.Build.0 = Debug|Win32
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Debug|Win64.ActiveCfg = Debug|x64
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Debug|Win64.Build.0 = Debug|x64
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Release|Win32.ActiveCfg = Release|Win32
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Release|Win32.Build.0 = Release|Win32
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Release|Win64.ActiveCfg = Release|x64
+ {96D17D51-0760-4732-89C1-26DFD5B581BF}.Release|Win64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/win32-msvc/cgame.vcproj b/win32-msvc/cgame.vcproj
new file mode 100644
index 0000000..cf6592e
--- /dev/null
+++ b/win32-msvc/cgame.vcproj
@@ -0,0 +1,422 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/win32-msvc/game.vcproj b/win32-msvc/game.vcproj
new file mode 100644
index 0000000..ec3dc72
--- /dev/null
+++ b/win32-msvc/game.vcproj
@@ -0,0 +1,560 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/win32-msvc/q3_ui.vcproj b/win32-msvc/q3_ui.vcproj
new file mode 100644
index 0000000..dd5da16
--- /dev/null
+++ b/win32-msvc/q3_ui.vcproj
@@ -0,0 +1,463 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file