st/code/ui/ui_spacetrader.c
2008-04-04 00:00:00 +00:00

3756 lines
97 KiB
C

/*
===========================================================================
Copyright (C) 2006 HermitWorks Entertainment Corporation
This file is part of Space Trader source code.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
===========================================================================
*/
#include "ui_local.h"
//
// ui_spacetrader.c - this is the core rules engine. all access to trading and rules info
// is done through here
//
#include "../game/bg_spacetrader.h"
globalState_t gsi; // global state mirrored from server
extern menuStackDef_t ui_menuStack;
extern menuStackDef_t cg_menuStack;
extern vmCvar_t ui_initialized;
extern vmCvar_t ui_spacetrader;
static void st_shop_select_buy( void );
static void st_shop_select_sell( void );
static void st_shop_unselect_buy( void );
static void st_shop_unselect_sell( void );
#define MAX_PLANETS 16
#define MAX_CHALLENGES 256
//
// locally cached info
//
#define MAX_ANSWERS 4
typedef struct
{
int commodity_id;
void *shape;
int low_time; // x value when y is lowest
int low_price; // the lowest y value
int high_time; // x value when y is highest
int high_price; // the highest y value
} graph_t;
#define MAX_GRAPHS 256
#define MAX_GRAPH_POINTS 256
#define MAX_NEWS_BLURBS 16
#define MAX_SHOP_LIST 64 // max amount of commodities in one buy/sell list
#define MAX_TALK_LINES 32
typedef struct
{
int npc; // the npc id of the npc that this local client is interacting with
int npcIsGood;
int seed; // game seed used for generating random numbers
int selectedprofile;
int gametype;
qhandle_t cp[ 8 ];
int clientselected; // the client that the server has selected in the BeginTurn screen
int contact_index;
int MENU; // menu state
int menus_init;
struct
{
qhandle_t graphShader;
qhandle_t lineshadow;
qhandle_t youarehere;
qhandle_t orbitShader;
qhandle_t decoration_accuracy;
qhandle_t decoration_boss;
qhandle_t decoration_money;
qhandle_t decoration_stash;
qhandle_t decoration_time;
qhandle_t arrow_past_head;
qhandle_t arrow_past_tail;
qhandle_t calendar;
qhandle_t arrow_left_head;
qhandle_t arrow_left_tail;
qhandle_t playerBust;
effectDef_t * ready;
effectDef_t * notready;
} assets;
struct
{
solarsystemInfo_t ss;
const planetInfo_t * selected; // the planet under the mouse cursor
const planetInfo_t * voted; // the planet that this player wants to travel to
const planetInfo_t * current;
int selectLockTime; // selection can't change before this timeout expires
float totalTime;
enum
{
STZ_DEFAULT,
STZ_OUT,
STZ_IN,
} zoomState;
solarsystemEye_t eye[2];
int lerpTime[2];
struct
{
int stepTime;
int action;
} showcase;
int readyTime; // last time this user pressed the ready button
qhandle_t labels[ 16 ];
effectDef_t * label;
} travel;
struct
{
int fromExplore; // dialog with this bot was from explore mode
} dialog;
struct
{
lineInfo_t lines[256];
uint lineCount;
char text[8 * 1024];
uint textLen;
} chat;
struct
{
int count;
struct
{
const char * text;
qhandle_t icon;
} blurbs[ MAX_NEWS_BLURBS ];
int tickerIndex; // current news blurb on the ticker
int tickerTime; // time the current blurb started showing
} news;
graph_t graphs[MAX_GRAPHS];
struct
{
int bounty;
char bossname[ MAX_NAME_LENGTH ];
} assassinate;
struct
{
int client; // the client that is under the crosshair
qhandle_t effect;
effectDef_t * ally;
effectDef_t * enemy;
} crosshair;
playerInfo_t modelinfo;
sfxHandle_t buySound;
sfxHandle_t cantbuySound;
sfxHandle_t sellSound;
} spacetraderInfo_t;
static spacetraderInfo_t st;
static void UI_ST_ScanForMissions( void );
static void UI_ST_SelectPlanet( const planetInfo_t * p, itemDef_t * item );
extern void _UI_DrawRect( float x, float y, float width, float height, float size, const float *color );
static int UI_ST_GetMissionInt(const char *key)
{
int value;
if (sql( "SELECT value FROM missions WHERE key LIKE $", "tsId", key, &value) ) {
return value;
} else {
Com_Error(ERR_FATAL, "Key: %s not found in missions table", key );
return 0;
}
}
/*
static void UI_ST_MsgBox( const char * title, const char * text )
{
trap_Cvar_Set( "ui_msgbox_title", title );
trap_Cvar_Set( "ui_msgbox_text", text );
Menus_ActivateByName( "msgbox" );
}
*/
static void cache_solarsystem( solarsystemInfo_t * ss )
{
int i;
ss->planetCount = trap_SQL_Select( "SELECT "
"id, "
"planets_text.id[ id ]^name, "
"IF (event_end>GS_TIME) THEN events_text.id[ event_id ]^name ELSE '', "
"dfs, "
"op, "
"size, "
"sa, "
"travel_time, "
"event_id, "
"0,"
"0,"
"model ( 'models/' || name || '.x42' ), "
"shader( 'ui/starmap/' || name ), "
"shader( 'ui/starmap/' || name || 'atmosphere' ), "
"shader( 'ui/planets/' || name ), "
"0,"
"events_icons.id[ event_id ].icon, "
"is_visible, "
"can_travel_to==0 AND id!=GS_PLANET, "
"0, "
"0, "
"0, "
"0 "
"FROM planets;", (char*)ss->planets, sizeof(ss->planets) );
st.travel.current = 0;
// default to planet model
for ( i=0; i<ss->planetCount; i++ ) {
if ( !ss->planets[ i ].model ) {
ss->planets[ i ].model = trap_R_RegisterModel( ASSET_PLANETMODEL );
}
}
if( sql( "SELECT # FROM planets SEARCH id ?;", "i", GS_PLANET ) )
{
st.travel.current = ss->planets + sqlint(0);
sqldone();
}
}
qhandle_t iconForModel( const char * path )
{
char model[ MAX_QPATH ];
char * skin;
Q_strncpyz( model, path, sizeof(model) );
skin = Q_strrchr( model, '/' );
if ( skin ) {
*skin++ = '\0';
} else {
skin = "default";
}
return trap_R_RegisterShaderNoMip( va("models/players/%s/icon_%s", model, skin ) );
}
static void cache_news( void )
{
st.news.count = 0;
if (sql( "SELECT value FROM missions_text WHERE key LIKE 'description'", 0 ))
{
st.news.blurbs[ st.news.count++ ].text = String_Alloc( sqltext(0) );
sqldone();
}
/*
for ( i=0; i<GS_PLANETCOUNT; i++ )
{
const char * blurb;
if ( GS_PLANETS(i).event_end <= GS_TIME+GS_PLANETS(i).time )
continue;
if ( sql( "SELECT description,'ui/events/' || name FROM events_text WHERE id=?;", "i", GS_PLANETS(i).event_id ) )
{
blurb = String_Alloc( va("%s " S_COLOR_GREEN "%d" S_COLOR_WHITE " %s.\n", sqltext(0), GS_PLANETS(i).event_end-GS_TIME, st.travel.units ) );
st.travel.ss.planets[ i ].disaster = blurb;
st.travel.ss.planets[ i ].disaster_icon = trap_R_RegisterShaderNoMip( sqltext(1) );
if ( st.news.count < MAX_NEWS_BLURBS )
{
st.news.blurbs[ st.news.count ].icon = st.travel.ss.planets[ i ].disaster_icon;
st.news.blurbs[ st.news.count++ ].text = blurb;
}
sqldone();
}
}
*/
}
static void cache_icons()
{
sql( "DELETE FROM icons;", "e" );
sql( "INSERT INTO icons(client,npc,icon) SELECT client,id,portrait(model) FROM clients;", "e" );
sql( "INSERT INTO icons(client,npc,icon) SELECT -1,id,portrait(model) FROM npcs WHERE clients.id[ id ].client=-1;", "e" );
st.assets.playerBust = trap_R_RegisterShaderNoMip( va( "models/players/%s/bust_default", UI_Cvar_VariableString( "model" ) ) );
}
static void draw_pricegraph( itemDef_t * item, int commodity_id )
{
// icon water mark
if ( sql( "SELECT icon,chart FROM commodities_text SEARCH id ?;", "i", commodity_id ) ) {
float w = min( item->window.rect.w, item->window.rect.h ) * 0.75f;
uiInfo.uiDC.setColor( item->window.foreColor );
uiInfo.uiDC.drawHandlePic( item->window.rect.x + (item->window.rect.w - w ) * 0.65f,
item->window.rect.y + (item->window.rect.h - w ) * 0.5f,
w,w,
sqlint(0) );
UI_Chart_Paint( item, sqlint(1) );
sqldone();
}
}
static void insert_prices( graphGen_t *gg, int x1, int x2, float price1, float price2, float range )
{
int x = x1 + ((x2-x1)>>1);
float y = max( 0.1f, price1 + ((price2-price1)*0.5f) + ((Q_random()-0.5f)*2.0f * range) );
gg->pts[ x ][ 1 ] = y;
gg->low = min( gg->low, (int)y );
gg->high = max( gg->high, (int)y );
if ( x1 != x ) insert_prices( gg, x1, x-1, price1, y, range * 0.64f );
if ( x2 != x ) insert_prices( gg, x+1, x2, y, price2, range * 0.64f );
}
//
// create graphs for all the commodities
//
static void cache_pricegraphs( int time1, int time2, int n )
{
graphGen_t gg;
qhandle_t chart;
vec4_t colorLow = {0.9f, 0.7f, 0, 1};
vec4_t colorHigh = {1, 1, 0, 1};
while( sql( "SELECT id,low,high FROM commodities;", 0 ) ) {
int commodity_id = sqlint(0);
int low = sqlint(1);
int high = sqlint(2);
int i,p1,p2,x1,x2;
UI_Graph_Init( &gg );
// plot prices
for ( i=x1=x2=p1=p2=0; sql( "SELECT time,price FROM prices SEARCH time ?2,?3 WHERE commodity_id=?1;", "iii", commodity_id, time1, time2 ); i++ ) {
int time = sqlint(0);
x2 = ((time-time1)*(n-1)) / (time2-time1);
p2 = sqlint(1);
low = min( low, p2 );
high = max( high, p2 );
if ( i > 0 ) {
gg.pts[ x1 ][ 1 ] =(float)p1;
gg.pts[ x2 ][ 1 ] =(float)p2;
insert_prices( &gg, x1+1, x2-1, (float)p1, (float)p2, fabsf( (float)(high-low) ) * 0.38f );
x1 = x2+1;
}
p1 = p2;
}
// uv coordinates and colors
for ( i=0; i<n; i++ )
{
gg.pts[ i ][0] = (float)i;
gg.uvs[ i ][0] = (float)i/(float)n;
gg.uvs[ i ][1] = 0.0f;
Vec4_Lrp( gg.colors[ i ], colorLow, colorHigh, (gg.pts[ i ][1]-gg.low) / (float)(gg.high-gg.low) );
}
gg.n = n;
chart = UI_Chart_Create( commodity_id, 0, n, gg.low * 0.9f, gg.high * 1.1f );
UI_Chart_AddGraph( chart, &gg, SHAPEGEN_LINE, st.assets.graphShader );
for ( i=0; i<=4; i++ ) {
float v = (((gg.high-gg.low)*i)/4.0f) + gg.low;
UI_Chart_AddLabel( chart, 0, v, fn( (int)v, FN_CURRENCY ) );
}
sql( "UPDATE commodities_text SET chart=?2 SEARCH id ?1;", "iie", commodity_id, chart );
}
}
static void cache_buylist()
{
while( sql( "SELECT commodity_id, commodities_effects.commodity_id[ commodity_id ].buy_effect, todaysprices.commodity_id[ commodity_id ].range FROM commodities_npcs WHERE npc_id=?;", "i", st.npc ) )
{
int id = sqlint(0);
qhandle_t h = sqlint(1);
int range = sqlint(2);
effectDef_t * effect = 0;
UI_Effect_SetFlag( h, EF_DELETEME );
if ( range < 2 ) effect = UI_Effect_Find( "buy_rockbottom" );
else if ( range < 25 ) effect = UI_Effect_Find( "buy_cheap" );
if ( effect ) {
h = UI_Effect_SpawnText( 0, effect, 0, 0 );
UI_Effect_SetFlag( h, EF_ONEFRAME_RECT );
sql( "UPDATE OR INSERT commodities_effects SET buy_effect=?2,commodity_id=?1 SEARCH commodity_id ?1;", "iie", id, h );
}
}
sql( "DELETE FROM buylist;", "e" );
sql( "INSERT INTO buylist(id,avg_price,price,change,range) "
"SELECT "
"commodity_id, "
"(commodities.id[ commodity_id ].high+commodities.id[ commodity_id ].low)/2, "
"todaysprices.commodity_id[ commodity_id ].price, "
"todaysprices.commodity_id[ commodity_id ].change, "
"todaysprices.commodity_id[ commodity_id ].range "
"FROM commodities_npcs SEARCH npc_id conversations.id[ 0 ].npc;", "e" );
}
static void cache_selllist()
{
while( sql( "SELECT commodity_id, commodities_effects.commodity_id[ commodity_id ].sell_effect, todaysprices.commodity_id[ commodity_id ].range FROM commodities_npcs WHERE npc_id=?;", "i", st.npc ) )
{
int id = sqlint(0);
qhandle_t h = sqlint(1);
int range = sqlint(2);
effectDef_t * effect = 0;
UI_Effect_SetFlag( h, EF_DELETEME );
if ( range > 95 ) effect = UI_Effect_Find( "sell_high" );
else if ( range > 75 ) effect = UI_Effect_Find( "sell_ok" );
if ( effect ) {
h = UI_Effect_SpawnText( 0, effect, 0, 0 );
UI_Effect_SetFlag( h, EF_ONEFRAME_RECT );
sql( "UPDATE OR INSERT commodities_effects SET sell_effect=?2,commodity_id=?1 SEARCH commodity_id ?1;", "iie", id, h );
}
}
st_shop_select_sell();
}
static void UI_ST_kickLaggingPlayers() {
while ( sql( "SELECT client FROM clients WHERE lag=1;", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_APPEND, va("kick %d; wait;", sqlint(0) ) );
}
}
static void UI_ST_IntegerPrint( int number, effectDef_t *fxd )
{
rectDef_t r;
r.x = 0.0F;
r.y = 0.0F;
r.w = 640.0F;
r.h = 460.0F;
UI_Effect_SpawnText( &r, fxd, va("%d", number), 0 );
}
static void UI_ST_CenterPrint( const char *text, effectDef_t *fxd )
{
uint i;
for( i = 0; i < lengthof( st.cp ); i++ )
{
uint c;
if( i & 1 )
{
c = lengthof( st.cp ) / 2 + i / 2;
}
else
{
c = lengthof( st.cp ) / 2 - i / 2 - 1;
}
if( !UI_Effect_IsAlive( st.cp[c] ) )
{
rectDef_t r;
r.x = -uiInfo.uiDC.glconfig.xbias;
r.y = (480.0F - 32.0F * lengthof( st.cp )) * 0.5F + c * 32.0F;
r.w = 640.0F + uiInfo.uiDC.glconfig.xbias*2.0f;
r.h = 32.0F;
st.cp[c] = UI_Effect_SpawnText( &r, fxd, text, 0 );
break;
}
}
}
#if 0 // broken, was never actually used
// convert from a message sent from the server(from the map), to a string
static const char * UI_Hint( const char *hint )
{
switch( SWITCHSTRING((const char*)hint) )
{
// crouch
case CS('c','r','o','u'):
{
int key;
char keyname[32];
key = trap_Key_GetKey("+movedown");
trap_Key_KeynumToStringBuf( key, keyname, 32 );
return va("Press '%s' to crouch", keyname);
} break;
// jump
case CS('j','u','m','p'):
{
int key;
char keyname[32];
key = trap_Key_GetKey("+moveup");
trap_Key_KeynumToStringBuf( key, keyname, 32 );
return va("Press '%s' to jump", keyname);
} break;
// move
case CS('m','o','v','e'):
{
return "Press the WASD keys to move";
} break;
// mouse
case CS('m','o','u','s'):
{
return "Use the mouse to look around";
} break;
// chat
case CS('c','h','a','t'):
{
int key;
char keyname[32];
key = trap_Key_GetKey("+attack");
trap_Key_KeynumToStringBuf( key, keyname, 32 );
return va("Press '%s' when facing someone to interact with them", keyname);
} break;
// shoot
case CS('s','h','o','o'):
{
int key;
char keyname[32];
key = trap_Key_GetKey("+attack");
trap_Key_KeynumToStringBuf( key, keyname, 32 );
return va("Press '%s' to attack", keyname);
} break;
}
return hint;
}
#endif
static void UI_ST_ChatPrint( int client, const char *msg )
{
const char *str;
if( sql( "SELECT n FROM clients SEARCH client ?", "i", client ) )
{
str = va( "%s: %s\n", sqltext( 0 ), msg );
trap_Con_Print( 1, str );
sqldone();
}
}
static void resize_contacts( menuDef_t * menu, int n ) {
itemDef_t *r, *p, *c;
itemDef_t * retire;
float b;
if ( !menu )
return;
r = Menu_FindItemByName( menu, "ready" );
p = Menu_FindItemByName( menu, "otherplayers" );
c = Menu_FindItemByName( menu, "contactlist" );
retire = Menu_FindItemByName( menu, "retire" );
if ( sql( "SELECT 1 FROM players SEARCH client %client WHERE objective=1;", 0 ) ) {
sqldone();
// make room for the retire button
b = retire->window.rect.y - 4.0f;
} else
b = retire->window.rect.y + retire->window.rect.h;
if ( n > 1 ) {
// multiplayer, size player list to fit number of players. contact list gets the rest
listBoxDef_t * lp = (listBoxDef_t*)p->typeData;
p->window.rect.h = lp->titlebar + lp->elementHeight*(n+0.25f);
b -= p->window.rect.h;
p->window.rect.y = b;
// adjust ready button
b -= (r->window.rect.h + 8.0f);
r->window.rect.y = b;
b -= 8.0f;
p->window.rectClient.y = p->window.rect.y - menu->window.rect.y;
}
// adjust height of contact list
c->window.rect.h = b-c->window.rect.y;
//
c->window.rectClient.y = c->window.rect.y - menu->window.rect.y;
c->window.rectClient.h = c->window.rect.h;
}
#if 0
static void readyUpEffect() {
int i;
for ( i=0; i<GS_PLAYERCOUNT; i++ ) {
int isReady = GS_READY&(1<<i);
effectDef_t * def = (isReady)?st.assets.ready:0;
qhandle_t h = 0;
sql( "SELECT ready_effect FROM clients_effects SEARCH client ?;", "isId", i,&h );
// if the current effect is not right then...
if ( def != UI_Effect_GetEffect( h ) ) {
// kill old effect
UI_Effect_SetFlag( h, EF_NOFOCUS );
// spawn new effect
if ( def ) {
h = UI_Effect_SpawnText( 0, def, 0, 0 );
}
}
sql( "UPDATE OR INSERT clients_effects SET ready_icon=?2, ready_effect=?3, client=?1 SEARCH client ?1;",
"iiie",
i,
isReady?st.assets.ready->shader:st.assets.notready->shader,
h
);
}
}
#endif
// a command has been sent from the server, try to process it
static int consolecommand( const char * cmd )
{
switch( SWITCHSTRING((const char*)cmd) )
{
// st_newcontact < npc_id >
case CS( 'n', 'e', 'w', 'c' ):
{
effectDef_t *fxd = UI_Effect_Find( "newcontact" );
if( !fxd )
fxd = UI_Effect_Find( "cp" );
if ( sql( "SELECT T_New_Contact;", 0 ) ) {
UI_ST_CenterPrint( sqltext(0), fxd );
sqldone();
}
}
break;
// st_notime
case CS('n','o','t','i'):
{
Menus_ActivateByName( "retireconfirm" );
} break;
// st_trade <npc_id>
case CS('t','r','a','d'):
{
// the server is telling the client to open a dialog box to begin trading with an npc
menuDef_t * m = Menus_FindByName( "travel_menu" );
if ( m )
{
int bot;
// active the travel menu
Menus_Close( m );
Menus_Activate( m );
Menus_ActivateByName( "merchant_page" );
Menus_ActivateByName( "shop_item_details" );
// remember who the client is trading with
st.npc = trap_ArgvI( 1 );
bot = trap_ArgvI( 2 );
sql( "SELECT good FROM npcs WHERE id=?;", "isId", st.npc, &st.npcIsGood );
// cache this npc's goods
cache_buylist();
cache_selllist();
// initialize buying state
st_shop_unselect_buy();
st_shop_unselect_sell();
trap_Cmd_ExecuteText( EXEC_NOW, va("st_lookat %d ;", bot) );
}
} break;
// st_awards
// the trading round has ended and the server is asking the client to display the awards
// for that round
//
case CS('a','w','a','r'):
{
switch ( st.gametype ) {
case GT_HUB:
Menus_FadeAllOutAndFadeIn( "trading_awards" );
break;
case GT_MOA:
case GT_ASSASSINATE:
Menus_FadeAllOutAndFadeIn( "assassinate_awards" );
break;
}
} break;
// st_planet - the server's response from selecting a planet
case CS('p','l','a','n'):
{
int resp;
menuDef_t *m;
m = Menus_FindByName( "travel_page" );
if( !m || !(m->window.flags & WINDOW_VISIBLE) )
break;
resp = trap_ArgvI( 1 );
if( resp >= 0 )
Menus_ActivateByName( "travel_details" );
switch( resp )
{
case 0: Menus_ActivateByName( "travelconfirm" ); break;
case 1: Menus_ActivateByName( "travelnotallready" ); break;
case 2: Menus_ActivateByName( "traveltoofar" ); break;
case 3: Menus_ActivateByName( "travelalreadythere" ); break;
}
} break;
// st_dialog <close/open>"
case CS('d','i','a','l'):
{
menuDef_t * travel_menu = Menus_FindByName( "travel_menu" );
menuDef_t * dialog_menu = Menus_FindByName( "dialog_menu" );
switch( SWITCHSTRING( UI_Argv(1) ) )
{
case CS('c','l','o','s'):
{
Menus_Close( dialog_menu );
if ( st.dialog.fromExplore )
Menus_Close( travel_menu );
st.MENU = 0;
st.dialog.fromExplore = 0;
} break;
case CS('o','p','e','n'):
{
// remember if the dialog was initiated from explore mode
if ( !(trap_Key_GetCatcher() & KEYCATCH_UI) )
{
st.dialog.fromExplore = 1;
if ( sql( "SELECT bot FROM conversations;", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_NOW, va( "st_lookat %d ;", sqlint(0) ) );
sqldone();
}
}
Menus_CloseAll();
Menus_Activate( travel_menu );
Menus_Activate( dialog_menu );
} break;
}
} break;
// st_jobs
case CS('j','o','b','s'):
{
//int bot_id = trap_ArgvI( 1 );
//int npc_id = trap_ArgvI( 2 );
Menus_CloseAll();
Menus_ShowByName( "travel_menu" );
Menus_ShowByName( "jobs_menu" );
} break;
case CS('p','o','s','t'): // st_postgame
{
// store the summary so when we get kicked from the game
// we still have the stats to show in the end.
while ( sql( "SELECT 'st_summary'||client:plain, "
"'\\client\\' || client:plain || "
"'\\place\\' || rank:plain || "
"'\\networth\\' || networth:plain || "
"'\\name\\' || clients.client[ client ]^n || "
"'\\me\\' || (client==%client):plain "
"FROM players;", 0 ) ) {
trap_Cvar_Set( sqltext(0), sqltext(1) );
}
if ( sql( "SELECT value FROM missions SEARCH key 'number';", 0) ) {
trap_Cvar_Set( "st_summary", sqltext(0) );
sqldone();
}
// record a high score in local cvar's to unlock maps!!
if ( sql( "SELECT networth, 'score_'||missions.key['number']^value, missions.key['par_score'].value, missions.key['number'].value FROM players SEARCH client %client;", 0 ) )
{
int ui_scoreVal;
int score = sqlint(0);
const char* var = sqltext(1);
const char* val = sqltext(0);
int par = sqlint(2);
int number = sqlint(3);
int current;
trap_Cvar_Register( NULL, var, "0", CVAR_ARCHIVE );
current = trap_Cvar_VariableInt( var );
if ( current < score ) {
trap_Cvar_Set( var, val );
}
/* current old high score
par score to beat
score score you just got
ui_score
Add one element from each section:
1 no new high score
2 score is the same as the last high score
3 player set a high score
10 next challenge was and still is locked
20 next challenge has just been unlocked
30 next challenge was and still is unlocked
*/
ui_scoreVal = 0;
if( score < current )
ui_scoreVal += 1;
else if( score > current )
ui_scoreVal += 3;
else //they are equal
ui_scoreVal += 2;
if ( number < 5 ) {
if( current < par && score < par )
ui_scoreVal += 10;
else if( current < par && score >= par )
ui_scoreVal += 20;
else if( current >= par )
ui_scoreVal += 30;
}
trap_Cvar_Set( "ui_score", va( "%d", ui_scoreVal ) );
trap_Cvar_Set( "ui_best", fn( current, FN_CURRENCY ) );
sqldone();
}
} break;
case CS('s','c','o','r'): // score
{
trap_SQL_Prepare ( "INSERT INTO scores(player,networth,cash,debt) VALUES(?,?,?,?);" );
trap_SQL_BindArgs ();
trap_SQL_Step ();
trap_SQL_Done ();
} break;
// st_startgame - server is saying the game is starting...
case CS('s','t','a','r'):
{
Menus_CloseByName( "WaitForPlayers" );
} break;
// st_bank - bank transaction done, update the gui
case CS('b','a','n','k'):
{
menuDef_t * menu = Menus_FindByName( "shop_item_details" );
if ( menu )
{
itemDef_t * item = Menu_FindItemByName( menu, "buyQty" );
// update the buy screen if it's visible
if ( menu->window.flags & WINDOW_VISIBLE && item && item->window.flags & WINDOW_VISIBLE ) {
st_shop_select_buy();
}
}
} break;
// a table has changed
// st_table
case CS('t','a','b','l'):
{
switch( SWITCHSTRING( UI_Argv(1) ) )
{
case CS('i','n','v','e'):
cache_selllist();
break;
case CS('c','o','n','t'):
sql( "UPDATE feeders SET id=-1,sel=-1 SEARCH name 'contactlist';", "e" );
break;
}
} break;
// st_moa - players have just finished battling the MoA
case CS('m','o','a',0):
{
int won = trap_ArgvI( 1 );
Menus_CloseAll();
Menus_OpenByName( won?"moa_won":"moa_lose" );
} break;
// st_chat - someone's saying something at the connect screen
case CS('c','h','a','t'):
{
int client = trap_ArgvI( 1 );
const char *msg = UI_Argv( 2 );
UI_ST_ChatPrint( client, msg );
} break;
//fallthrough to st_talk intended
// st_beginturn
case CS('b','e','g','i'):
{
Menus_FadeAllOutAndFadeIn( "travel_menu" );
Menus_FadeAllOutAndFadeIn( "status_page" );
if ( GS_TURN == 1 ) {
trap_Cmd_ExecuteText( EXEC_NOW, "updateserver 2" );
}
#ifdef USE_DRM
if ( trap_Cvar_VariableInt( "com_keyvalid" ) == 0 && GS_TURN > 1 ) {
Menus_FadeAllOutAndFadeIn( "registergame" );
}
#endif
} break;
// st_won
case CS('w','o','n',0):
{
Menus_ActivateByName( "missioncomplete" );
} break;
// st_lost
case CS('l','o','s','t'):
{
Menus_ActivateByName( "missionfailed" );
} break;
// st_cash
case CS('c','a','s','h'):
{
effectDef_t *effect = UI_Effect_Find( "stash" );
int qty = trap_ArgvI( 1 );
UI_ST_CenterPrint( fn( qty, FN_CURRENCY ), effect );
} break;
// st_lag
case CS('l','a','g',0):
{
qboolean show_lag = trap_ArgvI( 1 );
if ( show_lag == qtrue ) {
Menus_ActivateByName( "net_dc" );
} else {
Menus_CloseByName( "net_dc" );
}
} break;
// st_cp
case CS('c','p',0,0 ):
{
effectDef_t *fxd;
fxd = UI_Effect_Find( UI_Argc() > 2 ? UI_Argv( 2 ) : "cp" );
if ( sql( "SELECT index FROM strings SEARCH name $;", "t", UI_Argv(1) ) ) {
char t[ MAX_INFO_STRING ];
trap_SQL_Run( t, sizeof(t), sqlint(0), trap_ArgvI(3),0,0 );
UI_ST_CenterPrint( t, fxd );
sqldone();
} else {
UI_ST_CenterPrint( UI_Argv( 1 ), fxd );
}
} break;
#if 0 // broken, was never actually used
// st_hint
case CS('h','i','n','t' ):
{
effectDef_t *fxd;
const char *message;
fxd = UI_Effect_Find( UI_Argc() > 2 ? UI_Argv( 2 ) : "hint" );
message = UI_Hint( UI_Argv( 1 ) );
Com_Printf( va("[NOTICE] %s\n", message ) );
UI_ST_CenterPrint( message, fxd );
} break;
#endif
// st_int
case CS('i','n','t',0 ):
{
effectDef_t *fxd;
fxd = UI_Effect_Find( UI_Argc() > 2 ? UI_Argv( 2 ) : "int" );
Com_Printf( va("[NOTICE] %d\n", atoi( UI_Argv( 1 ) ) ) );
UI_ST_IntegerPrint( atoi(UI_Argv( 1 )), fxd );
} break;
// st_crosshair
case CS('c','r','o','s'):
{
effectDef_t * e;
UI_Effect_SetFlag( st.crosshair.effect, EF_NOFOCUS );
st.crosshair.client = trap_ArgvI( 1 );
if ( (st.gametype == GT_FFA) || (st.gametype == GT_ASSASSINATE && st.crosshair.client >= MAX_PLAYERS) )
e = st.crosshair.enemy;
else
e = st.crosshair.ally;
if ( e->rect.x == 0.0f ) {
e->rect.x = -uiInfo.uiDC.glconfig.xbias;
}
if ( e->rect.w == 640.0f ) {
e->rect.w = 640.0f + uiInfo.uiDC.glconfig.xbias * 2.0f;
}
if ( sql( "SELECT n FROM clients SEARCH client ?;", "i", st.crosshair.client ) ) {
st.crosshair.effect = UI_Effect_SpawnText( &e->rect, e, sqltext(0), 0 );
sqldone();
}
} break;
// st_lagging
case CS('l','a','g','g'):
{
int lagging = 0;
sql( "SELECT COUNT(*) FROM clients WHERE lag=1;", "sId", &lagging );
if ( lagging ) {
Menus_OpenByName( "waiting_for_players" );
} else {
Menus_CloseByName( "waiting_for_players" );
}
} break;
// st_reportscore
case CS('r','e','p','o'):
{
if (trap_Cvar_VariableInt("ui_logged_in") == 1 ) {
trap_Cmd_ExecuteText( EXEC_NOW, va("highscore %d %d %d %d %d %d", atoi(UI_Argv(1)), atoi(UI_Argv(2)), atoi(UI_Argv(3)), atoi(UI_Argv(4)), atoi(UI_Argv(5)), atoi(UI_Argv(6))));
}
} break;
// st_inspect
case CS('i','n','s','p'):
{
Menus_ActivateByName( "cheat" );
}break;
#if DEVELOPER
// st_dumptext
case CS('d','u','m','p'):
{
trap_Print( "Dialog\n" );
while( sql( "SELECT id,name,voice,model,spawn FROM npcs SEARCH name;",0 ) ) {
int npc_id = sqlint(0);
const char * name = sqltext(1);
const char * voice = sqltext(2);
const char * model = sqltext(3);
const char * spawn = sqltext(4);
trap_Print( va("[%3d] %s (model:'%s' voice: '%s' spawn: '%s')\n\n", npc_id, name, model, voice, spawn ) );
while ( sql( "SELECT id,dialogs_text.id[id]^text,priority FROM dialogs SEARCH npc_id ?;", "i", npc_id ) ) {
int dialog_id = sqlint(0);
const char * dialog_text = sqltext(1);
const char * priority = sqltext(2);
if ( priority && priority[ 0 ] ) {
trap_Print( va( " %d. (%s)\n%s\n", dialog_id, priority, dialog_text ) );
} else {
trap_Print( va( " %d. %s\n", dialog_id, dialog_text ) );
}
while ( sql( "SELECT line,text FROM dialogs_answers SEARCH id ? SORT 1;", "i", dialog_id ) ) {
int line = sqlint(0);
const char * text = sqltext(1);
/*
if ( sql( "SELECT cmd FROM dialogs_cmds SEARCH id ?1 WHERE line=?2", "ii", dialog_id, line ) ) {
const char * cmd = sqltext(0);
int dialog_next = 0;
const char * next = Q_strstr( cmd, 256, "say " );
if ( next ) {
if ( sql( "SELECT id FROM dialogs SEARCH name $;", "t", COM_Parse( &next ) ) ) {
dialog_next = sqlint(0);
sqldone();
}
}
if ( dialog_next > 0 ) {
trap_Print( va( " %d. %s [%d] (%s)\n", line, text, dialog_next, cmd ) );
} else {
trap_Print( va( " %d. %s (%s)\n", line, text, cmd ) );
}
sqldone();
}
*/
trap_Print( va( " %d. %s\n", line, text ) );
}
trap_Print( "\n" );
}
}
}break;
#endif
default:
return 0; // unkown command
}
return 1; // command has been accepted
}
static void UI_ST_PlayerSelectSlot_SetModel( playerInfo_t *info, char * model )
{
vec3_t viewangles;
vec3_t moveangles;
memset( info, 0, sizeof(playerInfo_t) );
viewangles[YAW] = 180.0f - 10.0f;
viewangles[PITCH] = 0.0f;
viewangles[ROLL] = 0.0f;
VectorClear( moveangles );
UI_PlayerInfo_SetModel( info, model, "", 0 );
UI_PlayerInfo_SetInfo( info, LEGS_IDLE, LEGS_IDLE, viewangles, vec3_origin, WP_PISTOL, qfalse );
}
static void Menu_Item_RunScript( const char * menuName, const char * itemName, const char * script )
{
menuDef_t * menu = Menus_FindByName( menuName );
if ( menu )
{
itemDef_t * item = Menu_FindItemByName( menu, itemName );
if ( item )
{
Item_RunScript( item, script );
}
}
}
static ID_INLINE void SolarSystemEye_Cpy( solarsystemEye_t *o, const solarsystemEye_t *i )
{
VectorCopy( i->lookAt, o->lookAt );
Quat_Cpy( o->direction, i->direction );
o->radius = i->radius;
o->fov = i->fov;
}
void UI_ST_SolarSystemBeginTransfer( const solarsystemEye_t *dest, int time )
{
SolarSystemEye_Cpy( &st.travel.eye[0], &st.travel.ss.eye );
SolarSystemEye_Cpy( &st.travel.eye[1], dest );
st.travel.lerpTime[0] = uiInfo.uiDC.realTime;
st.travel.lerpTime[1] = uiInfo.uiDC.realTime + time;
}
void UI_ST_SolarSystemSnapEye( const solarsystemEye_t *eye )
{
SolarSystemEye_Cpy( &st.travel.eye[0], eye );
SolarSystemEye_Cpy( &st.travel.eye[1], eye );
SolarSystemEye_Cpy( &st.travel.ss.eye, eye );
UI_ST_SolarSystemBeginTransfer( eye, 0 );
}
static void st_shop_unselect_buy( void ) {
sql( "UPDATE feeders SET sel=-1, id=-1 SEARCH name 'buylist';", "e" );
trap_Cvar_Set( "ui_buying", "0" );
}
static void st_shop_unselect_sell( void ) {
sql( "UPDATE feeders SET sel=-1, id=-1 SEARCH name 'selllist';", "e" );
trap_Cvar_Set( "ui_buying", "0" );
}
static void st_shop_select_buy( void ) {
if ( sql( "SELECT feeders.name[ 'buylist' ].id;", 0 ) ) {
int id = sqlint(0);
sqldone();
if ( id == -1 ) {
return;
}
}
st_shop_unselect_sell();
if ( sql( "SELECT "
"todaysprices.commodity_id[ feeders.name[ 'buylist' ].id ].price, "
"cargomax, "
"cargo, "
"cash, "
"commodities.abbreviation[ 'CH' ].id = feeders.name[ 'buylist' ].id "
"FROM players SEARCH client %client;", 0 ) ) {
int price = sqlint(0);
int cargo_max = sqlint(1);
int cargo_used = sqlint(2);
int cash = sqlint(3);
int cargo_hold = sqlint(4);
sqldone();
if ( cargo_max-cargo_used == 0 && !cargo_hold ) {
// player doesn't have any room in carg hold
trap_Cvar_Set( "ui_buying", "4" );
} else if ( price > cash ) {
// player doesn't have enough money to buy any
trap_Cvar_Set( "ui_buying", "3" );
} else {
// adjust the item details to show selected item
int buyMax;
trap_Cvar_Set( "ui_buying", "1" );
if ( cargo_hold ) {
buyMax = min( UI_ST_GetMissionInt( "max_cargo" ) - cargo_max, cash / price );
if (buyMax == 0) {
trap_Cvar_Set( "ui_buying", "7" );
}
} else {
buyMax = min( cargo_max-cargo_used, cash / price );
}
UI_ActivateEditDlg( "shop_item_details", "buyQty", (float)buyMax, 1.0f, (float)buyMax, qfalse );
}
}
}
// the feeder has something selected, do some house keeping to reflect what's selected
static void st_shop_select_sell( void ) {
if ( sql( "SELECT feeders.name[ 'selllist' ].id;", 0 ) ) {
int commodity_id = sqlint(0);
sqldone();
// nothing is selected
if ( commodity_id == -1 ) {
st_shop_unselect_sell();
return;
}
if ( sql( "SELECT commodities.id[ ?1 ].good = npcs.id[ conversations.id[ 0 ].npc ].good, inventory.commodity_id[ ?1 ].qty;", "i", commodity_id ) )
{
int willbuy = sqlint(0);
int qty = sqlint(1);
sqldone();
// something is selected on sell list, make sure buylist is not selected
st_shop_unselect_buy();
if ( !willbuy ) {
// merchant refuses to buy commodity
trap_Cvar_Set( "ui_buying", "5" );
return;
}
// if cargo holds are selected, find out howmany can be sold, can't sell full ones
if ( sql( "SELECT min(cargomax-cargo,?2) FROM players "
"SEARCH client %client "
"WHERE commodities.abbreviation[ 'CH' ].id=?1;", "ii", commodity_id, qty ) )
{
qty = sqlint(0);
sqldone();
if ( qty == 0 ) {
// cargo holds in use
trap_Cvar_Set( "ui_buying", "6" );
return;
}
}
UI_ActivateEditDlg( "shop_item_details", "sellQty", (float)qty, 1.0f, (float)qty, qfalse );
trap_Cvar_Set( "ui_buying", "2" );
}
}
}
//
// PROCESS MENU SCRIPT COMMANDS
//
static void scriptcommand( const char * cmd, const char ** args )
{
switch( SWITCHSTRING( cmd ) )
{
//st_awards
case CS( 'a','w','a','r'):
{
int players;
sql("SELECT COUNT(*) FROM players;", "sId", &players);
if (players > 1 ) {
if ( sql( "SELECT client=%client FROM players SEARCH stash DESC LIMIT 1;", 0) ) {
if (sqlint(0) == 1) {
trap_Cmd_ExecuteText( EXEC_NOW, "st_announcer award_stash 1500\n" );
}
sqldone();
}
if ( sql( "SELECT client=%client FROM players SEARCH readytime WHERE readytime < missions.key['tradetime'].value*1000 LIMIT 1;", 0 ) ) {
if (sqlint(0) == 1) {
trap_Cmd_ExecuteText( EXEC_NOW, "st_announcer award_fastest 1500\n" );
}
sqldone();
}
if ( sql( "SELECT rank:rank, rank==COUNT FROM players SEARCH client %client;", 0 ) ) {
if (sqlint(1) == 1) {
trap_Cmd_ExecuteText( EXEC_NOW, "st_announcer last 1500\n" );
} else {
trap_Cmd_ExecuteText( EXEC_NOW, va("st_announcer %s 1000\n", sqltext(0) ) );
}
sqldone();
}
}
}
break;
// st_results
case CS( 'r','e','s','u'):
{
int profit, networth;
float change;
char *sound;
if ( sql("SELECT SUM((todaysprices.commodity_id[ commodity_id ].price-cost)*qty) FROM inventory;", 0) ){
profit = sqlint(0);
sql("SELECT max(cash+inventory,missions.key['wealth'].value) FROM players SEARCH client %client;", "sId", &networth);
sqldone();
networth -= profit;
change = profit/(float)networth;
if ( change > 2.0f ) {
sound = "trade_mega";
} else if ( change > 1.5f ) {
sound = "trade_awesome";
} else if ( change > 1.0f ) {
sound = "trade_excellent";
} else if ( change > 0.5f ) {
sound = "trade_good";
} else {
sound = "trade_notgreat";
}
if ( sql( "SELECT snd FROM npcs_voices SEARCH cmd $1;", "t", sound ) ) {
trap_S_StartLocalSound(sqlint(0), CHAN_ANNOUNCER);
}
}
}
break;
//st_endofgame
case CS( 'e', 'n', 'd', 'o' ):
{
int number = trap_Cvar_VariableInt("st_summary");
if ( sql("SELECT networth, challenges.number[ ?1 ].par_score FROM summary;", "i", number ) ) {
int networth, par_score;
float ratio;
char *sound;
char *rank;
networth = sqlint(0);
par_score = sqlint(1);
sqldone();
ratio = networth/(float)par_score;
if (ratio > 5.0f ) {
sound = "rank_inside"; // 500% -
rank = "Inside Trader";
} else if ( ratio > 3.0f ) {
sound = "rank_grandmaster"; // 300% - 500%
rank = "Grandmaster Trader";
} else if ( ratio > 1.0f ) {
sound = "rank_master"; // 100% - 300%
rank = "Master Trader";
} else if ( ratio > 0.5f ) {
sound = "rank_bluecollar"; // 50% - 100%
rank = "Bluecollar Trader";
} else if ( ratio > 0.25f ) {
sound = "rank_hobo"; // 25% - 50%
rank = "Hobo Trader";
} else {
sound = "rank_bum"; // 0 - 25%
rank = "Bum Trader";
}
trap_Cvar_Set( "ui_rank", rank );
trap_Cvar_Set( "ui_par", fn( par_score, FN_CURRENCY ) );
trap_S_StartLocalSound(trap_S_RegisterSound( va("sound/voices/announcer/%s.ogg",sound), qfalse), CHAN_ANNOUNCER);
}
} break;
//st_url
case CS( 'u', 'r', 'l', 0 ):
{
const char *url;
String_Parse( args, &url );
if( trap_Cvar_VariableInt( "r_fullscreen" ) )
trap_Cmd_ExecuteText( EXEC_INSERT, va( "set r_fullscreen 0; vid_restart; openurl \"%s\"", url ) );
else
trap_Cmd_ExecuteText( EXEC_INSERT, va( "openurl \"%s\"", url ) );
}
break;
// st_resetLogin
case CS('r','e','s','e'):
{
trap_Cvar_Set( "ui_logged_in", "-1" );
trap_Cvar_Set( "com_sessionid", "" );
uiInfo.charsLoaded = false;
} break;
case CS('s','h','o','p'):
{
switch( SWITCHSTRING( cmd+5 ) )
{
// st_shop_buy
case CS('b','u','y',0):
{
if ( trap_Cvar_VariableInt( "ui_buying" ) == 1 ) {
// get purchase quantity from ui dialog
if ( sql( "SELECT feeders.name[ 'buylist' ].id, feeders.name[ 'buyqty' ].sel;", 0 ) ) {
int commodity_id = sqlint(0);
int bcnt = sqlint(1);
// send a command to server to process the transaction
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_buy %i %i;", commodity_id, bcnt ) );
// unselect commodity
sql( "UPDATE feeders SET sel=-1,id=-1 SEARCH name 'buylist';", "e" );
trap_Cvar_Set( "ui_buying", "0" );
sqldone();
trap_S_StartLocalSound( st.buySound, CHAN_LOCAL );
break;
}
}
trap_S_StartLocalSound( st.cantbuySound, CHAN_LOCAL );
} break;
// st_shop_sell
case CS('s','e','l','l'):
{
if ( trap_Cvar_VariableInt( "ui_buying" ) == 2 ) {
if ( sql( "SELECT feeders.name[ 'selllist' ].id, feeders.name[ 'sellqty' ].sel;", 0 ) ) {
// get sale quantiy from ui dialog
int commodity_id = sqlint(0);
int scnt = sqlint(1);;
// send a command to server to process the transaction
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_sell %i %i;", commodity_id, scnt ) );
sqldone();
trap_S_StartLocalSound( st.sellSound, CHAN_LOCAL );
break;
}
}
trap_S_StartLocalSound( st.cantbuySound, CHAN_LOCAL );
} break;
// st_shop_end
case CS('e','n','d',0):
{
if ( sql( "SELECT client FROM clients WHERE id = ?;", "i", st.npc ) )
{
// tell the server to release this merchant from shopping
trap_Cmd_ExecuteText( EXEC_APPEND, va( "st_endi %i ;", sqlint(0) ) );
sqldone();
}
} break;
// st_shop_select_
case CS('s','e','l','e'):
{
switch (SWITCHSTRING( cmd+12 ) )
{
case CS('b','u','y',0): st_shop_select_buy(); break;
case CS('s','e','l','l'): st_shop_select_sell(); break;
}
} break;
}
} break;
// st_planet
case CS('p','l','a','n'):
{
menuDef_t * menu = Menus_FindByName( "travel_details" );
if ( menu && menu->window.flags & WINDOW_VISIBLE ) {
break;
}
// the player has selected a planet from the travel screen...
if ( !gsi.MENU && st.travel.selected )
{
// ask server if we can travel
st.travel.voted = st.travel.selected;
st.travel.selectLockTime = uiInfo.uiDC.realTime + 500;
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_planet %d ;", st.travel.selected->id ) );
if (sql("SELECT snd FROM npcs_voices WHERE cmd = $1", "t", st.travel.selected->name) ) {
trap_S_StartLocalSound( sqlint(0), CHAN_ANNOUNCER);
sqldone();
}
}
} break;
// st_zoom
case CS('z','o','o','m'):
{
switch ( SWITCHSTRING( cmd + 4 ) )
{
case CS('i','n',0,0): // st_zoomin
{
if( !st.travel.selected )
UI_ST_SelectPlanet( st.travel.voted, 0 );
if( st.travel.selected && st.travel.zoomState != STZ_IN )
{
solarsystemEye_t eye;
SolarSystem_LookAt( &eye, st.travel.selected, (float)GS_TIME );
UI_ST_SolarSystemBeginTransfer( &eye, 1500 );
st.travel.zoomState = STZ_IN;
}
} break;
case CS('t','o',0,0): //st_zoomto
{
const char * s;
String_Parse( args, &s );
if( sql( "SELECT id FROM planets SEARCH name $;", "t", s ) )
{
int i;
int planet_id = sqlint(0);
for( i = 0; i < st.travel.ss.planetCount; i++ )
{
if( st.travel.ss.planets[i].id == planet_id )
{
solarsystemEye_t eye;
SolarSystem_LookAt( &eye, st.travel.ss.planets + i, (float)GS_TIME );
UI_ST_SolarSystemSnapEye( &eye );
st.travel.zoomState = STZ_IN;
break;
}
}
sqldone();
}
} break;
case CS('o','u','t',0): // st_zoomout
{
float angles[2], ofs[3];
solarsystemEye_t eye;
Float_Parse( args, angles + 0 );
Float_Parse( args, angles + 1 );
Float_Parse( args, ofs + 0 );
Float_Parse( args, ofs + 1 );
Float_Parse( args, ofs + 2 );
if( st.travel.zoomState == STZ_OUT )
break;
UI_ST_SelectPlanet( 0, 0 );
SolarSystem_TopDown( &eye, &st.travel.ss, (float)GS_TIME, angles, ofs, true );
UI_ST_SolarSystemBeginTransfer( &eye, 1000 );
st.travel.zoomState = STZ_OUT;
} break;
case CS('s','t','a','r'): // st_zoomstart
case CS('i','n','i','t'): // st_zoominit
{
solarsystemEye_t eye;
if( st.travel.current )
{
SolarSystem_LookAt( &eye, st.travel.current, (float)GS_TIME );
st.travel.zoomState = STZ_IN;
}
else
{
float angles[2] = { 0, 0 }, ofs[3] = { 0, 0, 0 };
SolarSystem_TopDown( &eye, &st.travel.ss, (float)GS_TIME, angles, ofs, true );
st.travel.zoomState = STZ_OUT;
}
UI_ST_SolarSystemSnapEye( &eye );
} break;
}
} break;
// st_solarsystem
case CS('s','o','l','a'):
{
cache_solarsystem( &st.travel.ss );
} break;
// st_travel
case CS('t','r','a','v'):
{
const planetInfo_t * p = st.travel.voted;
if ( p && p->time > 0 )
{
if ( p->time <= GS_TIMELEFT )
{
if ( sql( "SELECT name FROM planets SEARCH id ?;", "i", p->id ) ) {
trap_Cmd_ExecuteText( EXEC_INSERT, va( "callvote travel \"%s\" ;", sqltext(0) ) );
sqldone();
}
}
}
} break;
// st_won
case CS('w','o','n',0):
{
Menus_ActivateByName( "missioncomplete" );
} break;
// st_server
case CS('s','e','r','v'):
{
if ( sql( "SELECT 'flush ; map ' || challenges.id[ feeders.name['maplist'].id ]^name || '\n';", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_APPEND, sqltext(0) );
sqldone();
}
} break;
// st_visit
case CS('v','i','s','i'):
{
// visit contact if we're not already talking to them.
if ( sql( "SELECT id FROM feeders SEARCH name 'contactlist' WHERE conversations.id[ 0 ].bot != id;", 0 ) )
{
// kill the cross hair name effect, since we're leaving.
UI_Effect_SetFlag( st.crosshair.effect, EF_DELETEME );
trap_Cmd_ExecuteText( EXEC_NOW, va( "st_visit %d", sqlint(0) ) );
sqldone();
}
} break;
// st_scanforchallenges
case CS('s','c','a','n'):
{
int count;
UI_ST_ScanForMissions();
sql( "SELECT COUNT(*) FROM challenges;", "sId", &count );
if ( count == 1 ) {
// if there's only one map in list, launch it
if ( sql( "SELECT name FROM challenges;", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_APPEND, va("flush ; map %s\n", sqltext(0) ) );
sqldone();
break;
}
}
//if ( trap_Cvar_VariableInt( "com_downloading" ) == 1 ) {
//Menus_ActivateByName( "waitformission" );
//} else {
Menus_ActivateByName( "selectmission" );
//}
} break;
// st_dialog_*
case CS('d','i','a','l'):
{
switch ( SWITCHSTRING( cmd+7 ) )
{
// st_dialog_done
case CS('d','o','n','e'):
{
if ( sql( "SELECT bot FROM conversations;", 0 ) )
{
// tell the server to release this merchant from shopping
trap_Cmd_ExecuteText( EXEC_APPEND, va( "st_endi %i ;", sqlint(0) ) );
sqldone();
}
} break;
// st_dialog_say
case CS('s','a','y',0):
{
if ( sql( "SELECT bot, feeders.name['answerlist'].id FROM conversations LIMIT 1;", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_dialog %d %d ;", sqlint(0), sqlint(1) ) );
sqldone();
}
} break;
}
} break;
// st_jobs_day
case CS('j','o','b','s'):
{
if ( sql( "SELECT bot, feeders.name['jobslist'].id FROM conversations LIMIT 1;", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_dialog %d %d ;", sqlint(0), sqlint(1) ) );
sqldone();
}
} break;
// st_restart
case CS('r','e','s','t'):
{
Com_Error( ERR_DISCONNECT, "" );
} break;
// st_cash_*
case CS('c','a','s','h'):
{
switch ( SWITCHSTRING( cmd+5 ) )
{
// st_cash_withdraw
case CS('w','i','t','h'):
{
if ( sql( "SELECT sel FROM feeders SEARCH name 'withdraw_cash';", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_withdraw %d ;", sqlint(0) ) );
sqldone();
}
} break;
// st_cash_deposit
case CS('d','e','p','o'):
{
if ( sql( "SELECT sel FROM feeders SEARCH name 'deposit_cash';", 0 ) ) {
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_deposit %d ;", sqlint(0) ) );
sqldone();
}
} break;
//st_cash_paydebt
case CS( 'p', 'a', 'y', 'd' ):
{
if( sql( "SELECT MIN( -debt, cash ) FROM players SEARCH client %client;", 0 ) )
{
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_deposit %d ;", sqlint(0) ) );
sqldone();
}
}
break;
//st_cash_drawbalance
case CS( 'd', 'r', 'a', 'w' ):
{
if( sql( "SELECT MIN( debt, credit ) FROM players SEARCH client %client;", 0 ) )
{
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_withdraw %d ;", sqlint(0) ) );
sqldone();
}
}
break;
}
} break;
case CS('c','a','r','g'):
{
switch( SWITCHSTRING( cmd+6 ) )
{
// st_cargo_pay
case CS('p','a','y',0):
{
int f = trap_Cvar_VariableInt( "ui_cargoupgradeamount" );
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_cargo_upgrade %d ;", f ) );
trap_Cvar_SetValue( "ui_cargoupgradeamount", 1.0f );
} break;
}
} break;
// st_chat
case CS('c','h','a','t'):
{
char info[ MAX_INFO_STRING ];
trap_Cvar_VariableStringBuffer( "ui_chatline", info, sizeof( info ) );
trap_Cmd_ExecuteText( EXEC_ONSERVER, va( "st_chat \"%s\" ;", info ) );
} break;
// st_ready
case CS('r','e','a','d'):
{
if ( uiInfo.uiDC.realTime - st.travel.readyTime > 750 )
{
st.travel.readyTime = uiInfo.uiDC.realTime; // spam protection
trap_Cmd_ExecuteText( EXEC_ONSERVER, "st_ready" );
}
} break;
// st_intermission
case CS('i','n','t','e'):
{
trap_Cmd_ExecuteText( EXEC_ONSERVER, "st_intermission ;" );
} break;
// st_beginturn
case CS('b','e','g','i'):
{
trap_Cmd_ExecuteText( EXEC_ONSERVER, "st_beginturn ;" );
} break;
case CS('p','l','a','y'):
{
switch( SWITCHSTRING(cmd+13) )
{
// st_playerselect_btn
case CS('b','t','n',0):
{
int slot;
Int_Parse( args, &slot );
st.selectedprofile = slot;
// create new player
if ( uiInfo.characters[ slot ].name[ 0 ] == '\0' )
{
trap_Cvar_Set( "ui_playerselect_selected", va("%d",slot) );
trap_Cvar_Set( "ui_model", "mpc1" );
trap_Cvar_Set( "ui_name", "" );
UI_ST_PlayerSelectSlot_SetModel( &uiInfo.characters[ slot ].info, "mpc1" );
Menu_Item_RunScript( "selectplayer", va("st_playerselect_nameedit%d", slot ), "setFocus this ; editfield this" );
} else
{
Menus_OpenByName( "deleteplayer_ask" );
}
} break;
// st_playerselect_create
case CS('c','r','e','a'):
{
int slot;
char playerName [ MAX_NAME_LENGTH ];
char playerModel [ MAX_QPATH ];
Int_Parse( args, &slot );
trap_Cvar_VariableStringBuffer( "ui_name", playerName, sizeof(playerName) );
trap_Cvar_VariableStringBuffer( "ui_model", playerModel, sizeof(playerModel) );
if ( playerName[ 0 ] && playerModel[ 0 ] ) {
trap_Cmd_ExecuteText( EXEC_APPEND, va( "createcharacter %d \"%s\" \"%s\" ;", slot, playerName, playerModel ) );
}
} break;
// st_playerselect_delete
case CS('d','e','l','e'):
{
int slot = st.selectedprofile;
if ( slot >= 0 && slot < 5 )
{
trap_Cmd_ExecuteText( EXEC_APPEND, va( "deletecharacter %d ;", slot ) );
uiInfo.characters[ slot ].name[ 0 ] = '\0';
uiInfo.characters[ slot ].info.legsModel = 0;
}
} break;
// st_playerselect_load
case CS('l','o','a','d'):
{
int i;
if( !uiInfo.charsLoaded )
{
for( i=0; i<MAX_CHARACTERS; i++ )
uiInfo.characters[ i ].name[ 0 ] = '\0';
}
trap_Cmd_ExecuteText( EXEC_APPEND, "getaccount ;" );
uiInfo.charsLoaded = true;
} break;
// st_playerselect_model
case CS('m','o','d','e'):
{
int slot;
char playerModel [ MAX_QPATH ];
Int_Parse( args, &slot );
trap_Cvar_VariableStringBuffer( "ui_model", playerModel, sizeof(playerModel) );
UI_ST_PlayerSelectSlot_SetModel( &uiInfo.characters[ slot ].info, playerModel );
} break;
// st_playerselect_cancel
case CS('c','a','n','c'):
{
int slot;
Int_Parse( args, &slot );
UI_ST_PlayerSelectSlot_SetModel( &uiInfo.characters[ slot ].info, "" );
} break;
// st_playerselect_select
case CS('s','e','l','e'):
{
int slot;
Int_Parse( args, &slot );
st.selectedprofile = slot;
if ( uiInfo.characters[ slot ].name[ 0 ] != '\0' )
{
trap_Cvar_Set( "name", uiInfo.characters[ slot ].name);
trap_Cvar_Set( "model", uiInfo.characters[ slot ].model);
trap_Cvar_Set( "slot", va("%d", slot) );
Menus_CloseByName( "selectplayer" );
if (sql("SELECT 1 FROM challenges SEARCH name 'continue' WHERE complete != 1", NULL) ) {
Menus_ActivateByName( "sto_continue" );
sqldone();
} else {
Menus_ActivateByName( "joinserver" );
}
}
} break;
}
} break;
// st_forgotpassword
case CS('f','o','r','g'):
{
char email[ MAX_SAY_TEXT ];
trap_Cvar_VariableStringBuffer( "ui_email", email, sizeof(email) );
trap_Cmd_ExecuteText( EXEC_APPEND, va("forgotpassword %s ;", email ) );
} break;
// st_lagkick
case CS('l','a','g','k'):
{
UI_ST_kickLaggingPlayers();
} break;
// st_model
case CS('m','o','d','e'):
{
char model[ MAX_QPATH ];
trap_Cvar_VariableStringBuffer( "model", model, sizeof(model) );
UI_ST_PlayerSelectSlot_SetModel( &st.modelinfo, model );
} break;
}
}
//
// scans all the .mis files in the db/ folder and copies out all the missions rows from all the db's.
//
static void UI_ST_ScanForMissions( void )
{
int numfiles;
char buffer [ 2048 ];
char filename [ MAX_QPATH ];
char lang [ MAX_NAME_LENGTH ];
char** files;
int i;
int n;
trap_Cvar_VariableStringBuffer( "cl_lang", lang, sizeof(lang) );
numfiles = trap_FS_GetFileList( "db/", ".mis", buffer, sizeof(buffer) );
files = (char**)buffer;
sql( "DELETE FROM challenges;", "e" );
for ( i=0,n=0; i<numfiles; i++ )
{
trap_SQL_LoadDB( files[i], "sv_missions" );
if ( sql( "SELECT name FROM common.files SEARCH fullname missions.key['db_name']^value;", 0 ) ) {
trap_SQL_LoadDB( va("db/%s_%s.txt",sqltext(0),lang), "cl_missions_text" );
sqldone();
} else {
continue;
}
Q_strncpyz( filename, files[i] + 3, sizeof(filename) );
filename[ strlen(filename)-4 ] = '\0';
sql( "INSERT INTO challenges(id,number,title,description,name,icon,time,time_units,par_score,hidden,version,complete) "
"SELECT "
"?1, "
"missions.key['number'].value, "
"missions_text.key['title']^value, "
"missions_text.key['description']^value, "
"$2, "
"shader('ui/screenshots/'||$2), "
"missions.key['time'].value, "
"missions_text.key['time_units']^value, "
"missions.key['par_score'].value, "
"max(0,missions.key['hidden'].value), "
"missions.key['version'].value, "
"missions.key['complete'].value;", "ite", i, filename );
}
}
static void UI_ST_InitFrontEnd( void )
{
trap_SQL_Exec(
"CREATE TABLE scores"
"("
"challenge INTEGER, "
"your_rank INTEGER, "
"your_score INTEGER, "
"first_rank INTEGER, "
"first_score INTEGER, "
"first_name STRING "
");"
"CREATE TABLE stats"
"("
// \next_points\220\percentile\0.196723144834655\next_name\^2FrZ|^3Blaze\rank\3\points\0
"key STRING, "
"value STRING, "
"integer INTEGER "
");"
"CREATE TABLE challenges"
"("
"id INTEGER, "
"number INTEGER, "
"title STRING, "
"description STRING, "
"name STRING, "
"icon INTEGER, "
"par_score INTEGER, "
"time INTEGER, "
"time_units STRING, "
"hidden INTEGER, "
"version INTEGER, "
"complete INTEGER "
");"
);
// initializing ui and not connected to a server, scan missions
// available for play
UI_ST_ScanForMissions();
trap_Cvar_Set( "ui_currentMap", "0" );
trap_Cvar_Set( "ui_currentNetMap", "0" );
trap_Cvar_Set( "ui_menustack", "" );
trap_Cvar_Set( "ui_singlePlayerActive", "0" );
st.chat.lineCount = trap_Cvar_VariableInt( "ui_chatlines" );
trap_SQL_LoadDB( "ui/solarsystem.sql", 0 );
cache_solarsystem( &st.travel.ss );
trap_Con_Clear(1);
}
static qboolean UI_ST_InitGame( void )
{
char info[ MAX_INFO_STRING ];
// all game types
st.crosshair.ally = UI_Effect_Find( "ally" );
st.crosshair.enemy = UI_Effect_Find( "enemy" );
trap_GetConfigString( CS_SPACETRADER, info, sizeof(info) );
// check to make sure we're connecting to a space trader game
if ( info[ 0 ] == '\0' )
return qfalse;
uiInfo.maxplayers = 8;
BG_ST_ReadState( info, &gsi );
trap_UpdateGameState( &gsi );
st.seed = 1; // this is supposed to come from the server
st.chat.lineCount = trap_Cvar_VariableInt( "ui_chatlines" );
// cache disaster icons
sql( "INSERT INTO events_icons SELECT id,shader('ui/events/' || icon) FROM events_text;", "e" );
cache_solarsystem( &st.travel.ss );
cache_news();
st.assets.graphShader = trap_R_RegisterShaderNoMip( "ui/assets/pricegraph" );
st.assets.youarehere = trap_R_RegisterShaderNoMip( "ui/assets/youarehere" );
st.assets.orbitShader = trap_R_RegisterShaderNoMip( "ui/assets/orbit" );
st.assets.decoration_accuracy = trap_R_RegisterShaderNoMip( "ui/assets/decoration_accuracy" );
st.assets.decoration_money = trap_R_RegisterShaderNoMip( "ui/assets/decoration_money" );
st.assets.decoration_stash = trap_R_RegisterShaderNoMip( "ui/assets/decoration_stash" );
st.assets.decoration_time = trap_R_RegisterShaderNoMip( "ui/assets/decoration_time" );
st.assets.decoration_boss = trap_R_RegisterShaderNoMip( "ui/assets/decoration_boss" );
st.assets.arrow_past_head = trap_R_RegisterShaderNoMip( "ui/starmap/arrowpasthead" );
st.assets.arrow_past_tail = trap_R_RegisterShaderNoMip( "ui/starmap/arrowpasttail" );
st.assets.arrow_left_head = trap_R_RegisterShaderNoMip( "ui/starmap/arrowlefthead" );
st.assets.arrow_left_tail = trap_R_RegisterShaderNoMip( "ui/starmap/arrowlefttail" );
// st.assets.calendar = trap_R_RegisterShaderNoMip( "ui/assets/calendar" );
st.assets.ready = UI_Effect_Find( "ready" );
st.assets.notready = UI_Effect_Find( "notready" );
st.gametype = trap_Cvar_VariableInt("g_gametype");
st.travel.label = UI_Effect_Find( "planet_label" );
st.travel.totalTime = (float)UI_ST_GetMissionInt( "time" );
// cache commodity icons
sql( "UPDATE commodities_text SET icon=shader('ui/commodity/' || commodities.id[ id ]^abbreviation);", "e" );
sql( "UPDATE commodities_text SET icon=shader('ui/commodity/MG') WHERE icon=0;", "e" );
cache_pricegraphs( GS_TIME+(ST_START_TIME-1), GS_TIME, 96 );
if ( st.gametype == GT_ASSASSINATE ) {
if ( sql( "SELECT npcs.id[ ?1 ]^name, bounty FROM bosses WHERE id=?1;", "i", GS_BOSS ) )
{
Q_strncpyz( st.assassinate.bossname, sqltext(0), sizeof(st.assassinate.bossname) );
st.assassinate.bounty = sqlint(1);
sqldone();
}
}
cache_icons();
st.buySound = trap_S_RegisterSound( "sound/misc/stash_pickup", qtrue );
st.cantbuySound = trap_S_RegisterSound( "sound/misc/stash_full", qtrue );
st.sellSound = trap_S_RegisterSound( "sound/ui/pop", qtrue );
return qtrue;
}
static void UI_ST_UpdateSounds()
{
#if 0
int i;
for ( i=0; i<lengthof( gsInfo ); i++ )
{
int j;
for ( j=0; j<gsInfo[ i ].count; j++ )
{
gsInfo_t * gs = gsInfo[ i ].v + j;
if ( gs->flags & GE_TICK )
uiInfo.uiDC.startLocalSound( uiInfo.uiDC.Assets.dings[ 0 ], CHAN_LOCAL_SOUND );
if ( gs->flags & GE_LASTFRAME )
{
uiInfo.uiDC.startLocalSound( uiInfo.uiDC.Assets.dings[ 0 ], CHAN_LOCAL_SOUND );
uiInfo.uiDC.startLocalSound( uiInfo.uiDC.Assets.dings[ 1 ], CHAN_LOCAL_SOUND );
uiInfo.uiDC.startLocalSound( uiInfo.uiDC.Assets.dings[ 2 ], CHAN_LOCAL_SOUND );
uiInfo.uiDC.startLocalSound( uiInfo.uiDC.Assets.dings[ 3 ], CHAN_LOCAL_SOUND );
continue;
}
}
}
#endif
}
static void UI_ST_SelectPlanet( const planetInfo_t * p, itemDef_t * item ) {
int i;
// a different planet is selected
if ( p == st.travel.selected )
return;
if ( item ) {
trap_SQL_Prepare ( "UPDATE feeders SET sel=?1,id=?2 SEARCH name $3;" );
trap_SQL_BindInt ( 1, (p)?p-st.travel.ss.planets:-1 );
trap_SQL_BindInt ( 2, (p)?p->id:0 );
trap_SQL_BindText ( 3, item->window.name );
trap_SQL_Step ();
trap_SQL_Done ();
}
// kill old labels
for ( i=0; i<lengthof( st.travel.labels ); i++ ) {
UI_Effect_SetFlag( st.travel.labels[i], EF_NOFOCUS );
}
// make new labels
if ( p && !Q_stricmp(((menuDef_t*)item->parent)->window.name, "travel_page") ) {
int i = 0;
// name
st.travel.labels[ i++ ] = UI_Effect_SpawnText( 0, st.travel.label, p->name, p->icon );
// travel time
if ( p->time ) {
if ( sql( "SELECT index FROM strings SEARCH name $;", "t", "T_Travel_Time" ) ) {
int index = sqlint(0);
char t[ MAX_NAME_LENGTH ];
trap_SQL_Run( t, sizeof(t), index, p->time, 0,0 );
st.travel.labels[ i++ ] = UI_Effect_SpawnText( 0, st.travel.label, t, 0 );
sqldone();
}
}
// disaster
if( p->disaster_name && p->disaster_name[0] ) {
st.travel.labels[ i++ ] = UI_Effect_SpawnText( 0, st.travel.label, p->disaster_name, p->disaster_icon );
}
// bounties
while ( sql( "SELECT npcs.id[ id ]^name || '\n' || (bounty:currency), icons.npc[ id ].icon FROM bosses WHERE planet_id=? AND captured=0;", "i", p->id ) ) {
st.travel.labels[ i++ ] = UI_Effect_SpawnText( 0, st.travel.label, sqltext(0), sqlint(1) );
}
}
st.travel.selected = p;
}
static void UI_ST_DrawPlanetLabels( rectDef_t * r, planetInfo_t * p, itemDef_t * item, float a )
{
int i;
float ts;
rectDef_t t;
float rgba[4];
//float theta, radius;
//ts = (item->textscale * r->w) / 36.0f;
ts = item->textscale;
rgba[0] = item->window.foreColor[0];
rgba[1] = item->window.foreColor[1];
rgba[2] = item->window.foreColor[2];
rgba[3] = item->window.foreColor[3] * a;
//theta = DEG2RAD( 90 );
trap_R_SetColor( rgba );
if ( st.travel.label ) {
t = st.travel.label->rect;
for ( i=0; i<lengthof( st.travel.labels ); i++ ) {
if ( !UI_Effect_IsAlive( st.travel.labels[ i ] ) )
break;
UI_Effect_SetRect( st.travel.labels[ i ], &t, item );
t.y += t.h;
}
}
//planet name
t.x = r->x;
t.y = r->y - 6.0f;
t.w = r->w;
t.h = 1;
trap_R_RenderText( &t, ts, rgba, p->name, -1, TEXT_ALIGN_RIGHT | TEXT_ALIGN_NOCLIP,
ITEM_ALIGN_CENTER, item->textStyle | TEXT_STYLE_OUTLINED, item->font, -1, 0 );
//travel time
if ( p->time > 0 )
{
qhandle_t font = trap_R_RegisterFont( "digital" );
t.x = t.x + t.w + 8.0f;
trap_R_SetColor( colorGreen );
trap_R_RenderText( &t, ts*1.5f, rgba, va("%i",p->time), -1, TEXT_ALIGN_LEFT | TEXT_ALIGN_NOCLIP, TEXT_ALIGN_CENTER, TEXT_STYLE_OUTLINED, font, -1, 0 );
}
trap_R_SetColor( NULL );
}
static void Menus_SetState( int state ) {
if ( st.MENU == state )
return;
UI_SetMenuStack( &ui_menuStack );
st.MENU = state;
switch( st.MENU ) {
// st_awards
// the trading round has ended and the server is asking the client to display the awards
// for that round
//
case CS('a','w','a','r'):
{
switch ( st.gametype ) {
case GT_HUB:
Menus_FadeAllOutAndFadeIn( "trading_awards" );
break;
case GT_MOA:
case GT_ASSASSINATE:
Menus_FadeAllOutAndFadeIn( "assassinate_awards" );
break;
}
} break;
// st_moa - players have just finished battling the MoA
case CS('m','o','a','w'):
Menus_CloseAll();
Menus_OpenByName( "moa_won" );
break;
case CS('m','o','a','f'):
Menus_CloseAll();
Menus_OpenByName( "moa_lose" );
break;
case 0:
break;
// ass u me its a vote menu
default:
{
char cmd[ 5 ];
menuDef_t * menu;
cmd[ 0 ] = (char)(st.MENU)&0xFF;
cmd[ 1 ] = (char)(st.MENU>>8)&0xFF;
cmd[ 2 ] = (char)(st.MENU>>16)&0xFF;
cmd[ 3 ] = (char)(st.MENU>>24)&0xFF;
cmd[ 4 ] = '\0';
menu = Menus_FindByName( va( "%svote", cmd ) );
if( !menu )
menu = Menus_FindByName( "vote" );
Menus_Activate( menu );
Menus_ActivateByName( "watchvote" );
}
break;
}
}
static void UI_ST_InitMenus()
{
//
// Initialize CG MenuStack
//
UI_SetMenuStack( &cg_menuStack );
Menus_CloseAll();
switch( st.gametype )
{
case GT_FFA:
Menus_ActivateByName( "dashboard_ffa" );
break;
case GT_HUB:
Menus_ActivateByName( "dashboard" );
//Menus_ActivateByName( "dashboard_trade" );
Menus_ActivateByName( "multiplayer_scores" );
break;
case GT_ASSASSINATE:
Menus_ActivateByName( "dashboard" );
Menus_ActivateByName( "combat" );
Menus_ActivateByName( "combat-assassinate" );
Menus_ActivateByName( "multiplayer_scores" );
break;
case GT_MOA:
Menus_ActivateByName( "dashboard" );
Menus_ActivateByName( "combat" );
Menus_ActivateByName( "combat-moaboarding" );
Menus_ActivateByName( "multiplayer_scores" );
break;
default:
Menus_ActivateByName( "player" );
}
//
// Initialize UI MenuStack
//
UI_SetMenuStack( &ui_menuStack );
Menus_CloseAll();
if ( st.gametype == GT_HUB )
{
if ( GS_INPLAY == 0 )
{
Menus_FadeAllOutAndFadeIn( 0 );
Menus_ActivateByName( "BeginTurn" );
if ( ui_singlePlayerActive.integer == 0 )
{
// first turn + local server means this client is the host...
if ( GS_TURN == 1 && trap_Cvar_VariableInt( "sv_running" ) )
{
Menus_ActivateByName( "BeginTurn_Server" );
} else
Menus_ActivateByName( "BeginTurn_Client" );
Menus_ActivateByName( "BeginTurn_Chat" );
} else {
if ( sql( "select id from tips_text random 1;", 0 ) ) {
trap_Cvar_SetValue( "ui_professio_tip", (float)sqlint(0) );
sqldone();
}
}
if ( GS_TURN == 1 )
{
} else
{
Menus_ActivateByName( "BeginTurn_Results" );
}
} else {
Menus_SetState( gsi.MENU );
#ifdef USE_DRM
if ( trap_Cvar_VariableInt( "com_keyvalid" ) == 0 && GS_TURN > 1 ) {
Menus_ActivateByName( "registergame" );
}
#endif
}
}
st.menus_init = 1;
}
float UI_ST_SolarSystemAdjustCameraCurve( float transfer_time )
{
#ifndef M_LN2
#define M_LN2 0.69314718055994530941723212145818F
#endif
return 1.0F - ((1 - cosf((1 - transfer_time) * M_PI)) * 0.5F) *
powf( (1.0F / M_LN2) * logf( 2 - transfer_time ), 1.65F );
}
void UI_ST_DrawSolarSystemShowcase( itemDef_t *item, float transfer_time )
{
float time = (float)uiInfo.uiDC.realTime * 1e-7f;
static int dir = -1;
if( st.travel.showcase.stepTime - uiInfo.uiDC.realTime <= 0 )
{
dir = -1;
if( !st.travel.selected )
{
st.travel.selected = st.travel.ss.planets - 1; //not a valid planet but the do loop will ++ it right away
dir = 1;
}
do
{
st.travel.selected++;
if( st.travel.selected == st.travel.ss.planets + st.travel.ss.planetCount )
{
st.travel.selected = st.travel.ss.planets + 0;
dir = -1;
}
} while( st.travel.selected->decoration || !st.travel.selected->visible );
{
solarsystemEye_t eye;
SolarSystem_LookAt( &eye, st.travel.selected, time );
UI_ST_SolarSystemBeginTransfer( &eye, 20000 );
}
st.travel.showcase.stepTime = uiInfo.uiDC.realTime + 22000;
transfer_time = 0;
}
transfer_time = UI_ST_SolarSystemAdjustCameraCurve( transfer_time );
SolarSystem_LerpEye( &st.travel.ss.eye, &st.travel.eye[0], &st.travel.eye[1], dir, transfer_time );
SolarSystem_Draw( &st.travel.ss, &item->window.rect, NULL, NULL, time, 0 );
}
static qhandle_t build_sniperzoom( void )
{
#define cR 50
#define iD 0.02F
int i, t;
float rx, ry;
shapeGen_t gen[4] = { SHAPEGEN_FILL_USE_INDICES, SHAPEGEN_FILL_USE_INDICES, SHAPEGEN_FILL_USE_INDICES, SHAPEGEN_FILL_USE_INDICES };
curve_t cv[4];
vec2_t pts[4][(cR + 1) * 2];
short iidxs[(cR + 4) * 3];
vec4_t cls[(cR + 1) * 2];
short idxs[cR * 2 * 3];
//get the resolution multipliers
ry = 0.4F;
rx = ry * ((480 * uiInfo.uiDC.glconfig.yscale) / (640 * uiInfo.uiDC.glconfig.xscale)) *
(640 * uiInfo.uiDC.glconfig.xscale) / (640 * uiInfo.uiDC.glconfig.xscale + 2 * uiInfo.uiDC.glconfig.xbias);
//set up the curve_t records
for( i = 0; i < 4; i++ )
{
Com_Memset( cv + i, 0, sizeof( curve_t ) );
cv[i].pts = pts[i];
}
cv[2].colors = cv[3].colors = cls;
cv[2].indices = cv[3].indices = idxs;
//left half outer fill
for( i = 0; i < cR + 1; i++ )
{
float t = M_PI + (i * M_PI) / cR;
float x = sinf( t );
float y = -cosf( t );
cv[0].pts[i][0] = 0.5F + x * rx;
cv[0].pts[i][1] = 0.5F + y * ry;
}
cv[0].pts[cR + 1 + 0][0] = 0.5F;
cv[0].pts[cR + 1 + 0][1] = 0;
cv[0].pts[cR + 1 + 1][0] = 0;
cv[0].pts[cR + 1 + 1][1] = 0;
cv[0].pts[cR + 1 + 2][0] = 0;
cv[0].pts[cR + 1 + 2][1] = 1;
cv[0].pts[cR + 1 + 3][0] = 0.5F;
cv[0].pts[cR + 1 + 3][1] = 1;
cv[0].numPts = cR + 1 + 4;
//right half outer fill
for( i = 1; i < cR + 1; i++ )
{
float t = M_PI + (i * M_PI) / cR;
float x = -sinf( t );
float y = cosf( t );
cv[1].pts[i][0] = 0.5F + x * rx;
cv[1].pts[i][1] = 0.5F + y * ry;
}
//weld verts
cv[1].pts[cR][0] = cv[0].pts[0][0];
cv[1].pts[cR][1] = cv[0].pts[0][1];
cv[1].pts[0][0] = cv[0].pts[cR][0];
cv[1].pts[0][1] = cv[0].pts[cR][1];
cv[1].pts[cR + 1 + 0][0] = 0.5F;
cv[1].pts[cR + 1 + 0][1] = 1;
cv[1].pts[cR + 1 + 1][0] = 1;
cv[1].pts[cR + 1 + 1][1] = 1;
cv[1].pts[cR + 1 + 2][0] = 1;
cv[1].pts[cR + 1 + 2][1] = 0;
cv[1].pts[cR + 1 + 3][0] = 0.5F;
cv[1].pts[cR + 1 + 3][1] = 0;
cv[1].numPts = cR + 1 + 4;
//outer fill indices
t = 0;
for( i = 0; i <= cR / 2 + 1; i++ )
{
iidxs[t * 3 + 0] = cR + 3;
iidxs[t * 3 + 1] = (short)i;
iidxs[t * 3 + 2] = (short)(i ? i - 1 : cR + 4);
t++;
}
for( i = cR; i >= cR / 2 + 1; i-- )
{
iidxs[t * 3 + 0] = cR + 2;
iidxs[t * 3 + 1] = (short)(i + 1);
iidxs[t * 3 + 2] = (short)i;
t++;
}
iidxs[t * 3 + 0] = cR / 2 + 1;
iidxs[t * 3 + 1] = cR + 3;
iidxs[t * 3 + 2] = cR + 2;
t++;
cv[0].indices = iidxs;
cv[0].numIndices = t * 3;
cv[1].indices = iidxs;
cv[1].numIndices = t * 3;
//inner gradient setup
for( i = 0; i < cR + 1; i++ )
{
//outside verts
VectorSet( cls[i], 1, 1, 1 );
cls[i][3] = 1;
//inside verts
VectorSet( cls[cR + 1 + i], 1, 1, 1 );
cls[cR + 1 + i][3] = 0;
}
for( i = 0; i < cR; i++ )
{
idxs[i * 6 + 0] = (short)i;
idxs[i * 6 + 1] = (short)(i + 1);
idxs[i * 6 + 2] = (short)(cR + 1 + i);
idxs[i * 6 + 3] = (short)(cR + 1 + i);
idxs[i * 6 + 4] = (short)(i + 1);
idxs[i * 6 + 5] = (short)(cR + 1 + i + 1);
}
//left half inner gradient
for( i = 0; i < cR + 1; i++ )
{
//outside verts
cv[2].pts[i][0] = cv[0].pts[i][0];
cv[2].pts[i][1] = cv[0].pts[i][1];
//inside verts
cv[2].pts[cR + 1 + i][0] = 0.5F * iD + (1.0F - iD) * cv[0].pts[i][0];
cv[2].pts[cR + 1 + i][1] = 0.5F * iD + (1.0F - iD) * cv[0].pts[i][1];
}
cv[2].numPts = (cR + i) * 2;
cv[2].numIndices = cR * 2 * 3;
//right half inner gradient
for( i = 0; i < cR + 1; i++ )
{
//outside verts
cv[3].pts[i][0] = cv[1].pts[i][0];
cv[3].pts[i][1] = cv[1].pts[i][1];
//inside verts
cv[3].pts[cR + 1 + i][0] = 0.5F * iD + (1.0F - iD) * cv[1].pts[i][0];
cv[3].pts[cR + 1 + i][1] = 0.5F * iD + (1.0F - iD) * cv[1].pts[i][1];
}
//outside verts are automatically welded by virtue of where they come from
//make sure the inside ones are also exact:
cv[3].pts[cR + 1 + 0][0] = cv[2].pts[cR + 1 + cR][0];
cv[3].pts[cR + 1 + 0][1] = cv[2].pts[cR + 1 + cR][1];
cv[3].pts[cR + 1 + cR][0] = cv[2].pts[cR + 1 + 0][0];
cv[3].pts[cR + 1 + cR][1] = cv[2].pts[cR + 1 + 0][1];
cv[3].numPts = (cR + i) * 2;
cv[3].numIndices = cR * 2 * 3;
return trap_R_ShapeCreate( cv, gen, 4 );
#undef iD
#undef cR
}
static void UI_ST_WealthColor( int wealth, int minWealth, int maxWealth, float rgba[4] )
{
if( wealth >= 0 )
{
Vec4_Lrp( rgba, g_color_table[COLOR_YELLOW], g_color_table[COLOR_GREEN],
Com_Clamp( 0, 1, (float)wealth / (float)maxWealth ) );
}
else
{
Vec4_Lrp( rgba, g_color_table[COLOR_YELLOW], g_color_table[COLOR_RED],
Com_Clamp( 0, 1, (float)wealth / (float)minWealth ) );
}
}
static ID_INLINE qboolean PackedBits_Get( const uint *bits, uint idx )
{
return ((bits[idx >> 5] & (uint)(1 << (idx & 0x1F))) != 0) ? qtrue : qfalse;
}
static ID_INLINE void PackedBits_Set( uint *bits, uint idx, qboolean value )
{
if( value )
bits[idx >> 5] |= (uint)(1 << (idx & 0x1F));
else
bits[idx >> 5] &= ~(uint)(1 << (idx & 0x1F));
}
static void UI_DrawStretchPic( float x, float y, float w, float h, qhandle_t shader )
{
UI_AdjustFrom640( &x, &y, &w, &h );
uiInfo.uiDC.drawStretchPic( x, y, w, h, 0, 0, 1, 1, shader );
}
static void UI_ST_DrawTravelRecordBar( itemDef_t *item )
{
static int numTravels = 0;
static struct
{
int time;
planetInfo_t *planet;
} ti[32]; //MAX 32 travels!!
static qhandle_t graphShape;
if( numTravels == 0 )
{
//load it up
while( sql( "SELECT time, planet_id FROM travels SEARCH time;", NULL ) )
{
int i;
int time = sqlint( 0 );
int planet_id = sqlint( 1 );
//int netWorth = sqlint( 2 );
if( time < 0 )
continue;
ti[numTravels].time = time;
//ti[numTravels].netWorth = netWorth;
for( i = 0; i < st.travel.ss.planetCount; i++ )
{
if( st.travel.ss.planets[i].id == planet_id )
{
ti[numTravels].planet = st.travel.ss.planets + i;
break;
}
}
numTravels++;
}
if( !numTravels )
{
//if there's some SQL error then draw nothing and don't spam the DB
numTravels = -1;
return;
}
{
int i, n;
curve_t cv;
shapeGen_t gen = SHAPEGEN_LINE;
vec2_t pts[512];
uint ptSet[lengthof( pts ) / 32 + 1]; //these bits be tightly packed bools...n'yarr!
vec2_t uvs[lengthof( pts )];
vec4_t cls[lengthof( pts )];
int low, high;
low = -100;
high = 5000;
n = -1;
Com_Memset( ptSet, 0, sizeof( ptSet ) );
while( sql( "SELECT time, networth FROM history SEARCH time;", 0 ) )
{
int time = sqlint( 0 ) - 1; //table time is 1-based
int worth = sqlint( 1 );
if( time < 0 || time > lengthof( pts ) )
continue;
pts[time][0] = time / st.travel.totalTime;
pts[time][1] = (float)worth;
PackedBits_Set( ptSet, time, qtrue );
if( worth < low ) low = worth;
if( worth > high ) high = worth;
if( n < time ) n = time;
}
n++;
for( i = 0; i < n; i++ )
{
int worth;
/*
If the point wasn't in the table (for whatever reason),
just interpolate it *by ommitting it from the curve*.
*/
if( !PackedBits_Get( ptSet, i ) )
{
int j;
for( j = i; j < n - 1; j++ )
{
pts[i][0] = pts[i + 1][0];
pts[i][1] = pts[i + 1][1];
PackedBits_Set( ptSet, i, PackedBits_Get( ptSet, i + 1 ) );
}
//adjust the count
n -= 1;
//do the loop again
i -= 1; //cancel the i++ as we want to check the new element at this index
continue;
}
worth = (int)pts[i][1];
//normalize the vertical value and invert since screen space y increases downward
pts[i][1] = 1.0F - Com_Clamp( 0.0f, 1.0f, (pts[i][1] - low) / (float)(high - low) );
uvs[i][0] = pts[i][0];
uvs[i][1] = 0.0f;
UI_ST_WealthColor( worth, -10000, 100000, cls[i] );
}
Com_Memset( &cv, 0, sizeof( curve_t ) );
cv.pts = pts;
cv.uvs = uvs;
cv.colors = cls;
cv.numPts = n;
graphShape = trap_R_ShapeCreate( &cv, &gen, 1 );
}
}
if( numTravels > 0 )
{
int i;
int totalTime = UI_ST_GetMissionInt( "time" );
float w = min( item->window.rect.h, 64 );
float h = w;
float xScale = (item->window.rect.w-w) / (float)totalTime;
for( i = 0; i < numTravels; i++ )
{
float x = (float)ti[i].time * xScale;
float y = 0.0f;
x += item->window.rect.x;
y = item->window.rect.y + (item->window.rect.h - h) * 0.5F;
trap_R_SetColor( g_color_table[ColorIndex( COLOR_WHITE )] );
UI_DrawStretchPic( x+2.0f, y+2.0f, w-4.0f, h-4.0f, ti[i].planet->icon );
if( i > 0 )
{
float xe = (float)ti[i].time * xScale;
float xs = (float)ti[i - 1].time * xScale + w;
float d = min( item->window.rect.h, 16 );
float y = item->window.rect.y + (item->window.rect.h - d) * 0.5F;
rectDef_t r;
if( xe <= xs )
//planets overlap, can't draw between them
continue;
xe += item->window.rect.x;
xs += item->window.rect.x;
UI_DrawStretchPic( xe - d, y, d, d, st.assets.arrow_past_head );
UI_DrawStretchPic( xs, y, xe - xs - d, d, st.assets.arrow_past_tail );
r.x = xs;
r.y = item->window.rect.y;
r.w = xe - xs;
r.h = item->window.rect.h;
if( r.w > d * 2.0f )
r.w -= d;
//UI_DrawStretchPic( r.x + r.w*0.5f - 14.0f, r.y, 28.0f, r.h, st.assets.calendar );
trap_R_SetColor( item->window.backColor );
trap_R_RenderText( &r, item->textscale, colorYellow, va( "%i", ti[i].time - ti[i - 1].time ),
-1, TEXT_ALIGN_CENTER | TEXT_ALIGN_NOCLIP, ITEM_ALIGN_CENTER, item->textStyle, item->font, -1, 0 );
}
}
//draw in the last arrow
{
float xe = item->window.rect.w;
float xs = (float)ti[numTravels - 1].time * xScale + w * 0.5F;
float d = min( item->window.rect.h, 16 );
float y = item->window.rect.y + (item->window.rect.h - d) * 0.5F;
rectDef_t r;
if( xe > xs )
{
xe += item->window.rect.x;
xs += item->window.rect.x;
UI_DrawStretchPic( xe - d, y, d, d, st.assets.arrow_left_head );
UI_DrawStretchPic( xs, y, xe - xs - d, d, st.assets.arrow_left_tail );
r.x = xs;
r.y = item->window.rect.y;
r.w = xe - xs;
r.h = item->window.rect.h;
if( r.w > d * 2.0f )
r.w -= d;
//UI_DrawStretchPic( r.x + r.w*0.5f - 14.0f, r.y, 28.0f, r.h, st.assets.calendar );
trap_R_SetColor( item->window.backColor );
trap_R_RenderText( &r, item->textscale, colorGreen, va( "%i", totalTime - ti[numTravels - 1].time ),
-1, TEXT_ALIGN_CENTER | TEXT_ALIGN_NOCLIP, ITEM_ALIGN_CENTER, item->textStyle, item->font, -1, 0 );
}
}
DrawShape( &item->window.rect, graphShape, st.assets.graphShader );
}
#if 0
if ( trap_Key_IsDown( K_MOUSE1 ) )
{
float x = (float)uiInfo.uiDC.cursorx;
float y = (float)uiInfo.uiDC.cursory;
if ( Rect_ContainsPoint( &item->window.rect, x,y ) ) {
rectDef_t r;
float t = ((x-item->window.rect.x)/(item->window.rect.w)) * st.travel.totalTime;
r.y = item->window.rect.y - 12.0f;
r.h = 12.0f;
r.x = x - 32.0f;
r.w = 64.0f;
trap_R_SetColor( colorGreen );
trap_R_RenderText( &r, item->textscale, item->window.foreColor, fn( (int)t, FN_PLAIN ), -1, item->textalignx, item->textaligny, item->textStyle, item->font, -1, 0 );
}
}
#endif
}
static void UI_InsertMasterServerText( const char * info ) {
char key [ MAX_INFO_KEY ];
char value [ MAX_INFO_VALUE ];
trap_SQL_Prepare( "UPDATE OR INSERT stats SET key=$1,value=$2,integer=?3 SEARCH key $1;" );
for ( ;; )
{
Info_NextPair( &info, key, value );
if ( key[ 0 ] == '\0' )
break;
trap_SQL_BindText ( 1, key );
trap_SQL_BindText ( 2, value );
trap_SQL_BindInt ( 3, atoi(value) );
trap_SQL_Step ();
}
trap_SQL_Done();
}
int QDECL UI_ST_exec( int cmd, ... )
{
va_list argptr;
va_start(argptr, cmd);
switch ( cmd )
{
case UI_ST_INIT_FRONTEND:
{
UI_ST_InitFrontEnd();
} break;
case UI_ST_INIT_GAME:
{
UI_ST_InitGame();
UI_ST_InitMenus();
} break;
case UI_ST_SHUTDOWN:
{
} break;
case UI_ST_PAINT:
{
UI_ST_UpdateSounds();
} break;
case UI_ST_REFRESH:
{
if ( trap_UpdateGameState( &gsi ) ) {
menuDef_t * menu;
if ( st.menus_init ) {
Menus_SetState( gsi.MENU );
}
// hack to resize buttons on travel bar
menu = Menus_FindByName( "travel_menu" );
if ( menu && menu->window.flags&WINDOW_VISIBLE ) {
if ( sql( "SELECT COUNT(*) FROM players;", 0 ) ) {
resize_contacts( menu, sqlint(0) );
sqldone();
}
}
}
} break;
case UI_ST_MAINMENU:
{
if ( trap_Cvar_VariableInt( "st_summary" ) != 0 ) {
// we're back to the main menu with scores in tact, we must have just
// finished a game. show the summary.
char summary[ MAX_INFO_STRING ];
int i;
trap_SQL_Exec( "CREATE TABLE summary"
"("
"client INTEGER, "
"place INTEGER, "
"name STRING, "
"networth INTEGER, "
"me INTEGER "
");"
);
trap_SQL_Prepare( "UPDATE OR INSERT summary CS $2 WHERE client=?1;" );
for ( i=0; i < 8; i++ ) {
trap_Cvar_VariableStringBuffer( va( "st_summary%d", i ), summary, sizeof(summary) );
if ( summary[0] != '\0' )
{
trap_SQL_BindInt ( 1, i );
trap_SQL_BindText ( 2, summary );
trap_SQL_Step ();
}
}
trap_SQL_Done();
{
char info[ MAX_INFO_STRING ];
sql( "DELETE FROM stats;", "e" );
trap_Cvar_VariableStringBuffer( "st_postresults", info, sizeof(info) );
UI_InsertMasterServerText( info );
}
Menus_ActivateByName("endofgame");
// the client is disconnected via Com_Error, since we don't actually want to display
// the message we can trash it here and nothing will pop up on the user.
trap_Cvar_Set("com_errorMessage", "" );
trap_Cvar_Set("st_summary", "0" );
}
} break;
case UI_ST_INGAMEMENU:
{
if( st.gametype == GT_HUB )
{
Menus_ActivateByName("travel_menu");
Menus_ActivateByName("status_page");
}
else
Menus_ActivateByName("escape_menu");
} break;
case UI_ST_CHATMENU:
{
if( GS_INPLAY && !trap_Cvar_VariableInt( "ui_singlePlayerActive" ) )
{
menuDef_t *m = Menus_FindByName( "Ingame_Chat" );
if( m && !(m->window.flags & WINDOW_VISIBLE) )
Menus_Activate( m );
}
}
break;
case UI_ST_OWNERDRAW_PAINT:
{
itemDef_t * item = va_arg( argptr, itemDef_t * );
switch( item->window.ownerDraw )
{
case CG_SNIPER_ZOOM:
{
static qhandle_t sniperZoom = 0;
if( !sniperZoom )
sniperZoom = build_sniperzoom();
DrawShape( &item->window.rect, sniperZoom, item->window.background );
}
break;
case UI_TRAVEL_RECORD_BAR:
{
UI_ST_DrawTravelRecordBar( item );
}
break;
case UI_TRAVELSCREEN:
{
int i;
float a;
float sel_scale = 1.0F;
float time = (float)GS_TIME;
float transfer_time = Com_Clamp( 0, 1,
(float)(uiInfo.uiDC.realTime - st.travel.lerpTime[0]) /
(float)(st.travel.lerpTime[1] - st.travel.lerpTime[0]) );
// we are in the front end, animating show case behind guis...
if( ui_spacetrader.integer == 0 )
{
UI_ST_DrawSolarSystemShowcase( item, transfer_time );
break;
}
switch( st.travel.zoomState )
{
case STZ_IN:
a = 1.0F - Com_Clamp( 0, 1, transfer_time * 4 );
transfer_time = UI_ST_SolarSystemAdjustCameraCurve( transfer_time );
sel_scale = 1.0F - transfer_time;
break;
case STZ_OUT:
a = 1.0F - Com_Clamp( 0, 1, (1.0F - transfer_time) * 4 );
transfer_time = UI_ST_SolarSystemAdjustCameraCurve( transfer_time );
sel_scale = transfer_time;
break;
default:
a = 1.0F;
break;
}
#if 0
if( ui_spacetrader.integer == 1 && trap_Key_IsDown( K_MOUSE1 ) )
{
menuDef_t * menu = Menus_FindByName( "travel_page" );
if ( menu && menu->window.flags&WINDOW_VISIBLE ) {
itemDef_t * item = Menu_FindItemByName( menu, "recordbar" );
if ( item ) {
float x = (float)uiInfo.uiDC.cursorx;
float y = (float)uiInfo.uiDC.cursory;
if ( Rect_ContainsPoint( &item->window.rect, x,y ) ) {
time = ((x-item->window.rect.x)/(item->window.rect.w)) * st.travel.totalTime;
}
}
}
}
#endif
SolarSystem_LerpEye( &st.travel.ss.eye, &st.travel.eye[0], &st.travel.eye[1], 1, transfer_time );
SolarSystem_Draw( &st.travel.ss, &item->window.rect,
(item->window.flags & WINDOW_DECORATION) ? NULL : st.travel.selected,
(item->window.flags & WINDOW_DECORATION) ? NULL : st.travel.current,
time, sel_scale );
if( a < 0.001f )
break;
for( i = 0; i < st.travel.ss.planetCount; i++ )
{
planetInfo_t * p = st.travel.ss.planets + i;
vec3_t pos;
rectDef_t r;
// skip any invisible planets
if( !p->visible || p->decoration )
continue;
SolarSystem_PlanetPosition( p, (float)GS_TIME, pos );
if( SolarSystem_GetScreenRect( item, pos, (float)p->size * 4 + 10, &r, 0 ) )
{
UI_ST_DrawPlanetLabels( &r, p, item, a );
}
}
} break;
case UI_SHOPBUY_PRICEGRAPH:
{
if ( sql( "SELECT feeders.name[ 'buylist' ].id, feeders.name[ 'selllist' ].id;", 0 ) ) {
int buy = sqlint(0);
int sell = sqlint(1);
int id;
sqldone();
if ( buy >= 0 ) {
id = buy;
} else if ( sell >= 0 ) {
id = sell;
} else
break;
draw_pricegraph( item, id );
}
} break;
case UI_UPGRADE_PRICEGRAPH:
//draw_pricegraph( item, GS_CARGO_ID, GS_TIME-120, GS_TIME );
break;
case UI_PLAYERSELECT_SLOT0_MODEL:
case UI_PLAYERSELECT_SLOT1_MODEL:
case UI_PLAYERSELECT_SLOT2_MODEL:
case UI_PLAYERSELECT_SLOT3_MODEL:
case UI_PLAYERSELECT_SLOT4_MODEL:
{
menuDef_t *parent = (menuDef_t*)item->parent;
float zoom = min( 1.0f, (uiInfo.uiDC.realTime - item->focusTime)/200.0f );
if ( item != parent->focusItem )
zoom = 1.0f - zoom;
zoom *= zoom;
UI_DrawPlayer ( &item->window.rect,
&uiInfo.characters[ item->window.ownerDraw-UI_PLAYERSELECT_SLOT0_MODEL ].info,
uiInfo.uiDC.realTime,
zoom
);
} break;
case UI_PLAYER_MODEL:
{
UI_DrawPlayer ( &item->window.rect,
&uiInfo.characters[ st.selectedprofile ].info,
uiInfo.uiDC.realTime, 1.0f );
} break;
case UI_CURRENT_MODEL:
{
UI_DrawPlayer ( &item->window.rect,
&st.modelinfo,
uiInfo.uiDC.realTime, 1.0f );
} break;
case UI_DRAW_PLANET:
{
int planet_id = GS_PLANET;
if ( item->values ) {
if ( trap_SQL_Prepare( item->values ) ) {
trap_SQL_Step();
planet_id = trap_SQL_ColumnAsInt( 0 );
trap_SQL_Done();
}
}
UI_DrawPlanet ( &st.travel.ss, &item->window.rect, planet_id, (float)GS_TIME );
} break;
case UI_NEWS_TICKER:
{
if( st.news.count > 0 )
{
char ticker[100];
int t = uiInfo.uiDC.realTime;
int o = (t - st.news.tickerTime) / 100;
int i;
for( i = 0; i < sizeof( ticker ); i++ )
{
int x = o + i - sizeof( ticker );
if( x >= 0 )
{
char c;
if( Q_IsColorString( st.news.blurbs[st.news.tickerIndex].text + x ) )
{
o += 2;
i--;
continue;
}
c = st.news.blurbs[st.news.tickerIndex].text[x];
ticker[i] = c;
if( !c )
break;
continue;
}
ticker[i] = ' ';
}
trap_R_RenderText( &item->window.rect, item->textscale, item->window.foreColor,
ticker, -2, item->textalignx, item->textaligny, item->textStyle, item->font,
-1, 0 );
if( i == 0 )
{
st.news.tickerTime = uiInfo.uiDC.realTime;
st.news.tickerIndex = (st.news.tickerIndex + 1) % st.news.count;
}
}
} break;
case UI_RADAR:
{
int n;
int i;
rectDef_t rc;
const rectDef_t *r = &item->window.rect;
radarInfo_t units[ 64 ];
float cx, cy, R;
#define FIELD_OFFSET( T, f ) (int)(&((T*)0)->f)
radarFilter_t filters[] =
{
{ ET_PLAYER, FIELD_OFFSET( entityState_t, pos.trBase ) },
{ ET_ITEM, FIELD_OFFSET( entityState_t, origin ) },
};
R = min( r->w, r->h );
n = trap_Get_Radar( units, lengthof( units ), filters, lengthof( filters ), 1500.0f );
R *= 0.5F;
cx = (r->x + r->x + r->w) * 0.5F;
cy = (r->y + r->y + r->h) * 0.5F;
trap_R_SetColor( colorWhite );
for( i = 0; i < n; i++ )
{
qhandle_t t;
const radarInfo_t *ri = units + i;
//no they aren't backwards, check out the definition of ri->theta
float x = sinf( ri->theta ) * ri->r * R;
float y = cosf( ri->theta ) * ri->r * R;
rc.x = cx + x - 8.0F;
rc.y = cy + y - 8.0F;
rc.w = 16.0F;
rc.h = 16.0F;
switch( ri->ent.eType )
{
case ET_PLAYER: {
if (!(ri->ent.eFlags & EF_DEAD)) {
t = ( ri->ent.clientNum < MAX_PLAYERS )?uiInfo.uiDC.Assets.radar_player:uiInfo.uiDC.Assets.radar_bot;
} else {
t = 0;
}
} break;
case ET_ITEM: t = trap_R_RegisterShaderNoMip( bg_itemlist[ri->ent.modelindex].radar_effect ); break;
default: t = 0;
}
if ( t ) {
UI_DrawHandlePic( rc.x, rc.y, rc.w, rc.h, t );
}
}
}
break;
case UI_BOSSHEALTH:
{
rectDef_t * r = &item->window.rect;
if ( sql( "SELECT icon FROM icons SEARCH npc ?;", "i", GS_BOSS ) ) {
trap_R_SetColor( item->window.backColor );
uiInfo.uiDC.drawHandlePic( r->x, r->y, r->w, r->h, sqlint(0) );
if ( GS_ASSASSINATE.maxhealth > 0 ) {
float x,y,w;
float h = r->h * (float)GS_ASSASSINATE.health / GS_ASSASSINATE.maxhealth;
x = r->x;
y = r->y + h;
w = r->w;
h = r->h - h;
UI_AdjustFrom640( &x, &y, &w, &h );
uiInfo.uiDC.drawStretchPic( x, y, w, h, 0, 0, 1, 1, uiInfo.uiDC.Assets.redbar );
}
sqldone();
}
} break;
case UI_TRAVEL_DISASTERICON:
{
const planetInfo_t * p = st.travel.selected;
if ( p && p->disaster_name && p->disaster_name[0] && p->disaster_icon ) {
rectDef_t * r = &item->window.rect;
uiInfo.uiDC.setColor( colorWhite );
UI_DrawHandlePic( r->x, r->y, r->w, r->h, st.travel.selected->disaster_icon );
}
} break;
}
} break;
case UI_ST_OWNERDRAW_MOUSEMOVE:
{
if ( ui_spacetrader.integer != 0 )
{
menuDef_t * menu = Menus_FindByName( "travel_details" );
itemDef_t * item = va_arg( argptr, itemDef_t * );
float x = (float)va_arg( argptr, double ); // todo : pass floats instead of promoting.
float y = (float)va_arg( argptr, double );
if ( menu && menu->window.flags & WINDOW_VISIBLE ) {
break;
}
switch( item->window.ownerDraw )
{
case UI_TRAVELSCREEN:
{
//_UI_DrawRect( x-4, y-4, 8, 8, 2.0f, colorRed );
if( uiInfo.uiDC.realTime >= st.travel.selectLockTime )
UI_ST_SelectPlanet( SolarSystem_Select( &st.travel.ss, item, (float)GS_TIME, x,y ), item );
} break;
}
}
} break;
case UI_ST_CONSOLECOMMAND:
{
return consolecommand( va_arg( argptr, const char * ) );
} break;
case UI_ST_SCRIPTCOMMAND:
{
const char * cmd = va_arg( argptr, const char * );
const char ** args = va_arg( argptr, const char** );
scriptcommand( cmd, args );
} break;
case UI_ST_CONFIGSTRING:
{
int num = va_arg( argptr, int );
if ( num >= CS_PLAYERS && num < CS_PLAYERS + MAX_CLIENTS && trap_Cvar_VariableInt( "cl_spacetrader" ) == 1 ) {
sql( "UPDATE icons SET icon=portrait(clients.client[client]^model) SEARCH client ?;", "ie", num-CS_PLAYERS );
}
} break;
case UI_ST_REQUEST_HIGHSCORES:
{
while( sql( "SELECT Title FROM missions;", 0 ) )
{
trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalhighscores \"%s\" \"%s\";", sqltext(0), UI_Cvar_VariableString("name") ) );
}
}
break;
case UI_ST_REPORT_HIGHSCORE_RESPONSE:
{
// \next_points\220\percentile\0.196723144834655\next_name\^2FrZ|^3Blaze\rank\3\points\0
if ( trap_Cvar_VariableInt( "cl_spacetrader" ) == 0 ) {
char info[ MAX_INFO_STRING ];
trap_Cvar_VariableStringBuffer( "st_postresults", info, sizeof(info) );
UI_InsertMasterServerText( info );
}
} break;
case UI_ST_AUTHORIZED:
{
int code = va_arg( argptr, int );
char info[ MAX_INFO_STRING ];
trap_Cvar_VariableStringBuffer( "cl_servermessage", info, sizeof(info) );
switch(code)
{
case AUTHORIZE_OK:
{
menuDef_t * m = Menus_FindByName( "login" );
//clear out old characters
memset(uiInfo.characters, 0, sizeof(uiInfo.characters));
if ( m && m->window.flags & WINDOW_VISIBLE )
{
Menus_Close( m );
Menus_OpenByName("selectplayer");
}
trap_Cvar_Set( "ui_logged_in", "1" );
} break;
case AUTHORIZE_NOTVERIFIED:
{
trap_Cvar_Set( "ui_password", "" );
trap_Cvar_Set( "ui_logged_in", "2" );
}
break;
case AUTHORIZE_BAD:
{
trap_Cvar_Set( "ui_password", "" );
trap_Cvar_Set( "ui_logged_in", "0" );
} break;
case AUTHORIZE_UNAVAILABLE:
{
menuDef_t * m = Menus_FindByName( "login" );
if ( m && m->window.flags & WINDOW_VISIBLE )
{
Menus_Close( m );
Menus_OpenByName("noauth");
}
trap_Cvar_Set( "ui_logged_in", "0" );
} break;
case AUTHORIZE_DELETECHARACTER:
{
} break;
case AUTHORIZE_CREATECHARACTER: // flow through
{
int error_code = atoi(info);
if (error_code == -1)
{
int i;
for (i=0; i < MAX_CHARACTERS; i++) {
if (uiInfo.characters[i].name[0] == '\0') {
uiInfo.characters[i].info.legsModel = 0;
}
}
if ( sql( "SELECT index FROM strings SEARCH name 'TT_PLAYER_INVALIDNAME';", 0 ) ) {
char t[ MAX_INFO_STRING ];
trap_SQL_Run( t, sizeof(t), sqlint(0), 0,0,0 );
sqldone();
trap_Cvar_Set( "ui_errorMessage", t );
Menus_OpenByName( "error_popmenu" );
break;
}
}
}
case AUTHORIZE_ACCOUNTINFO:
{
int i;
for ( i=0; i<MAX_CHARACTERS; i++ )
{
const char * s = Info_ValueForKey( info, va("%d", i ) );
if ( s && s[0] )
{
characterInfo_t * c = uiInfo.characters + i;
Q_strncpyz( c->name, COM_Parse( &s ), sizeof(c->name) );
Q_strncpyz( c->model, COM_Parse( &s ), sizeof(c->model) );
c->rank = atoi(COM_Parse( &s ));
UI_ST_PlayerSelectSlot_SetModel( &c->info, c->model );
} else
uiInfo.characters[ i ].name[ 0 ] = '\0';
}
} break;
}
}
break;
case UI_ST_SERVER_ERRORMESSAGE:
{
char *message = va_arg( argptr, char * );
Com_Error( ERR_SERVERDISCONNECT, message);
} break;
}
return 1;
}
int UI_GlobalInt() {
char token[ MAX_NAME_LENGTH ];
trap_Argv( 0, token, sizeof(token) );
switch( SWITCHSTRING(token) )
{
// "%taxes"
case CS( 't','a','x','e'):
{
int tax = 0;
sql( "SELECT max(missions.key['moa_mintax'].value,((inventory+cash)*missions.key['moa_taxrate'].value)/100) FROM players SEARCH client %client;", "sId", &tax );
return tax;
} break;
case CS( 'c','a','r','g'):
{
int cargo_left;
sql( "SELECT cargomax-cargo FROM players SERACH client %client;", "sId", &cargo_left );
return cargo_left;
} break; // "%cargoleft"
}
return 0;
}
const char * UI_ST_getString( int id, ... )
{
va_list argptr;
va_start(argptr, id);
switch( id )
{
case UI_PLAYERSELECT_SLOT0_BTN:
case UI_PLAYERSELECT_SLOT1_BTN:
case UI_PLAYERSELECT_SLOT2_BTN:
case UI_PLAYERSELECT_SLOT3_BTN:
case UI_PLAYERSELECT_SLOT4_BTN:
{
return (uiInfo.characters[ id-UI_PLAYERSELECT_SLOT0_BTN ].name[0])?"DELETE":"CREATE";
} break;
case UI_PLAYERSELECT_SLOT0_NAME:
case UI_PLAYERSELECT_SLOT1_NAME:
case UI_PLAYERSELECT_SLOT2_NAME:
case UI_PLAYERSELECT_SLOT3_NAME:
case UI_PLAYERSELECT_SLOT4_NAME:
{
characterInfo_t * c = uiInfo.characters + id-UI_PLAYERSELECT_SLOT0_NAME;
if ( c->name[ 0 ] == '\0' )
return 0;
if ( c->rank == 0 )
return c->name;
return va( "%s^-\n%s PLACE", c->name, fn(c->rank,FN_RANK) );
} break;
case UI_PLAYER_NAME:
{
return (st.selectedprofile>=0)?uiInfo.characters[ st.selectedprofile ].name:0;
} break;
default:
{
char t[ 2048 ];
trap_SQL_Run( t, sizeof(t), id, 0,0,0 );
return va( "%s", t );
} break;
}
}