From a8f828a066773a59fdf646299359d35ae0e845af Mon Sep 17 00:00:00 2001 From: wyatt8740 Date: Mon, 6 Jan 2014 12:02:16 -0500 Subject: Removed accidental upload of copyrighted map files --- WinQuake/cl_tent.c | 788 ++++++++++++++++++++++++++--------------------------- 1 file changed, 394 insertions(+), 394 deletions(-) (limited to 'WinQuake/cl_tent.c') diff --git a/WinQuake/cl_tent.c b/WinQuake/cl_tent.c index 546e832..4565fd0 100644 --- a/WinQuake/cl_tent.c +++ b/WinQuake/cl_tent.c @@ -1,394 +1,394 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ -// cl_tent.c -- client side temporary entities - -#include "quakedef.h" - -int num_temp_entities; -entity_t cl_temp_entities[MAX_TEMP_ENTITIES]; -beam_t cl_beams[MAX_BEAMS]; - -sfx_t *cl_sfx_wizhit; -sfx_t *cl_sfx_knighthit; -sfx_t *cl_sfx_tink1; -sfx_t *cl_sfx_ric1; -sfx_t *cl_sfx_ric2; -sfx_t *cl_sfx_ric3; -sfx_t *cl_sfx_r_exp3; -#ifdef QUAKE2 -sfx_t *cl_sfx_imp; -sfx_t *cl_sfx_rail; -#endif - -/* -================= -CL_ParseTEnt -================= -*/ -void CL_InitTEnts (void) -{ - cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); - cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); - cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); - cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); - cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); - cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); - cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); -#ifdef QUAKE2 - cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav"); - cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav"); -#endif -} - -/* -================= -CL_ParseBeam -================= -*/ -void CL_ParseBeam (model_t *m) -{ - int ent; - vec3_t start, end; - beam_t *b; - int i; - - ent = MSG_ReadShort (); - - start[0] = MSG_ReadCoord (); - start[1] = MSG_ReadCoord (); - start[2] = MSG_ReadCoord (); - - end[0] = MSG_ReadCoord (); - end[1] = MSG_ReadCoord (); - end[2] = MSG_ReadCoord (); - -// override any beam with the same entity - for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) - if (b->entity == ent) - { - b->entity = ent; - b->model = m; - b->endtime = cl.time + 0.2; - VectorCopy (start, b->start); - VectorCopy (end, b->end); - return; - } - -// find a free beam - for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) - { - if (!b->model || b->endtime < cl.time) - { - b->entity = ent; - b->model = m; - b->endtime = cl.time + 0.2; - VectorCopy (start, b->start); - VectorCopy (end, b->end); - return; - } - } - Con_Printf ("beam list overflow!\n"); -} - -/* -================= -CL_ParseTEnt -================= -*/ -void CL_ParseTEnt (void) -{ - int type; - vec3_t pos; -#ifdef QUAKE2 - vec3_t endpos; -#endif - dlight_t *dl; - int rnd; - int colorStart, colorLength; - - type = MSG_ReadByte (); - switch (type) - { - case TE_WIZSPIKE: // spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 20, 30); - S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); - break; - - case TE_KNIGHTSPIKE: // spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 226, 20); - S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); - break; - - case TE_SPIKE: // spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); -#ifdef GLTEST - Test_Spawn (pos); -#else - R_RunParticleEffect (pos, vec3_origin, 0, 10); -#endif - if ( rand() % 5 ) - S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); - else - { - rnd = rand() & 3; - if (rnd == 1) - S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); - else if (rnd == 2) - S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); - else - S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); - } - break; - case TE_SUPERSPIKE: // super spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 0, 20); - - if ( rand() % 5 ) - S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); - else - { - rnd = rand() & 3; - if (rnd == 1) - S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); - else if (rnd == 2) - S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); - else - S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); - } - break; - - case TE_GUNSHOT: // bullet hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 0, 20); - break; - - case TE_EXPLOSION: // rocket explosion - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_ParticleExplosion (pos); - dl = CL_AllocDlight (0); - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - break; - - case TE_TAREXPLOSION: // tarbaby explosion - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_BlobExplosion (pos); - - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - break; - - case TE_LIGHTNING1: // lightning bolts - CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); - break; - - case TE_LIGHTNING2: // lightning bolts - CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); - break; - - case TE_LIGHTNING3: // lightning bolts - CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); - break; - -// PGM 01/21/97 - case TE_BEAM: // grappling hook beam - CL_ParseBeam (Mod_ForName("progs/beam.mdl", true)); - break; -// PGM 01/21/97 - - case TE_LAVASPLASH: - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_LavaSplash (pos); - break; - - case TE_TELEPORT: - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_TeleportSplash (pos); - break; - - case TE_EXPLOSION2: // color mapped explosion - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - colorStart = MSG_ReadByte (); - colorLength = MSG_ReadByte (); - R_ParticleExplosion2 (pos, colorStart, colorLength); - dl = CL_AllocDlight (0); - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - break; - -#ifdef QUAKE2 - case TE_IMPLOSION: - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - S_StartSound (-1, 0, cl_sfx_imp, pos, 1, 1); - break; - - case TE_RAILTRAIL: - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - endpos[0] = MSG_ReadCoord (); - endpos[1] = MSG_ReadCoord (); - endpos[2] = MSG_ReadCoord (); - S_StartSound (-1, 0, cl_sfx_rail, pos, 1, 1); - S_StartSound (-1, 1, cl_sfx_r_exp3, endpos, 1, 1); - R_RocketTrail (pos, endpos, 0+128); - R_ParticleExplosion (endpos); - dl = CL_AllocDlight (-1); - VectorCopy (endpos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - break; -#endif - - default: - Sys_Error ("CL_ParseTEnt: bad type"); - } -} - - -/* -================= -CL_NewTempEntity -================= -*/ -entity_t *CL_NewTempEntity (void) -{ - entity_t *ent; - - if (cl_numvisedicts == MAX_VISEDICTS) - return NULL; - if (num_temp_entities == MAX_TEMP_ENTITIES) - return NULL; - ent = &cl_temp_entities[num_temp_entities]; - memset (ent, 0, sizeof(*ent)); - num_temp_entities++; - cl_visedicts[cl_numvisedicts] = ent; - cl_numvisedicts++; - - ent->colormap = vid.colormap; - return ent; -} - - -/* -================= -CL_UpdateTEnts -================= -*/ -void CL_UpdateTEnts (void) -{ - int i; - beam_t *b; - vec3_t dist, org; - float d; - entity_t *ent; - float yaw, pitch; - float forward; - - num_temp_entities = 0; - -// update lightning - for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) - { - if (!b->model || b->endtime < cl.time) - continue; - - // if coming from the player, update the start position - if (b->entity == cl.viewentity) - { - VectorCopy (cl_entities[cl.viewentity].origin, b->start); - } - - // calculate pitch and yaw - VectorSubtract (b->end, b->start, dist); - - if (dist[1] == 0 && dist[0] == 0) - { - yaw = 0; - if (dist[2] > 0) - pitch = 90; - else - pitch = 270; - } - else - { - yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - - forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]); - pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - // add new entities for the lightning - VectorCopy (b->start, org); - d = VectorNormalize(dist); - while (d > 0) - { - ent = CL_NewTempEntity (); - if (!ent) - return; - VectorCopy (org, ent->origin); - ent->model = b->model; - ent->angles[0] = pitch; - ent->angles[1] = yaw; - ent->angles[2] = rand()%360; - - for (i=0 ; i<3 ; i++) - org[i] += dist[i]*30; - d -= 30; - } - } - -} - - +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// cl_tent.c -- client side temporary entities + +#include "quakedef.h" + +int num_temp_entities; +entity_t cl_temp_entities[MAX_TEMP_ENTITIES]; +beam_t cl_beams[MAX_BEAMS]; + +sfx_t *cl_sfx_wizhit; +sfx_t *cl_sfx_knighthit; +sfx_t *cl_sfx_tink1; +sfx_t *cl_sfx_ric1; +sfx_t *cl_sfx_ric2; +sfx_t *cl_sfx_ric3; +sfx_t *cl_sfx_r_exp3; +#ifdef QUAKE2 +sfx_t *cl_sfx_imp; +sfx_t *cl_sfx_rail; +#endif + +/* +================= +CL_ParseTEnt +================= +*/ +void CL_InitTEnts (void) +{ + cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); + cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); + cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); + cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); + cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); + cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); + cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); +#ifdef QUAKE2 + cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav"); + cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav"); +#endif +} + +/* +================= +CL_ParseBeam +================= +*/ +void CL_ParseBeam (model_t *m) +{ + int ent; + vec3_t start, end; + beam_t *b; + int i; + + ent = MSG_ReadShort (); + + start[0] = MSG_ReadCoord (); + start[1] = MSG_ReadCoord (); + start[2] = MSG_ReadCoord (); + + end[0] = MSG_ReadCoord (); + end[1] = MSG_ReadCoord (); + end[2] = MSG_ReadCoord (); + +// override any beam with the same entity + for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) + if (b->entity == ent) + { + b->entity = ent; + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + +// find a free beam + for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) + { + if (!b->model || b->endtime < cl.time) + { + b->entity = ent; + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + } + Con_Printf ("beam list overflow!\n"); +} + +/* +================= +CL_ParseTEnt +================= +*/ +void CL_ParseTEnt (void) +{ + int type; + vec3_t pos; +#ifdef QUAKE2 + vec3_t endpos; +#endif + dlight_t *dl; + int rnd; + int colorStart, colorLength; + + type = MSG_ReadByte (); + switch (type) + { + case TE_WIZSPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 20, 30); + S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); + break; + + case TE_KNIGHTSPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 226, 20); + S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); + break; + + case TE_SPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); +#ifdef GLTEST + Test_Spawn (pos); +#else + R_RunParticleEffect (pos, vec3_origin, 0, 10); +#endif + if ( rand() % 5 ) + S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); + else + { + rnd = rand() & 3; + if (rnd == 1) + S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); + else if (rnd == 2) + S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); + else + S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); + } + break; + case TE_SUPERSPIKE: // super spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 0, 20); + + if ( rand() % 5 ) + S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); + else + { + rnd = rand() & 3; + if (rnd == 1) + S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); + else if (rnd == 2) + S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); + else + S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); + } + break; + + case TE_GUNSHOT: // bullet hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 0, 20); + break; + + case TE_EXPLOSION: // rocket explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_ParticleExplosion (pos); + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 350; + dl->die = cl.time + 0.5; + dl->decay = 300; + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + break; + + case TE_TAREXPLOSION: // tarbaby explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_BlobExplosion (pos); + + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + break; + + case TE_LIGHTNING1: // lightning bolts + CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); + break; + + case TE_LIGHTNING2: // lightning bolts + CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); + break; + + case TE_LIGHTNING3: // lightning bolts + CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); + break; + +// PGM 01/21/97 + case TE_BEAM: // grappling hook beam + CL_ParseBeam (Mod_ForName("progs/beam.mdl", true)); + break; +// PGM 01/21/97 + + case TE_LAVASPLASH: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_LavaSplash (pos); + break; + + case TE_TELEPORT: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_TeleportSplash (pos); + break; + + case TE_EXPLOSION2: // color mapped explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + colorStart = MSG_ReadByte (); + colorLength = MSG_ReadByte (); + R_ParticleExplosion2 (pos, colorStart, colorLength); + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 350; + dl->die = cl.time + 0.5; + dl->decay = 300; + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + break; + +#ifdef QUAKE2 + case TE_IMPLOSION: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + S_StartSound (-1, 0, cl_sfx_imp, pos, 1, 1); + break; + + case TE_RAILTRAIL: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + endpos[0] = MSG_ReadCoord (); + endpos[1] = MSG_ReadCoord (); + endpos[2] = MSG_ReadCoord (); + S_StartSound (-1, 0, cl_sfx_rail, pos, 1, 1); + S_StartSound (-1, 1, cl_sfx_r_exp3, endpos, 1, 1); + R_RocketTrail (pos, endpos, 0+128); + R_ParticleExplosion (endpos); + dl = CL_AllocDlight (-1); + VectorCopy (endpos, dl->origin); + dl->radius = 350; + dl->die = cl.time + 0.5; + dl->decay = 300; + break; +#endif + + default: + Sys_Error ("CL_ParseTEnt: bad type"); + } +} + + +/* +================= +CL_NewTempEntity +================= +*/ +entity_t *CL_NewTempEntity (void) +{ + entity_t *ent; + + if (cl_numvisedicts == MAX_VISEDICTS) + return NULL; + if (num_temp_entities == MAX_TEMP_ENTITIES) + return NULL; + ent = &cl_temp_entities[num_temp_entities]; + memset (ent, 0, sizeof(*ent)); + num_temp_entities++; + cl_visedicts[cl_numvisedicts] = ent; + cl_numvisedicts++; + + ent->colormap = vid.colormap; + return ent; +} + + +/* +================= +CL_UpdateTEnts +================= +*/ +void CL_UpdateTEnts (void) +{ + int i; + beam_t *b; + vec3_t dist, org; + float d; + entity_t *ent; + float yaw, pitch; + float forward; + + num_temp_entities = 0; + +// update lightning + for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) + { + if (!b->model || b->endtime < cl.time) + continue; + + // if coming from the player, update the start position + if (b->entity == cl.viewentity) + { + VectorCopy (cl_entities[cl.viewentity].origin, b->start); + } + + // calculate pitch and yaw + VectorSubtract (b->end, b->start, dist); + + if (dist[1] == 0 && dist[0] == 0) + { + yaw = 0; + if (dist[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]); + pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + // add new entities for the lightning + VectorCopy (b->start, org); + d = VectorNormalize(dist); + while (d > 0) + { + ent = CL_NewTempEntity (); + if (!ent) + return; + VectorCopy (org, ent->origin); + ent->model = b->model; + ent->angles[0] = pitch; + ent->angles[1] = yaw; + ent->angles[2] = rand()%360; + + for (i=0 ; i<3 ; i++) + org[i] += dist[i]*30; + d -= 30; + } + } + +} + + -- cgit v1.2.3