From 8777a286a029c2caaaefc329a700b5c146fbc0c6 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Thu, 25 Oct 2018 01:19:48 +1100 Subject: [PATCH 01/14] begin stock market --- .../irresistible/cnr/features/_features.pwn | 1 + .../cnr/features/stocks/_stocks.pwn | 9 +++++++++ .../irresistible/cnr/features/stocks/stocks.pwn | 17 +++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 gamemodes/irresistible/cnr/features/stocks/_stocks.pwn create mode 100644 gamemodes/irresistible/cnr/features/stocks/stocks.pwn diff --git a/gamemodes/irresistible/cnr/features/_features.pwn b/gamemodes/irresistible/cnr/features/_features.pwn index 2a45333..350cb81 100644 --- a/gamemodes/irresistible/cnr/features/_features.pwn +++ b/gamemodes/irresistible/cnr/features/_features.pwn @@ -15,6 +15,7 @@ #include "irresistible\cnr\features\robbery\_robbery.pwn" #include "irresistible\cnr\features\visage\_visage.pwn" #include "irresistible\cnr\features\minijobs\_minijobs.pwn" +#include "irresistible\cnr\features\stocks\_stocks.pwn" // other #include "irresistible\cnr\features\shop.pwn" diff --git a/gamemodes/irresistible/cnr/features/stocks/_stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/_stocks.pwn new file mode 100644 index 0000000..be8bb0e --- /dev/null +++ b/gamemodes/irresistible/cnr/features/stocks/_stocks.pwn @@ -0,0 +1,9 @@ +/* + * Irresistible Gaming (c) 2018 + * Developed by Lorenc Pekaj + * Module: cnr\features\stocks\_stocks.pwn + * Purpose: encloses all stocks modules & components (cnr) + */ + +/* ** Includes ** */ +#include "irresistible\cnr\features\stocks\stocks.pwn" diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn new file mode 100644 index 0000000..d7b1acd --- /dev/null +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -0,0 +1,17 @@ +/* + * Irresistible Gaming (c) 2018 + * Developed by Lorenc Pekaj + * Module: cnrs + * Purpose: + */ + +/* ** Includes ** */ +#include < YSI\y_hooks > + +/* ** Definitions ** */ + +/* ** Variables ** */ + +/* ** Hooks ** */ + +/* ** Functions ** */ From b1847fffdf2a798390d99146f9ebff38ab313846 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Thu, 25 Oct 2018 08:32:18 +1100 Subject: [PATCH 02/14] begin base for price of stocks --- .../cnr/features/stocks/stocks.pwn | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index d7b1acd..6f8e89e 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -9,9 +9,192 @@ #include < YSI\y_hooks > /* ** Definitions ** */ +#define MAX_STOCKS ( 32 ) + +#define STOCK_REPORTING_PERIOD ( 86400 ) // 1 day + +#define STOCK_REPORTING_PERIODS ( 30 ) // last 30 periods (days) + +#define DIALOG_STOCK_MARKET 8923 /* ** Variables ** */ +enum E_STOCK_MARKET_DATA +{ + E_NAME[ 64 ], E_SYMBOL[ 4 ], E_MAX_SHARES, + + // market maker + E_MM_SHARES, E_MM_IPO_PRICE +}; + +enum E_STOCK_MARKET_PRICE_DATA +{ + E_CURRENT_PRICE, + E_CURRENT_EARNINGS, E_PREVIOUS_EARNINGS +}; + +static stock + g_stockMarketData [ MAX_STOCKS ] [ E_STOCK_MARKET_DATA ], + g_stockMarketPriceData [ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ], + Iterator: stockmarkets < MAX_STOCKS > +; /* ** Hooks ** */ +hook OnScriptInit( ) +{ + CreateStockMarket( 0, "The Mining Company", "MC", 10000000, 100 ); + return 1; +} + +/* ** SQL Thread ** */ +thread Stock_UpdateReportingPeriods( stockid ) +{ + new + rows = cache_get_row_count( ); + + if ( rows ) + { + for ( new row = 0; row < rows; row ++ ) + { + g_stockMarketPriceData[ stockid ] [ row ] [ E_CURRENT_PRICE ] = cache_get_field_content_int( row, "CURRENT_PRICE" ); + g_stockMarketPriceData[ stockid ] [ row ] [ E_CURRENT_EARNINGS ] = cache_get_field_content_int( row, "CURRENT_EARNINGS" ); + g_stockMarketPriceData[ stockid ] [ row ] [ E_PREVIOUS_EARNINGS ] = cache_get_field_content_int( row, "PREVIOUS_EARNINGS" ); + } + } + return 1; +} + +/* ** Command ** */ +CMD:increase( playerid, params[ ] ) { + g_stockMarketPriceData[ 0 ] [ 0 ] [ E_CURRENT_EARNINGS ] += strval( params ); + return 1; +} + +CMD:newreport( playerid, params[ ] ) { + StockMarket_CreateReport( 0 ); + return 1; +} + +CMD:stockmarkets( playerid, params[ ] ) +{ + szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Current Price\t"COL_WHITE"Market Capitalization\t"COL_WHITE"Price Change (24H)\n"; + + foreach ( new s : stockmarkets ) + { + printf("%d %d\n", g_stockMarketPriceData[ s ] [ 0 ] [ E_CURRENT_EARNINGS ], g_stockMarketPriceData[ s ] [ 1 ] [ E_CURRENT_EARNINGS ]); + new Float: price_difference = ( ( float( g_stockMarketPriceData[ s ] [ 0 ] [ E_CURRENT_EARNINGS ] ) / float( g_stockMarketPriceData[ s ] [ 1 ] [ E_CURRENT_EARNINGS ] ) ) - 1.0 ) * 100.0; + new current_price = g_stockMarketPriceData[ s ] [ 0 ] [ E_CURRENT_PRICE ]; + + // prevent integer overflow (over 2.6B) + new Float: market_cap_millions = ( float( g_stockMarketData[ s ] [ E_MAX_SHARES ] ) / 1000000.0 ) * ( float( current_price ) / 1000000.0 ); + new Float: market_cap_billions = market_cap_millions / 1000.0; + + format( + szLargeString, sizeof( szLargeString ), + "%s%s (%s)\t"COL_GREEN"%s\t$%s%s\t%s%s%%\n", + szLargeString, + g_stockMarketData[ s ] [ E_NAME ], + g_stockMarketData[ s ] [ E_SYMBOL ], + cash_format( current_price ), + market_cap_billions > 1.0 ? number_format( market_cap_billions, .decimals = 2 ) : number_format( market_cap_millions, .decimals = 2 ), + market_cap_billions > 1.0 ? ( "B" ) : ( "M" ), + price_difference >= 0.0 ? ( COL_GREEN ) : ( COL_RED ), + number_format( price_difference, .decimals = 2 ) + ); + } + + return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Select", "Close" ); +} /* ** Functions ** */ +stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], max_shares, ipo_price ) +{ + if ( ! Iter_Contains( stockmarkets, stockid ) ) + { + strcpy( g_stockMarketData[ stockid ] [ E_NAME ], name ); + strcpy( g_stockMarketData[ stockid ] [ E_SYMBOL ], symbol ); + + g_stockMarketData[ stockid ] [ E_MM_SHARES ] = max_shares; + g_stockMarketData[ stockid ] [ E_MM_IPO_PRICE ] = ipo_price; + + // reset stock price information + for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ); r ++ ) { + g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_PRICE ] = 0; + g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_EARNINGS ] = 0; + g_stockMarketPriceData[ stockid ] [ r ] [ E_PREVIOUS_EARNINGS ] = 0; + } + + // load price information if there is + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_REPORTS` WHERE `STOCK_ID`=%d ORDER BY `REPORTING_TIME` DESC LIMIT %d", stockid, sizeof( g_stockMarketPriceData[ ] ) ), "Stock_UpdateReportingPeriods", "d", stockid ); + + // add to iterator + Iter_Add( stockmarkets, stockid ); + } + return stockid; +} + +stock StockMarket_CreateReport( stockid ) +{ + new old_earnings = g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_EARNINGS ]; + new old_price = g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_PRICE ]; + + // store temporary stock info + new temp_stock_price_data[ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ]; + temp_stock_price_data = g_stockMarketPriceData; + + // shift all earnings by one + for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ) - 2; r ++ ) { + + g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_CURRENT_PRICE ] = temp_stock_price_data[ stockid ] [ r ] [ E_CURRENT_PRICE ]; + g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_CURRENT_EARNINGS ] = temp_stock_price_data[ stockid ] [ r ] [ E_CURRENT_EARNINGS ]; + g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_PREVIOUS_EARNINGS ] = temp_stock_price_data[ stockid ] [ r ] [ E_PREVIOUS_EARNINGS ]; + } + + // price difference + new Float: price_change = float( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_EARNINGS ] ) / float( old_earnings ); + + // circuit breaker ... max 20% loss + if ( price_change < 0.8 ) { + price_change = 0.8; + } + + new new_price = floatround( float( old_price ) * price_change ); + + // minimum stock price should be $10 + if ( new_price < 1 ) { + new_price = 1; + } + + // set information + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_PRICE ] = new_price; + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_EARNINGS ] = 0; // set to 0 + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PREVIOUS_EARNINGS ] = old_earnings; + + // insert to database + + + // + erase(szLargeString); + // shift all earnings by one + for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ); r ++ ) { + format( szLargeString, 1024, "%s{%d,%d,%d}, ", szLargeString, g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_PRICE ], g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_EARNINGS ], g_stockMarketPriceData[ stockid ] [ r ] [ E_PREVIOUS_EARNINGS ] ); + } + + printf("%s\n",szLargeString); + return 1; +} + +stock StockMarket_GetCurrentPrice( stockid ) { + return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_PRICE ]; +} + +/* + + CREATE TABLE IF NOT EXISTS `STOCK_REPORTS` ( + `STOCK_ID` int(11), + `CURRENT_PRICE` int(11), + `CURRENT_EARNINGS` int(11), + `PREVIOUS_EARNINGS` int(11), + `REPORTING_TIME` TIMESTAMP default CURRENT_TIMESTAMP + ); + + */ From 8d8fdc1392ebc3019f200a08774276f7481f16d6 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Fri, 26 Oct 2018 04:58:12 +1100 Subject: [PATCH 03/14] use float for prices, reduce data redundancy --- .../cnr/features/stocks/stocks.pwn | 179 ++++++++++++------ gamemodes/irresistible/servervars.pwn | 9 + 2 files changed, 127 insertions(+), 61 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index 6f8e89e..9c3e9ca 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -11,7 +11,7 @@ /* ** Definitions ** */ #define MAX_STOCKS ( 32 ) -#define STOCK_REPORTING_PERIOD ( 86400 ) // 1 day +#define STOCK_REPORTING_PERIOD ( 500 ) // 1 day #define STOCK_REPORTING_PERIODS ( 30 ) // last 30 periods (days) @@ -28,8 +28,8 @@ enum E_STOCK_MARKET_DATA enum E_STOCK_MARKET_PRICE_DATA { - E_CURRENT_PRICE, - E_CURRENT_EARNINGS, E_PREVIOUS_EARNINGS + E_SQL_ID, Float: E_PRICE, + Float: E_EARNINGS }; static stock @@ -41,7 +41,33 @@ static stock /* ** Hooks ** */ hook OnScriptInit( ) { - CreateStockMarket( 0, "The Mining Company", "MC", 10000000, 100 ); + // server variables + AddServerVariable( "stock_report_time", "0", GLOBAL_VARTYPE_INT ); + + // create markets + CreateStockMarket( 0, "The Mining Company", "MC", 50000000, 100 ); + return 1; +} + +hook OnServerUpdate( ) +{ + new current_time = GetServerTime( ); + new last_reporting = GetServerVariableInt( "stock_report_time" ); + + // check if its reporting time + if ( current_time > last_reporting ) + { + // reporting period + UpdateServerVariableInt( "stock_report_time", current_time + STOCK_REPORTING_PERIOD ); + + // create a new reporting period for every stock there + foreach ( new s : stockmarkets ) + { + StockMarket_CreateReport( s ); + } + + print( "Successfully created new reporting period for all online companies" ); + } return 1; } @@ -55,17 +81,23 @@ thread Stock_UpdateReportingPeriods( stockid ) { for ( new row = 0; row < rows; row ++ ) { - g_stockMarketPriceData[ stockid ] [ row ] [ E_CURRENT_PRICE ] = cache_get_field_content_int( row, "CURRENT_PRICE" ); - g_stockMarketPriceData[ stockid ] [ row ] [ E_CURRENT_EARNINGS ] = cache_get_field_content_int( row, "CURRENT_EARNINGS" ); - g_stockMarketPriceData[ stockid ] [ row ] [ E_PREVIOUS_EARNINGS ] = cache_get_field_content_int( row, "PREVIOUS_EARNINGS" ); + g_stockMarketPriceData[ stockid ] [ row ] [ E_SQL_ID ] = cache_get_field_content_int( row, "ID" ); + g_stockMarketPriceData[ stockid ] [ row ] [ E_PRICE ] = cache_get_field_content_float( row, "CURRENT_PRICE" ); + g_stockMarketPriceData[ stockid ] [ row ] [ E_EARNINGS ] = cache_get_field_content_float( row, "CURRENT_EARNINGS" ); } } return 1; } +thread StockMarket_InsertReport( stockid ) +{ + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_SQL_ID ] = cache_insert_id( ); + return 1; +} + /* ** Command ** */ CMD:increase( playerid, params[ ] ) { - g_stockMarketPriceData[ 0 ] [ 0 ] [ E_CURRENT_EARNINGS ] += strval( params ); + StockMarket_UpdateEarnings( 0, strval( params ) ); return 1; } @@ -80,28 +112,23 @@ CMD:stockmarkets( playerid, params[ ] ) foreach ( new s : stockmarkets ) { - printf("%d %d\n", g_stockMarketPriceData[ s ] [ 0 ] [ E_CURRENT_EARNINGS ], g_stockMarketPriceData[ s ] [ 1 ] [ E_CURRENT_EARNINGS ]); - new Float: price_difference = ( ( float( g_stockMarketPriceData[ s ] [ 0 ] [ E_CURRENT_EARNINGS ] ) / float( g_stockMarketPriceData[ s ] [ 1 ] [ E_CURRENT_EARNINGS ] ) ) - 1.0 ) * 100.0; - new current_price = g_stockMarketPriceData[ s ] [ 0 ] [ E_CURRENT_PRICE ]; - - // prevent integer overflow (over 2.6B) - new Float: market_cap_millions = ( float( g_stockMarketData[ s ] [ E_MAX_SHARES ] ) / 1000000.0 ) * ( float( current_price ) / 1000000.0 ); - new Float: market_cap_billions = market_cap_millions / 1000.0; + printf("%f %f\n", g_stockMarketPriceData[ s ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ s ] [ 1 ] [ E_EARNINGS ]); + new Float: price_difference = ( g_stockMarketPriceData[ s ] [ 1 ] [ E_EARNINGS ] / g_stockMarketPriceData[ s ] [ 2 ] [ E_EARNINGS ] - 1.0 ) * 100.0; + new Float: current_price = g_stockMarketPriceData[ s ] [ 1 ] [ E_PRICE ]; format( szLargeString, sizeof( szLargeString ), - "%s%s (%s)\t"COL_GREEN"%s\t$%s%s\t%s%s%%\n", + "%s%s (%s)\t"COL_GREEN"%s\t%s%s%%\n", szLargeString, g_stockMarketData[ s ] [ E_NAME ], g_stockMarketData[ s ] [ E_SYMBOL ], - cash_format( current_price ), - market_cap_billions > 1.0 ? number_format( market_cap_billions, .decimals = 2 ) : number_format( market_cap_millions, .decimals = 2 ), - market_cap_billions > 1.0 ? ( "B" ) : ( "M" ), + cash_format( current_price, .decimals = 0 ), price_difference >= 0.0 ? ( COL_GREEN ) : ( COL_RED ), number_format( price_difference, .decimals = 2 ) ); } + SendServerMessage( playerid, "The stock market will close in %s.", secondstotime( GetServerVariableInt( "stock_report_time" ) - GetServerTime( ) ) ); return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Select", "Close" ); } @@ -118,9 +145,8 @@ stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], max_share // reset stock price information for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ); r ++ ) { - g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_PRICE ] = 0; - g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_EARNINGS ] = 0; - g_stockMarketPriceData[ stockid ] [ r ] [ E_PREVIOUS_EARNINGS ] = 0; + g_stockMarketPriceData[ stockid ] [ r ] [ E_PRICE ] = 0.0; + g_stockMarketPriceData[ stockid ] [ r ] [ E_EARNINGS ] = 0.0; } // load price information if there is @@ -134,8 +160,20 @@ stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], max_share stock StockMarket_CreateReport( stockid ) { - new old_earnings = g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_EARNINGS ]; - new old_price = g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_PRICE ]; + // limit a 20% loss for players + new Float: circuit_breaker = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ] * 0.8; + + if ( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] < circuit_breaker ) { + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = circuit_breaker; + } + + // change stock price proportional to earnings increase/decrease + new Float: price_difference = g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] / g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ]; + printf("Earnings %f\n", price_difference ); + + if ( ( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ] * price_difference ) < 1.0 ) { + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = 1.0; + } // store temporary stock info new temp_stock_price_data[ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ]; @@ -143,58 +181,77 @@ stock StockMarket_CreateReport( stockid ) // shift all earnings by one for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ) - 2; r ++ ) { - - g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_CURRENT_PRICE ] = temp_stock_price_data[ stockid ] [ r ] [ E_CURRENT_PRICE ]; - g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_CURRENT_EARNINGS ] = temp_stock_price_data[ stockid ] [ r ] [ E_CURRENT_EARNINGS ]; - g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_PREVIOUS_EARNINGS ] = temp_stock_price_data[ stockid ] [ r ] [ E_PREVIOUS_EARNINGS ]; + g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_PRICE ] = temp_stock_price_data[ stockid ] [ r ] [ E_PRICE ]; + g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_EARNINGS ] = temp_stock_price_data[ stockid ] [ r ] [ E_EARNINGS ]; } - // price difference - new Float: price_change = float( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_EARNINGS ] ) / float( old_earnings ); + // set current price to previous reporting period price + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ]; - // circuit breaker ... max 20% loss - if ( price_change < 0.8 ) { - price_change = 0.8; - } + // reset earnings + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = 0.0; // set to 0 - new new_price = floatround( float( old_price ) * price_change ); + // insert to database the old information + mysql_format( + dbHandle, szBigString, sizeof ( szBigString ), + "INSERT INTO `STOCK_REPORTS` (`STOCK_ID`, `CURRENT_PRICE`, `CURRENT_EARNINGS`) VALUES (%d, %f, %f)", + stockid, + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ], + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] + ); - // minimum stock price should be $10 - if ( new_price < 1 ) { - new_price = 1; - } - - // set information - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_PRICE ] = new_price; - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_EARNINGS ] = 0; // set to 0 - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PREVIOUS_EARNINGS ] = old_earnings; - - // insert to database - - - // - erase(szLargeString); - // shift all earnings by one - for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ); r ++ ) { - format( szLargeString, 1024, "%s{%d,%d,%d}, ", szLargeString, g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_PRICE ], g_stockMarketPriceData[ stockid ] [ r ] [ E_CURRENT_EARNINGS ], g_stockMarketPriceData[ stockid ] [ r ] [ E_PREVIOUS_EARNINGS ] ); - } - - printf("%s\n",szLargeString); + mysql_tquery( dbHandle, szBigString, "StockMarket_InsertReport", "d", stockid ); return 1; } -stock StockMarket_GetCurrentPrice( stockid ) { - return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_CURRENT_PRICE ]; +stock StockMarket_UpdateEarnings( stockid, amount ) +{ + if ( ! Iter_Contains( stockmarkets, stockid ) ) + return 0; + + printf( "Current Earnings: %f, Prior Earnings: %f", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ] ); + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] += amount; + mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `CURRENT_EARNINGS` = `CURRENT_EARNINGS` + %d WHERE `ID` = %d", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); + return 1; +} + +stock Float: StockMarket_GetEarnings( stockid ) { + return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ]; +} + +stock Float: StockMarket_GetCurrentPrice( stockid ) { + return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ]; +} + +stock StockMarket_MakeSellOrder( stockid, accountid, Float: shares, Float: price ) +{ + //INSERT INTO `STOCK_ORDERS` (`USER_ID`, `STOCK_ID`, ``) + return 1; } /* CREATE TABLE IF NOT EXISTS `STOCK_REPORTS` ( + `ID` int(11) primary key auto_increment, `STOCK_ID` int(11), - `CURRENT_PRICE` int(11), - `CURRENT_EARNINGS` int(11), - `PREVIOUS_EARNINGS` int(11), + `CURRENT_PRICE` float, + `CURRENT_EARNINGS` float, `REPORTING_TIME` TIMESTAMP default CURRENT_TIMESTAMP ); + CREATE TABLE IF NOT EXISTS `STOCK_OWNERS` ( + `ID` int(11) primary key auto_increment, + `USER_ID` int(11), + `STOCK_ID` int(11), + `SHARES` float, + `PRICE` float + ); + + CREATE TABLE IF NOT EXISTS `STOCK_ORDERS` ( + `ID` int(11) primary key auto_increment, + `USER_ID` int(11), + `STOCK_ID` int(11), + `SHARES` float, + `PRICE` float, + ); */ diff --git a/gamemodes/irresistible/servervars.pwn b/gamemodes/irresistible/servervars.pwn index 74c9d94..f135fde 100644 --- a/gamemodes/irresistible/servervars.pwn +++ b/gamemodes/irresistible/servervars.pwn @@ -12,6 +12,15 @@ /* ** Includes ** */ #include < YSI\y_hooks > +/* ** Macros ** */ +#define GetServerVariableInt GetGVarInt + +#define UpdateServerVariableString(%0,%1) \ + (UpdateServerVariable(%0, 0, 0, %1, GLOBAL_VARTYPE_STRING)) + +#define UpdateServerVariableInt(%0,%1) \ + (UpdateServerVariable(%0, %1, 0, "", GLOBAL_VARTYPE_INT)) + /* ** Hooks ** */ hook OnGameModeInit( ) { From 1d99d05bcbc47ac81aa5453612239b0aeba9a361 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Fri, 26 Oct 2018 11:44:45 +1100 Subject: [PATCH 04/14] add buy slip functionality with initial market maker ... work on /shares --- .../cnr/features/stocks/stocks.pwn | 209 ++++++++++++++++-- 1 file changed, 190 insertions(+), 19 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index 9c3e9ca..31211cb 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -16,6 +16,9 @@ #define STOCK_REPORTING_PERIODS ( 30 ) // last 30 periods (days) #define DIALOG_STOCK_MARKET 8923 +#define DIALOG_STOCK_MARKET_BUY 8925 + +#define STOCK_MM_USER_ID ( 0 ) /* ** Variables ** */ enum E_STOCK_MARKET_DATA @@ -23,7 +26,7 @@ enum E_STOCK_MARKET_DATA E_NAME[ 64 ], E_SYMBOL[ 4 ], E_MAX_SHARES, // market maker - E_MM_SHARES, E_MM_IPO_PRICE + Float: E_IPO_SHARES, Float: E_IPO_PRICE }; enum E_STOCK_MARKET_PRICE_DATA @@ -38,6 +41,11 @@ static stock Iterator: stockmarkets < MAX_STOCKS > ; +/* ** Forwards / Getters ** */ +stock Float: StockMarket_GetCurrentPrice( stockid ) { + return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ]; +} + /* ** Hooks ** */ hook OnScriptInit( ) { @@ -45,7 +53,7 @@ hook OnScriptInit( ) AddServerVariable( "stock_report_time", "0", GLOBAL_VARTYPE_INT ); // create markets - CreateStockMarket( 0, "The Mining Company", "MC", 50000000, 100 ); + CreateStockMarket( 0, "The Mining Company", "MC", 1000000, 100 ); return 1; } @@ -71,6 +79,51 @@ hook OnServerUpdate( ) return 1; } +hook OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] ) +{ + if ( dialogid == DIALOG_STOCK_MARKET && response ) + { + new + x = 0; + + foreach ( new s : stockmarkets ) + { + if ( x == listitem ) + { + SetPVarInt( playerid, "stockmarket_selection", s ); + StockMarket_ShowBuySlip( playerid, s ); + } + x ++; + } + return 1; + } + else if ( dialogid == DIALOG_STOCK_MARKET_BUY ) + { + new + stockid = GetPVarInt( playerid, "stockmarket_selection" ); + + if ( response ) + { + new + Float: shares; + + if ( sscanf( inputtext, "f", shares ) ) SendError( playerid, "You must use a valid value." ); + else if ( shares <= 10.0 ) SendError( playerid, "The minimum number of shares you can buy is 10." ); + else + { + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `STOCK_ID` = %d AND `USER_ID` = %d", stockid, STOCK_MM_USER_ID ), "StockMarket_OnPurchaseOrder", "ddf", playerid, stockid, shares ); + return 1; + } + return StockMarket_ShowBuySlip( playerid, stockid ); + } + else + { + return cmd_stockmarkets( playerid, "" ); + } + } + return 1; +} + /* ** SQL Thread ** */ thread Stock_UpdateReportingPeriods( stockid ) { @@ -86,6 +139,17 @@ thread Stock_UpdateReportingPeriods( stockid ) g_stockMarketPriceData[ stockid ] [ row ] [ E_EARNINGS ] = cache_get_field_content_float( row, "CURRENT_EARNINGS" ); } } + else // no historical reporting data, restock the market maker + { + // set current stock market prices to IPO + g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ] = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; + + // create report for the company using the IPO price + StockMarket_CreateReport( stockid ); + + // give market maker shares + StockMarket_GiveShares( stockid, STOCK_MM_USER_ID, g_stockMarketData[ stockid ] [ E_IPO_SHARES ] ); // , g_stockMarketData[ stockid ] [ E_IPO_PRICE ] + } return 1; } @@ -95,6 +159,101 @@ thread StockMarket_InsertReport( stockid ) return 1; } +thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) +{ + new + rows = cache_get_row_count( ); + + if ( ! rows ) { + return SendError( playerid, "This stock has no available shares for sale." ); + } + + // check if the player has the money for the purchase + new Float: ask_price = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ]; + new purchase_cost = floatround( ask_price * shares ); + + if ( GetPlayerCash( playerid ) < purchase_cost ) { + return SendError( playerid, "You need at least %s to purchase this many shares.", cash_format( purchase_cost ) ), StockMarket_ShowBuySlip( playerid, stockid ), 1; + } + + // check if quantity is valid + new Float: available_quantity = cache_get_field_content_float( 0, "SHARES" ); + + if ( shares > available_quantity ) { + return SendError( playerid, "There are not that many shares available for sale." ), StockMarket_ShowBuySlip( playerid, stockid ), 1; + } + + // reduce the market makers shares + StockMarket_GiveShares( stockid, STOCK_MM_USER_ID, -shares ); + + // increment the players shares + StockMarket_GiveShares( stockid, GetPlayerAccountID( playerid ), shares ); + + // reduce player balance and alert + GivePlayerCash( playerid, -purchase_cost ); + SendServerMessage( playerid, "You have successfully purchased %s shares of %s (@ %s/ea) for %s.", number_format( shares, .decimals = 3 ), g_stockMarketData[ stockid ] [ E_NAME ], cash_format( ask_price, .decimals = 2 ), cash_format( purchase_cost ) ); + return 1; +} + +thread StockMarket_OnShowBuySlip( playerid, stockid ) +{ + new + rows = cache_get_row_count( ); + + if ( ! rows ) { + return SendError( playerid, "This stock does now have any shares available to buy." ); + } + + new + Float: available_quantity = cache_get_field_content_float( 0, "SHARES" ); + + format( + szBigString, sizeof ( szBigString ), + ""COL_WHITE"You can buy shares of %s for "COL_GREEN"%s"COL_WHITE" each.\n\nThere are %s available shares to buy.", + g_stockMarketData[ stockid ] [ E_NAME ], + cash_format( g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ), + number_format( available_quantity, .decimals = 3 ) + ); + ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET_BUY, DIALOG_STYLE_INPUT, ""COL_WHITE"Stock Market", szBigString, "Buy", "Close" ); + return 1; +} + +thread StockMarket_OnShowShares( playerid ) +{ + new + rows = cache_get_row_count( ); + + if ( ! rows ) { + return SendError( playerid, "This stock does now have any shares available to buy." ); + } + + szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Total Shares\t"COL_WHITE"Current Price ($)\t"COL_GREEN"Value ($)\n"; + + for ( new row = 0; row < rows; row ++ ) + { + new + stockid = cache_get_field_content_int( row, "ID" ); + + if ( Iter_Contains( stockmarkets, stockid ) ) + { + new Float: current_price = StockMarket_GetCurrentPrice( stockid ); + new Float: shares = cache_get_field_content_float( row, "SHARES" ); + + format( + szLargeString, sizeof( szLargeString ), + "%s%s (%s)\t%s\t%s\t"COL_GREEN"%s\n", + szLargeString, + g_stockMarketData[ stockid ] [ E_NAME ], + g_stockMarketData[ stockid ] [ E_SYMBOL ], + number_format( shares, .decimals = 3 ), + cash_format( current_price, .decimals = 2 ), + cash_format( floatround( shares * current_price ) ) + ); + } + } + return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Sell", "Close" ), 1; +} + /* ** Command ** */ CMD:increase( playerid, params[ ] ) { StockMarket_UpdateEarnings( 0, strval( params ) ); @@ -106,9 +265,10 @@ CMD:newreport( playerid, params[ ] ) { return 1; } +CMD:stocks( playerid, params[ ] ) return cmd_stockmarkets( playerid, params ); CMD:stockmarkets( playerid, params[ ] ) { - szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Current Price\t"COL_WHITE"Market Capitalization\t"COL_WHITE"Price Change (24H)\n"; + szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Current Price\t"COL_WHITE"Price Change (24H)\n"; foreach ( new s : stockmarkets ) { @@ -129,24 +289,30 @@ CMD:stockmarkets( playerid, params[ ] ) } SendServerMessage( playerid, "The stock market will close in %s.", secondstotime( GetServerVariableInt( "stock_report_time" ) - GetServerTime( ) ) ); - return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Select", "Close" ); + return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Buy", "Close" ); +} + +CMD:shares( playerid, params[ ] ) +{ + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `USER_ID` = %d", GetPlayerAccountID( playerid ) ), "StockMarket_OnShowShares", "d", playerid ); + return 1; } /* ** Functions ** */ -stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], max_shares, ipo_price ) +stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ipo_shares, Float: ipo_price ) { if ( ! Iter_Contains( stockmarkets, stockid ) ) { strcpy( g_stockMarketData[ stockid ] [ E_NAME ], name ); strcpy( g_stockMarketData[ stockid ] [ E_SYMBOL ], symbol ); - g_stockMarketData[ stockid ] [ E_MM_SHARES ] = max_shares; - g_stockMarketData[ stockid ] [ E_MM_IPO_PRICE ] = ipo_price; + g_stockMarketData[ stockid ] [ E_IPO_SHARES ] = ipo_shares; + g_stockMarketData[ stockid ] [ E_IPO_PRICE ] = ipo_price; // reset stock price information for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ); r ++ ) { g_stockMarketPriceData[ stockid ] [ r ] [ E_PRICE ] = 0.0; - g_stockMarketPriceData[ stockid ] [ r ] [ E_EARNINGS ] = 0.0; + g_stockMarketPriceData[ stockid ] [ r ] [ E_EARNINGS ] = 1.0; } // load price information if there is @@ -189,7 +355,7 @@ stock StockMarket_CreateReport( stockid ) g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ]; // reset earnings - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = 0.0; // set to 0 + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = 1.0; // set to 1 instead of 0 to prevent errors // insert to database the old information mysql_format( @@ -200,6 +366,8 @@ stock StockMarket_CreateReport( stockid ) g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] ); + // todo: adjust sell orders + mysql_tquery( dbHandle, szBigString, "StockMarket_InsertReport", "d", stockid ); return 1; } @@ -219,18 +387,23 @@ stock Float: StockMarket_GetEarnings( stockid ) { return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ]; } -stock Float: StockMarket_GetCurrentPrice( stockid ) { - return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ]; +stock StockMarket_GiveShares( stockid, accountid, Float: shares ) +{ + mysql_format( + dbHandle, szBigString, sizeof ( szBigString ), + "INSERT INTO `STOCK_OWNERS` (`USER_ID`, `STOCK_ID`, `SHARES`) VALUES (%d, %d, %f) ON DUPLICATE KEY UPDATE `SHARES` = `SHARES` + %f", + accountid, stockid, shares, shares + ); + mysql_single_query( szBigString ); } -stock StockMarket_MakeSellOrder( stockid, accountid, Float: shares, Float: price ) +static stock StockMarket_ShowBuySlip( playerid, stockid ) { - //INSERT INTO `STOCK_ORDERS` (`USER_ID`, `STOCK_ID`, ``) + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `STOCK_ID` = %d AND `USER_ID` = %d", stockid, STOCK_MM_USER_ID ), "StockMarket_OnShowBuySlip", "dd", playerid, stockid ); return 1; } /* - CREATE TABLE IF NOT EXISTS `STOCK_REPORTS` ( `ID` int(11) primary key auto_increment, `STOCK_ID` int(11), @@ -240,18 +413,16 @@ stock StockMarket_MakeSellOrder( stockid, accountid, Float: shares, Float: price ); CREATE TABLE IF NOT EXISTS `STOCK_OWNERS` ( - `ID` int(11) primary key auto_increment, `USER_ID` int(11), `STOCK_ID` int(11), `SHARES` float, - `PRICE` float + PRIMARY KEY (USER_ID, STOCK_ID) ); - CREATE TABLE IF NOT EXISTS `STOCK_ORDERS` ( + CREATE TABLE IF NOT EXISTS `STOCK_TRADE_LOG` ( `ID` int(11) primary key auto_increment, `USER_ID` int(11), `STOCK_ID` int(11), `SHARES` float, - `PRICE` float, - ); + ) */ From 68fc7ad263ad5822872a65f3ca6d9e8e8decc647 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Sun, 28 Oct 2018 22:34:48 +1100 Subject: [PATCH 05/14] add mining stock --- gamemodes/irresistible/cnr/features/_features.pwn | 2 +- gamemodes/irresistible/cnr/features/minijobs/mining.pwn | 1 + gamemodes/irresistible/cnr/features/stocks/stocks.pwn | 9 +++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/_features.pwn b/gamemodes/irresistible/cnr/features/_features.pwn index 350cb81..1b8599f 100644 --- a/gamemodes/irresistible/cnr/features/_features.pwn +++ b/gamemodes/irresistible/cnr/features/_features.pwn @@ -12,10 +12,10 @@ #include "irresistible\cnr\features\vehicles\_vehicles.pwn" #include "irresistible\cnr\features\business\_business.pwn" #include "irresistible\cnr\features\gangs\_gangs.pwn" +#include "irresistible\cnr\features\stocks\_stocks.pwn" #include "irresistible\cnr\features\robbery\_robbery.pwn" #include "irresistible\cnr\features\visage\_visage.pwn" #include "irresistible\cnr\features\minijobs\_minijobs.pwn" -#include "irresistible\cnr\features\stocks\_stocks.pwn" // other #include "irresistible\cnr\features\shop.pwn" diff --git a/gamemodes/irresistible/cnr/features/minijobs/mining.pwn b/gamemodes/irresistible/cnr/features/minijobs/mining.pwn index 9d78fd4..887e9f5 100644 --- a/gamemodes/irresistible/cnr/features/minijobs/mining.pwn +++ b/gamemodes/irresistible/cnr/features/minijobs/mining.pwn @@ -378,6 +378,7 @@ hook OnPlayerEnterDynRaceCP( playerid, checkpointid ) DestroyDynamicRaceCP( p_MiningExport[ playerid ] ); p_MiningExport[ playerid ] = 0xFFFF; GivePlayerCash( playerid, cashEarned ); + StockMarket_UpdateEarnings( E_STOCK_MINING_COMPANY, cashEarned ); GivePlayerScore( playerid, floatround( oresExported / 2 ) ); // 16 score is a bit too much for ore... so half that = 8 //GivePlayerExperience( playerid, E_MINING, float( oresExported ) * 0.2 ); SendServerMessage( playerid, "You have exported %d rock ore(s) to an industry, earning you "COL_GOLD"%s"COL_WHITE".", oresExported, cash_format( cashEarned ) ); diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index 31211cb..409dbf5 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -11,7 +11,7 @@ /* ** Definitions ** */ #define MAX_STOCKS ( 32 ) -#define STOCK_REPORTING_PERIOD ( 500 ) // 1 day +#define STOCK_REPORTING_PERIOD ( 600 ) // 1 day #define STOCK_REPORTING_PERIODS ( 30 ) // last 30 periods (days) @@ -35,6 +35,11 @@ enum E_STOCK_MARKET_PRICE_DATA Float: E_EARNINGS }; +enum +{ + E_STOCK_MINING_COMPANY +}; + static stock g_stockMarketData [ MAX_STOCKS ] [ E_STOCK_MARKET_DATA ], g_stockMarketPriceData [ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ], @@ -378,7 +383,7 @@ stock StockMarket_UpdateEarnings( stockid, amount ) return 0; printf( "Current Earnings: %f, Prior Earnings: %f", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ] ); - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] += amount; + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] += float( amount ); mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `CURRENT_EARNINGS` = `CURRENT_EARNINGS` + %d WHERE `ID` = %d", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); return 1; } From c616c9435985b4c2a34157e732eb244a36a76834 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Wed, 31 Oct 2018 09:42:10 +1100 Subject: [PATCH 06/14] dividend payout implementation --- .../cnr/features/stocks/stocks.old.pwn | 433 ++++++++++++++++++ .../cnr/features/stocks/stocks.pwn | 181 ++++---- 2 files changed, 533 insertions(+), 81 deletions(-) create mode 100644 gamemodes/irresistible/cnr/features/stocks/stocks.old.pwn diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.old.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.old.pwn new file mode 100644 index 0000000..409dbf5 --- /dev/null +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.old.pwn @@ -0,0 +1,433 @@ +/* + * Irresistible Gaming (c) 2018 + * Developed by Lorenc Pekaj + * Module: cnrs + * Purpose: + */ + +/* ** Includes ** */ +#include < YSI\y_hooks > + +/* ** Definitions ** */ +#define MAX_STOCKS ( 32 ) + +#define STOCK_REPORTING_PERIOD ( 600 ) // 1 day + +#define STOCK_REPORTING_PERIODS ( 30 ) // last 30 periods (days) + +#define DIALOG_STOCK_MARKET 8923 +#define DIALOG_STOCK_MARKET_BUY 8925 + +#define STOCK_MM_USER_ID ( 0 ) + +/* ** Variables ** */ +enum E_STOCK_MARKET_DATA +{ + E_NAME[ 64 ], E_SYMBOL[ 4 ], E_MAX_SHARES, + + // market maker + Float: E_IPO_SHARES, Float: E_IPO_PRICE +}; + +enum E_STOCK_MARKET_PRICE_DATA +{ + E_SQL_ID, Float: E_PRICE, + Float: E_EARNINGS +}; + +enum +{ + E_STOCK_MINING_COMPANY +}; + +static stock + g_stockMarketData [ MAX_STOCKS ] [ E_STOCK_MARKET_DATA ], + g_stockMarketPriceData [ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ], + Iterator: stockmarkets < MAX_STOCKS > +; + +/* ** Forwards / Getters ** */ +stock Float: StockMarket_GetCurrentPrice( stockid ) { + return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ]; +} + +/* ** Hooks ** */ +hook OnScriptInit( ) +{ + // server variables + AddServerVariable( "stock_report_time", "0", GLOBAL_VARTYPE_INT ); + + // create markets + CreateStockMarket( 0, "The Mining Company", "MC", 1000000, 100 ); + return 1; +} + +hook OnServerUpdate( ) +{ + new current_time = GetServerTime( ); + new last_reporting = GetServerVariableInt( "stock_report_time" ); + + // check if its reporting time + if ( current_time > last_reporting ) + { + // reporting period + UpdateServerVariableInt( "stock_report_time", current_time + STOCK_REPORTING_PERIOD ); + + // create a new reporting period for every stock there + foreach ( new s : stockmarkets ) + { + StockMarket_CreateReport( s ); + } + + print( "Successfully created new reporting period for all online companies" ); + } + return 1; +} + +hook OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] ) +{ + if ( dialogid == DIALOG_STOCK_MARKET && response ) + { + new + x = 0; + + foreach ( new s : stockmarkets ) + { + if ( x == listitem ) + { + SetPVarInt( playerid, "stockmarket_selection", s ); + StockMarket_ShowBuySlip( playerid, s ); + } + x ++; + } + return 1; + } + else if ( dialogid == DIALOG_STOCK_MARKET_BUY ) + { + new + stockid = GetPVarInt( playerid, "stockmarket_selection" ); + + if ( response ) + { + new + Float: shares; + + if ( sscanf( inputtext, "f", shares ) ) SendError( playerid, "You must use a valid value." ); + else if ( shares <= 10.0 ) SendError( playerid, "The minimum number of shares you can buy is 10." ); + else + { + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `STOCK_ID` = %d AND `USER_ID` = %d", stockid, STOCK_MM_USER_ID ), "StockMarket_OnPurchaseOrder", "ddf", playerid, stockid, shares ); + return 1; + } + return StockMarket_ShowBuySlip( playerid, stockid ); + } + else + { + return cmd_stockmarkets( playerid, "" ); + } + } + return 1; +} + +/* ** SQL Thread ** */ +thread Stock_UpdateReportingPeriods( stockid ) +{ + new + rows = cache_get_row_count( ); + + if ( rows ) + { + for ( new row = 0; row < rows; row ++ ) + { + g_stockMarketPriceData[ stockid ] [ row ] [ E_SQL_ID ] = cache_get_field_content_int( row, "ID" ); + g_stockMarketPriceData[ stockid ] [ row ] [ E_PRICE ] = cache_get_field_content_float( row, "CURRENT_PRICE" ); + g_stockMarketPriceData[ stockid ] [ row ] [ E_EARNINGS ] = cache_get_field_content_float( row, "CURRENT_EARNINGS" ); + } + } + else // no historical reporting data, restock the market maker + { + // set current stock market prices to IPO + g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ] = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; + + // create report for the company using the IPO price + StockMarket_CreateReport( stockid ); + + // give market maker shares + StockMarket_GiveShares( stockid, STOCK_MM_USER_ID, g_stockMarketData[ stockid ] [ E_IPO_SHARES ] ); // , g_stockMarketData[ stockid ] [ E_IPO_PRICE ] + } + return 1; +} + +thread StockMarket_InsertReport( stockid ) +{ + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_SQL_ID ] = cache_insert_id( ); + return 1; +} + +thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) +{ + new + rows = cache_get_row_count( ); + + if ( ! rows ) { + return SendError( playerid, "This stock has no available shares for sale." ); + } + + // check if the player has the money for the purchase + new Float: ask_price = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ]; + new purchase_cost = floatround( ask_price * shares ); + + if ( GetPlayerCash( playerid ) < purchase_cost ) { + return SendError( playerid, "You need at least %s to purchase this many shares.", cash_format( purchase_cost ) ), StockMarket_ShowBuySlip( playerid, stockid ), 1; + } + + // check if quantity is valid + new Float: available_quantity = cache_get_field_content_float( 0, "SHARES" ); + + if ( shares > available_quantity ) { + return SendError( playerid, "There are not that many shares available for sale." ), StockMarket_ShowBuySlip( playerid, stockid ), 1; + } + + // reduce the market makers shares + StockMarket_GiveShares( stockid, STOCK_MM_USER_ID, -shares ); + + // increment the players shares + StockMarket_GiveShares( stockid, GetPlayerAccountID( playerid ), shares ); + + // reduce player balance and alert + GivePlayerCash( playerid, -purchase_cost ); + SendServerMessage( playerid, "You have successfully purchased %s shares of %s (@ %s/ea) for %s.", number_format( shares, .decimals = 3 ), g_stockMarketData[ stockid ] [ E_NAME ], cash_format( ask_price, .decimals = 2 ), cash_format( purchase_cost ) ); + return 1; +} + +thread StockMarket_OnShowBuySlip( playerid, stockid ) +{ + new + rows = cache_get_row_count( ); + + if ( ! rows ) { + return SendError( playerid, "This stock does now have any shares available to buy." ); + } + + new + Float: available_quantity = cache_get_field_content_float( 0, "SHARES" ); + + format( + szBigString, sizeof ( szBigString ), + ""COL_WHITE"You can buy shares of %s for "COL_GREEN"%s"COL_WHITE" each.\n\nThere are %s available shares to buy.", + g_stockMarketData[ stockid ] [ E_NAME ], + cash_format( g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ), + number_format( available_quantity, .decimals = 3 ) + ); + ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET_BUY, DIALOG_STYLE_INPUT, ""COL_WHITE"Stock Market", szBigString, "Buy", "Close" ); + return 1; +} + +thread StockMarket_OnShowShares( playerid ) +{ + new + rows = cache_get_row_count( ); + + if ( ! rows ) { + return SendError( playerid, "This stock does now have any shares available to buy." ); + } + + szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Total Shares\t"COL_WHITE"Current Price ($)\t"COL_GREEN"Value ($)\n"; + + for ( new row = 0; row < rows; row ++ ) + { + new + stockid = cache_get_field_content_int( row, "ID" ); + + if ( Iter_Contains( stockmarkets, stockid ) ) + { + new Float: current_price = StockMarket_GetCurrentPrice( stockid ); + new Float: shares = cache_get_field_content_float( row, "SHARES" ); + + format( + szLargeString, sizeof( szLargeString ), + "%s%s (%s)\t%s\t%s\t"COL_GREEN"%s\n", + szLargeString, + g_stockMarketData[ stockid ] [ E_NAME ], + g_stockMarketData[ stockid ] [ E_SYMBOL ], + number_format( shares, .decimals = 3 ), + cash_format( current_price, .decimals = 2 ), + cash_format( floatround( shares * current_price ) ) + ); + } + } + return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Sell", "Close" ), 1; +} + +/* ** Command ** */ +CMD:increase( playerid, params[ ] ) { + StockMarket_UpdateEarnings( 0, strval( params ) ); + return 1; +} + +CMD:newreport( playerid, params[ ] ) { + StockMarket_CreateReport( 0 ); + return 1; +} + +CMD:stocks( playerid, params[ ] ) return cmd_stockmarkets( playerid, params ); +CMD:stockmarkets( playerid, params[ ] ) +{ + szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Current Price\t"COL_WHITE"Price Change (24H)\n"; + + foreach ( new s : stockmarkets ) + { + printf("%f %f\n", g_stockMarketPriceData[ s ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ s ] [ 1 ] [ E_EARNINGS ]); + new Float: price_difference = ( g_stockMarketPriceData[ s ] [ 1 ] [ E_EARNINGS ] / g_stockMarketPriceData[ s ] [ 2 ] [ E_EARNINGS ] - 1.0 ) * 100.0; + new Float: current_price = g_stockMarketPriceData[ s ] [ 1 ] [ E_PRICE ]; + + format( + szLargeString, sizeof( szLargeString ), + "%s%s (%s)\t"COL_GREEN"%s\t%s%s%%\n", + szLargeString, + g_stockMarketData[ s ] [ E_NAME ], + g_stockMarketData[ s ] [ E_SYMBOL ], + cash_format( current_price, .decimals = 0 ), + price_difference >= 0.0 ? ( COL_GREEN ) : ( COL_RED ), + number_format( price_difference, .decimals = 2 ) + ); + } + + SendServerMessage( playerid, "The stock market will close in %s.", secondstotime( GetServerVariableInt( "stock_report_time" ) - GetServerTime( ) ) ); + return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Buy", "Close" ); +} + +CMD:shares( playerid, params[ ] ) +{ + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `USER_ID` = %d", GetPlayerAccountID( playerid ) ), "StockMarket_OnShowShares", "d", playerid ); + return 1; +} + +/* ** Functions ** */ +stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ipo_shares, Float: ipo_price ) +{ + if ( ! Iter_Contains( stockmarkets, stockid ) ) + { + strcpy( g_stockMarketData[ stockid ] [ E_NAME ], name ); + strcpy( g_stockMarketData[ stockid ] [ E_SYMBOL ], symbol ); + + g_stockMarketData[ stockid ] [ E_IPO_SHARES ] = ipo_shares; + g_stockMarketData[ stockid ] [ E_IPO_PRICE ] = ipo_price; + + // reset stock price information + for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ); r ++ ) { + g_stockMarketPriceData[ stockid ] [ r ] [ E_PRICE ] = 0.0; + g_stockMarketPriceData[ stockid ] [ r ] [ E_EARNINGS ] = 1.0; + } + + // load price information if there is + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_REPORTS` WHERE `STOCK_ID`=%d ORDER BY `REPORTING_TIME` DESC LIMIT %d", stockid, sizeof( g_stockMarketPriceData[ ] ) ), "Stock_UpdateReportingPeriods", "d", stockid ); + + // add to iterator + Iter_Add( stockmarkets, stockid ); + } + return stockid; +} + +stock StockMarket_CreateReport( stockid ) +{ + // limit a 20% loss for players + new Float: circuit_breaker = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ] * 0.8; + + if ( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] < circuit_breaker ) { + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = circuit_breaker; + } + + // change stock price proportional to earnings increase/decrease + new Float: price_difference = g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] / g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ]; + printf("Earnings %f\n", price_difference ); + + if ( ( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ] * price_difference ) < 1.0 ) { + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = 1.0; + } + + // store temporary stock info + new temp_stock_price_data[ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ]; + temp_stock_price_data = g_stockMarketPriceData; + + // shift all earnings by one + for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ) - 2; r ++ ) { + g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_PRICE ] = temp_stock_price_data[ stockid ] [ r ] [ E_PRICE ]; + g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_EARNINGS ] = temp_stock_price_data[ stockid ] [ r ] [ E_EARNINGS ]; + } + + // set current price to previous reporting period price + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ]; + + // reset earnings + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = 1.0; // set to 1 instead of 0 to prevent errors + + // insert to database the old information + mysql_format( + dbHandle, szBigString, sizeof ( szBigString ), + "INSERT INTO `STOCK_REPORTS` (`STOCK_ID`, `CURRENT_PRICE`, `CURRENT_EARNINGS`) VALUES (%d, %f, %f)", + stockid, + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ], + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] + ); + + // todo: adjust sell orders + + mysql_tquery( dbHandle, szBigString, "StockMarket_InsertReport", "d", stockid ); + return 1; +} + +stock StockMarket_UpdateEarnings( stockid, amount ) +{ + if ( ! Iter_Contains( stockmarkets, stockid ) ) + return 0; + + printf( "Current Earnings: %f, Prior Earnings: %f", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ] ); + g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] += float( amount ); + mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `CURRENT_EARNINGS` = `CURRENT_EARNINGS` + %d WHERE `ID` = %d", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); + return 1; +} + +stock Float: StockMarket_GetEarnings( stockid ) { + return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ]; +} + +stock StockMarket_GiveShares( stockid, accountid, Float: shares ) +{ + mysql_format( + dbHandle, szBigString, sizeof ( szBigString ), + "INSERT INTO `STOCK_OWNERS` (`USER_ID`, `STOCK_ID`, `SHARES`) VALUES (%d, %d, %f) ON DUPLICATE KEY UPDATE `SHARES` = `SHARES` + %f", + accountid, stockid, shares, shares + ); + mysql_single_query( szBigString ); +} + +static stock StockMarket_ShowBuySlip( playerid, stockid ) +{ + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `STOCK_ID` = %d AND `USER_ID` = %d", stockid, STOCK_MM_USER_ID ), "StockMarket_OnShowBuySlip", "dd", playerid, stockid ); + return 1; +} + +/* + CREATE TABLE IF NOT EXISTS `STOCK_REPORTS` ( + `ID` int(11) primary key auto_increment, + `STOCK_ID` int(11), + `CURRENT_PRICE` float, + `CURRENT_EARNINGS` float, + `REPORTING_TIME` TIMESTAMP default CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS `STOCK_OWNERS` ( + `USER_ID` int(11), + `STOCK_ID` int(11), + `SHARES` float, + PRIMARY KEY (USER_ID, STOCK_ID) + ); + + CREATE TABLE IF NOT EXISTS `STOCK_TRADE_LOG` ( + `ID` int(11) primary key auto_increment, + `USER_ID` int(11), + `STOCK_ID` int(11), + `SHARES` float, + ) + */ diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index 409dbf5..5e2c356 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -23,16 +23,16 @@ /* ** Variables ** */ enum E_STOCK_MARKET_DATA { - E_NAME[ 64 ], E_SYMBOL[ 4 ], E_MAX_SHARES, + E_NAME[ 64 ], E_SYMBOL[ 4 ], Float: E_MAX_SHARES, // market maker - Float: E_IPO_SHARES, Float: E_IPO_PRICE + Float: E_IPO_SHARES, Float: E_IPO_PRICE }; enum E_STOCK_MARKET_PRICE_DATA { E_SQL_ID, Float: E_PRICE, - Float: E_EARNINGS + Float: E_POOL }; enum @@ -42,13 +42,13 @@ enum static stock g_stockMarketData [ MAX_STOCKS ] [ E_STOCK_MARKET_DATA ], - g_stockMarketPriceData [ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ], + g_stockMarketReportData [ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ], Iterator: stockmarkets < MAX_STOCKS > ; /* ** Forwards / Getters ** */ stock Float: StockMarket_GetCurrentPrice( stockid ) { - return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ]; + return g_stockMarketReportData[ stockid ] [ 0 ] [ E_PRICE ]; } /* ** Hooks ** */ @@ -76,7 +76,7 @@ hook OnServerUpdate( ) // create a new reporting period for every stock there foreach ( new s : stockmarkets ) { - StockMarket_CreateReport( s ); + StockMarket_ReleaseDividends( s ); } print( "Successfully created new reporting period for all online companies" ); @@ -139,18 +139,17 @@ thread Stock_UpdateReportingPeriods( stockid ) { for ( new row = 0; row < rows; row ++ ) { - g_stockMarketPriceData[ stockid ] [ row ] [ E_SQL_ID ] = cache_get_field_content_int( row, "ID" ); - g_stockMarketPriceData[ stockid ] [ row ] [ E_PRICE ] = cache_get_field_content_float( row, "CURRENT_PRICE" ); - g_stockMarketPriceData[ stockid ] [ row ] [ E_EARNINGS ] = cache_get_field_content_float( row, "CURRENT_EARNINGS" ); + g_stockMarketReportData[ stockid ] [ row ] [ E_SQL_ID ] = cache_get_field_content_int( row, "ID" ); + g_stockMarketReportData[ stockid ] [ row ] [ E_POOL ] = cache_get_field_content_float( row, "POOL" ); } } else // no historical reporting data, restock the market maker { // set current stock market prices to IPO - g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ] = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; + //g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ] = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; // create report for the company using the IPO price - StockMarket_CreateReport( stockid ); + //StockMarket_ReleaseDividends( stockid ); // give market maker shares StockMarket_GiveShares( stockid, STOCK_MM_USER_ID, g_stockMarketData[ stockid ] [ E_IPO_SHARES ] ); // , g_stockMarketData[ stockid ] [ E_IPO_PRICE ] @@ -160,7 +159,7 @@ thread Stock_UpdateReportingPeriods( stockid ) thread StockMarket_InsertReport( stockid ) { - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_SQL_ID ] = cache_insert_id( ); + g_stockMarketReportData[ stockid ] [ 0 ] [ E_SQL_ID ] = cache_insert_id( ); return 1; } @@ -174,7 +173,7 @@ thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) } // check if the player has the money for the purchase - new Float: ask_price = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ]; + new Float: ask_price = g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ]; new purchase_cost = floatround( ask_price * shares ); if ( GetPlayerCash( playerid ) < purchase_cost ) { @@ -216,7 +215,7 @@ thread StockMarket_OnShowBuySlip( playerid, stockid ) szBigString, sizeof ( szBigString ), ""COL_WHITE"You can buy shares of %s for "COL_GREEN"%s"COL_WHITE" each.\n\nThere are %s available shares to buy.", g_stockMarketData[ stockid ] [ E_NAME ], - cash_format( g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ), + cash_format( g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ), number_format( available_quantity, .decimals = 3 ) ); ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET_BUY, DIALOG_STYLE_INPUT, ""COL_WHITE"Stock Market", szBigString, "Buy", "Close" ); @@ -259,6 +258,72 @@ thread StockMarket_OnShowShares( playerid ) return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Sell", "Close" ), 1; } +thread Stock_OnDividendPayout( stockid ) +{ + new + rows = cache_get_row_count( ); + + // pay out existing shareholders + if ( rows ) + { + new + Float: total_shares = g_stockMarketData[ stockid ] [ E_MAX_SHARES ]; + + for ( new row = 0; row < rows; row ++ ) + { + new account_id = cache_get_field_content_int( row, "USER_ID" ); + new Float: shares_owned = cache_get_field_content_float( row, "SHARES" ); + + new Float: dividend_rate = shares_owned / total_shares; + new dividend_payout = floatround( g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] * dividend_rate ); + + new + shareholder; + + foreach ( shareholder : Player ) if ( GetPlayerAccountID( shareholder ) == account_id ) { + break; + } + + if ( 0 <= shareholder < MAX_PLAYERS && Iter_Contains( Player, shareholder ) ) { + GivePlayerBankMoney( shareholder, dividend_payout ), Beep( shareholder ); + SendServerMessage( shareholder, "A "COL_GOLD"%s"COL_WHITE" dividend (%0.2f%s) has been paid out for owning %s!", cash_format( dividend_payout ), dividend_rate * 100.0, "%%", g_stockMarketData[ stockid ] [ E_NAME ] ); + } else { + mysql_single_query( sprintf( "UPDATE `USERS` SET `BANKMONEY` = `BANKMONEY` + %d WHERE `ID` = %d", dividend_payout, account_id ) ); + } + } + } + + // store temporary stock info + new temp_stock_price_data[ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ]; + temp_stock_price_data = g_stockMarketReportData; + + // shift all earnings by one + for ( new r = 0; r < sizeof( g_stockMarketReportData[ ] ) - 2; r ++ ) { + g_stockMarketReportData[ stockid ] [ r + 1 ] [ E_POOL ] = temp_stock_price_data[ stockid ] [ r ] [ E_POOL ]; + } + + // reset earnings + g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] = 1.0; // set to 1 instead of 0 to prevent errors + + // insert to database the old information + mysql_format( dbHandle, szBigString, sizeof ( szBigString ), "INSERT INTO `STOCK_REPORTS` (`STOCK_ID`, `POOL`) VALUES (%d, %f)", stockid, g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] ); + mysql_tquery( dbHandle, szBigString, "StockMarket_InsertReport", "d", stockid ); + return 1; +} + +thread Stock_UpdateMaximumShares( stockid ) +{ + new + rows = cache_get_row_count( ); + + if ( rows ) { + g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = cache_get_field_content_float( 0, "TOTAL_SHARES" ); + } else { + g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = g_stockMarketData[ stockid ] [ E_IPO_SHARES ]; + } + return 1; +} + /* ** Command ** */ CMD:increase( playerid, params[ ] ) { StockMarket_UpdateEarnings( 0, strval( params ) ); @@ -266,34 +331,32 @@ CMD:increase( playerid, params[ ] ) { } CMD:newreport( playerid, params[ ] ) { - StockMarket_CreateReport( 0 ); + StockMarket_ReleaseDividends( 0 ); return 1; } CMD:stocks( playerid, params[ ] ) return cmd_stockmarkets( playerid, params ); CMD:stockmarkets( playerid, params[ ] ) { - szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Current Price\t"COL_WHITE"Price Change (24H)\n"; + szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Max Shares\t"COL_WHITE"Dividend Per Share ($)\n"; foreach ( new s : stockmarkets ) { - printf("%f %f\n", g_stockMarketPriceData[ s ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ s ] [ 1 ] [ E_EARNINGS ]); - new Float: price_difference = ( g_stockMarketPriceData[ s ] [ 1 ] [ E_EARNINGS ] / g_stockMarketPriceData[ s ] [ 2 ] [ E_EARNINGS ] - 1.0 ) * 100.0; - new Float: current_price = g_stockMarketPriceData[ s ] [ 1 ] [ E_PRICE ]; + new + Float: payout = g_stockMarketReportData[ s ] [ 0 ] [ E_POOL ] / g_stockMarketData[ s ] [ E_MAX_SHARES ]; format( szLargeString, sizeof( szLargeString ), - "%s%s (%s)\t"COL_GREEN"%s\t%s%s%%\n", + "%s%s (%s)\t%s\t"COL_GREEN"%s\n", szLargeString, g_stockMarketData[ s ] [ E_NAME ], g_stockMarketData[ s ] [ E_SYMBOL ], - cash_format( current_price, .decimals = 0 ), - price_difference >= 0.0 ? ( COL_GREEN ) : ( COL_RED ), - number_format( price_difference, .decimals = 2 ) + number_format( g_stockMarketData[ s ] [ E_MAX_SHARES ], .decimals = 0 ), + cash_format( payout, .decimals = 2 ) ); } - SendServerMessage( playerid, "The stock market will close in %s.", secondstotime( GetServerVariableInt( "stock_report_time" ) - GetServerTime( ) ) ); + SendServerMessage( playerid, "The stock market will payout dividends in %s.", secondstotime( GetServerVariableInt( "stock_report_time" ) - GetServerTime( ) ) ); return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Buy", "Close" ); } @@ -315,13 +378,15 @@ stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ip g_stockMarketData[ stockid ] [ E_IPO_PRICE ] = ipo_price; // reset stock price information - for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ); r ++ ) { - g_stockMarketPriceData[ stockid ] [ r ] [ E_PRICE ] = 0.0; - g_stockMarketPriceData[ stockid ] [ r ] [ E_EARNINGS ] = 1.0; + for ( new r = 0; r < sizeof( g_stockMarketReportData[ ] ); r ++ ) { + g_stockMarketReportData[ stockid ] [ r ] [ E_POOL ] = 1.0; } // load price information if there is - mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_REPORTS` WHERE `STOCK_ID`=%d ORDER BY `REPORTING_TIME` DESC LIMIT %d", stockid, sizeof( g_stockMarketPriceData[ ] ) ), "Stock_UpdateReportingPeriods", "d", stockid ); + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_REPORTS` WHERE `STOCK_ID`=%d ORDER BY `REPORTING_TIME` DESC LIMIT %d", stockid, sizeof( g_stockMarketReportData[ ] ) ), "Stock_UpdateReportingPeriods", "d", stockid ); + + // load the maximum number of shares + mysql_tquery( dbHandle, sprintf( "SELECT SUM(`SHARES`) AS TOTAL_SHARES FROM `STOCK_OWNERS` WHERE `STOCK_ID`=0", stockid ), "Stock_UpdateMaximumShares", "d", stockid ); // add to iterator Iter_Add( stockmarkets, stockid ); @@ -329,51 +394,10 @@ stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ip return stockid; } -stock StockMarket_CreateReport( stockid ) +static stock StockMarket_ReleaseDividends( stockid ) { - // limit a 20% loss for players - new Float: circuit_breaker = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ] * 0.8; - - if ( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] < circuit_breaker ) { - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = circuit_breaker; - } - - // change stock price proportional to earnings increase/decrease - new Float: price_difference = g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] / g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ]; - printf("Earnings %f\n", price_difference ); - - if ( ( g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ] * price_difference ) < 1.0 ) { - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = 1.0; - } - - // store temporary stock info - new temp_stock_price_data[ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ]; - temp_stock_price_data = g_stockMarketPriceData; - - // shift all earnings by one - for ( new r = 0; r < sizeof( g_stockMarketPriceData[ ] ) - 2; r ++ ) { - g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_PRICE ] = temp_stock_price_data[ stockid ] [ r ] [ E_PRICE ]; - g_stockMarketPriceData[ stockid ] [ r + 1 ] [ E_EARNINGS ] = temp_stock_price_data[ stockid ] [ r ] [ E_EARNINGS ]; - } - - // set current price to previous reporting period price - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketPriceData[ stockid ] [ 1 ] [ E_PRICE ]; - - // reset earnings - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] = 1.0; // set to 1 instead of 0 to prevent errors - - // insert to database the old information - mysql_format( - dbHandle, szBigString, sizeof ( szBigString ), - "INSERT INTO `STOCK_REPORTS` (`STOCK_ID`, `CURRENT_PRICE`, `CURRENT_EARNINGS`) VALUES (%d, %f, %f)", - stockid, - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_PRICE ], - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] - ); - - // todo: adjust sell orders - - mysql_tquery( dbHandle, szBigString, "StockMarket_InsertReport", "d", stockid ); + mysql_format( dbHandle, szBigString, sizeof ( szBigString ), "SELECT * as `TOTAL_SHARES` FROM `STOCK_OWNERS` WHERE `STOCK_ID`=%d", stockid, stockid ); + mysql_tquery( dbHandle, szBigString, "Stock_OnDividendPayout", "d", stockid ); return 1; } @@ -382,16 +406,12 @@ stock StockMarket_UpdateEarnings( stockid, amount ) if ( ! Iter_Contains( stockmarkets, stockid ) ) return 0; - printf( "Current Earnings: %f, Prior Earnings: %f", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 1 ] [ E_EARNINGS ] ); - g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ] += float( amount ); - mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `CURRENT_EARNINGS` = `CURRENT_EARNINGS` + %d WHERE `ID` = %d", g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ], g_stockMarketPriceData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); + printf( "Current Pool: %f, Prior Pool: %f", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 1 ] [ E_POOL ] ); + g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] += float( amount ); + mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `POOL` = `POOL` + %d WHERE `ID` = %d", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); return 1; } -stock Float: StockMarket_GetEarnings( stockid ) { - return g_stockMarketPriceData[ stockid ] [ 0 ] [ E_EARNINGS ]; -} - stock StockMarket_GiveShares( stockid, accountid, Float: shares ) { mysql_format( @@ -412,8 +432,7 @@ static stock StockMarket_ShowBuySlip( playerid, stockid ) CREATE TABLE IF NOT EXISTS `STOCK_REPORTS` ( `ID` int(11) primary key auto_increment, `STOCK_ID` int(11), - `CURRENT_PRICE` float, - `CURRENT_EARNINGS` float, + `POOL` float, `REPORTING_TIME` TIMESTAMP default CURRENT_TIMESTAMP ); From ae01993db4c8340f36ba3827cc34769f2e9cd9e4 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Wed, 31 Oct 2018 18:19:18 +1100 Subject: [PATCH 07/14] - add sell feature - add basic pricing mechanism --- .../cnr/features/stocks/stocks.pwn | 270 +++++++++++++++--- 1 file changed, 229 insertions(+), 41 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index 5e2c356..a68410d 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -16,23 +16,24 @@ #define STOCK_REPORTING_PERIODS ( 30 ) // last 30 periods (days) #define DIALOG_STOCK_MARKET 8923 +#define DIALOG_PLAYER_STOCKS 8924 #define DIALOG_STOCK_MARKET_BUY 8925 +#define DIALOG_STOCK_MARKET_SELL 8926 #define STOCK_MM_USER_ID ( 0 ) /* ** Variables ** */ enum E_STOCK_MARKET_DATA { - E_NAME[ 64 ], E_SYMBOL[ 4 ], Float: E_MAX_SHARES, + E_NAME[ 64 ], E_SYMBOL[ 4 ], Float: E_MAX_SHARES, // market maker - Float: E_IPO_SHARES, Float: E_IPO_PRICE + Float: E_IPO_SHARES, Float: E_IPO_PRICE, Float: E_MAX_PRICE }; enum E_STOCK_MARKET_PRICE_DATA { - E_SQL_ID, Float: E_PRICE, - Float: E_POOL + E_SQL_ID, Float: E_PRICE, Float: E_POOL }; enum @@ -40,10 +41,15 @@ enum E_STOCK_MINING_COMPANY }; +static const Float: STOCK_MARKET_POOL_FACTOR = 100000.0; // for every STOCK_MARKET_POOL_FACTOR ... STOCK_MARKET_PRICE_FACTOR added to the price +static const Float: STOCK_MARKET_PRICE_FACTOR = 5.0; + static stock g_stockMarketData [ MAX_STOCKS ] [ E_STOCK_MARKET_DATA ], g_stockMarketReportData [ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ], - Iterator: stockmarkets < MAX_STOCKS > + Iterator: stockmarkets < MAX_STOCKS >, + + Float: p_PlayerShares [ MAX_PLAYERS ] [ MAX_STOCKS ] ; /* ** Forwards / Getters ** */ @@ -58,7 +64,7 @@ hook OnScriptInit( ) AddServerVariable( "stock_report_time", "0", GLOBAL_VARTYPE_INT ); // create markets - CreateStockMarket( 0, "The Mining Company", "MC", 1000000, 100 ); + CreateStockMarket( 0, "The Mining Company", "MC", 100000.0, 25.0, 250.0 ); // 25m mcap max return 1; } @@ -97,11 +103,64 @@ hook OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] ) { SetPVarInt( playerid, "stockmarket_selection", s ); StockMarket_ShowBuySlip( playerid, s ); + break; } x ++; } return 1; } + else if ( dialogid == DIALOG_PLAYER_STOCKS && response ) + { + new + x = 0; + + foreach ( new stockid : stockmarkets ) if ( p_PlayerShares[ playerid ] [ stockid ] ) + { + if ( x == listitem ) + { + SetPVarInt( playerid, "stockmarket_selling_stock", stockid ); + StockMarket_ShowSellSlip( playerid, stockid ); + break; + } + x ++; + } + return 1; + } + else if ( dialogid == DIALOG_STOCK_MARKET_SELL ) + { + if ( ! response ) { + return cmd_shares( playerid, "" ), 1; + } + + new + stockid = GetPVarInt( playerid, "stockmarket_selling_stock" ); + + if ( ! Iter_Contains( stockmarkets, stockid ) ) { + return SendError( playerid, "There was an error processing your sell order, please try again." ); + } + + new + input_shares; + + if ( sscanf( inputtext, "d", input_shares ) ) SendError( playerid, "You must use a valid value." ); + else if ( input_shares > floatround( p_PlayerShares[ playerid ] [ stockid ], floatround_floor ) ) SendError( playerid, "You do not have this many shares available to sell." ); + else if ( input_shares <= 1 ) SendError( playerid, "The minimum number of shares you can sell is 1." ); + else + { + new + Float: shares = float( input_shares ); + + if ( ( p_PlayerShares[ playerid ] [ stockid ] -= shares ) < 0.1 ) { + mysql_single_query( sprintf( "DELETE FROM `STOCK_OWNERS` WHERE `USER_ID`=%d AND `STOCK_ID`=%d", GetPlayerAccountID( playerid ), stockid ) ); + } else { + StockMarket_GiveShares( stockid, GetPlayerAccountID( playerid ), -shares ); + } + StockMarket_UpdateSellOrder( stockid, GetPlayerAccountID( playerid ), shares ); + SendServerMessage( playerid, "You have placed a sell order for %s shares at %s each. "COL_ORANGE"To cancel your sell order, /shares cancel", number_format( shares, .decimals = 3 ), cash_format( g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ) ); + return 1; + } + return StockMarket_ShowSellSlip( playerid, stockid ); + } else if ( dialogid == DIALOG_STOCK_MARKET_BUY ) { new @@ -110,13 +169,13 @@ hook OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] ) if ( response ) { new - Float: shares; + shares; - if ( sscanf( inputtext, "f", shares ) ) SendError( playerid, "You must use a valid value." ); - else if ( shares <= 10.0 ) SendError( playerid, "The minimum number of shares you can buy is 10." ); + if ( sscanf( inputtext, "d", shares ) ) SendError( playerid, "You must use a valid value." ); + else if ( shares < 10 ) SendError( playerid, "The minimum number of shares you can buy is 10." ); else { - mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `STOCK_ID` = %d AND `USER_ID` = %d", stockid, STOCK_MM_USER_ID ), "StockMarket_OnPurchaseOrder", "ddf", playerid, stockid, shares ); + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_SELL_ORDERS` WHERE `STOCK_ID`=%d ORDER BY `LIST_DATE` ASC", stockid ), "StockMarket_OnPurchaseOrder", "ddf", playerid, stockid, float( shares ) ); return 1; } return StockMarket_ShowBuySlip( playerid, stockid ); @@ -141,18 +200,19 @@ thread Stock_UpdateReportingPeriods( stockid ) { g_stockMarketReportData[ stockid ] [ row ] [ E_SQL_ID ] = cache_get_field_content_int( row, "ID" ); g_stockMarketReportData[ stockid ] [ row ] [ E_POOL ] = cache_get_field_content_float( row, "POOL" ); + g_stockMarketReportData[ stockid ] [ row ] [ E_PRICE ] = cache_get_field_content_float( row, "PRICE" ); } } else // no historical reporting data, restock the market maker { // set current stock market prices to IPO - //g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ] = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; + g_stockMarketReportData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; // create report for the company using the IPO price - //StockMarket_ReleaseDividends( stockid ); + StockMarket_ReleaseDividends( stockid ); - // give market maker shares - StockMarket_GiveShares( stockid, STOCK_MM_USER_ID, g_stockMarketData[ stockid ] [ E_IPO_SHARES ] ); // , g_stockMarketData[ stockid ] [ E_IPO_PRICE ] + // put market maker shares on the market + StockMarket_UpdateSellOrder( stockid, STOCK_MM_USER_ID, g_stockMarketData[ stockid ] [ E_IPO_SHARES ] ); } return 1; } @@ -172,6 +232,18 @@ thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) return SendError( playerid, "This stock has no available shares for sale." ); } + // check if quantity is valid + new + Float: available_quantity = 0.0; + + for ( new r = 0; r < rows; r ++ ) { + available_quantity += cache_get_field_content_float( r, "SHARES" ); + } + + if ( shares > available_quantity ) { + return SendError( playerid, "There are not that many shares available for sale." ), StockMarket_ShowBuySlip( playerid, stockid ), 1; + } + // check if the player has the money for the purchase new Float: ask_price = g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ]; new purchase_cost = floatround( ask_price * shares ); @@ -180,16 +252,51 @@ thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) return SendError( playerid, "You need at least %s to purchase this many shares.", cash_format( purchase_cost ) ), StockMarket_ShowBuySlip( playerid, stockid ), 1; } - // check if quantity is valid - new Float: available_quantity = cache_get_field_content_float( 0, "SHARES" ); + new + Float: amount_remaining = shares; - if ( shares > available_quantity ) { - return SendError( playerid, "There are not that many shares available for sale." ), StockMarket_ShowBuySlip( playerid, stockid ), 1; + for ( new row = 0; row < rows; row ++ ) + { + new sell_order_user_id = cache_get_field_content_int( row, "USER_ID" ); + new Float: sell_order_shares = cache_get_field_content_float( row, "SHARES" ); + + // check if seller is online + new + sellerid; + + foreach ( sellerid : Player ) if ( GetPlayerAccountID( sellerid ) == sell_order_user_id ) { + break; + } + + new Float: sold_shares = amount_remaining > sell_order_shares ? sell_order_shares : amount_remaining; + new sold_amount = floatround( sold_shares * ask_price ); + + if ( 0 <= sellerid < MAX_PLAYERS && Iter_Contains( Player, sellerid ) ) { + GivePlayerBankMoney( sellerid, sold_amount ), Beep( sellerid ); + SendServerMessage( sellerid, "You have sold %s %s shares to %s(%d) for "COL_GOLD"%s"COL_WHITE"!", number_format( sold_shares, .decimals = 3 ), g_stockMarketData[ stockid ] [ E_NAME ], ReturnPlayerName( playerid ), playerid, cash_format( sold_amount ) ); + } else { + mysql_single_query( sprintf( "UPDATE `USERS` SET `BANKMONEY` = `BANKMONEY` + %d WHERE `ID` = %d", sold_amount, sell_order_user_id ) ); + } + + // remove the sell order if there is little to no shares available + if ( sell_order_shares - amount_remaining < 1.0 ) + { + // get rid of this sell order + mysql_single_query( sprintf( "DELETE FROM `STOCK_SELL_ORDERS` WHERE `USER_ID`=%d and `STOCK_ID`=%d", sell_order_user_id, stockid ) ); + + // deduct the sell order amount from amount remaining + amount_remaining -= sell_order_shares; + } + else + { + // reduce sell order quantity + StockMarket_UpdateSellOrder( stockid, sell_order_user_id, -amount_remaining ); + + // the player's buy order was filled in the single sell order ... prevent updating + break; + } } - // reduce the market makers shares - StockMarket_GiveShares( stockid, STOCK_MM_USER_ID, -shares ); - // increment the players shares StockMarket_GiveShares( stockid, GetPlayerAccountID( playerid ), shares ); @@ -205,11 +312,11 @@ thread StockMarket_OnShowBuySlip( playerid, stockid ) rows = cache_get_row_count( ); if ( ! rows ) { - return SendError( playerid, "This stock does now have any shares available to buy." ); + return SendError( playerid, "This stock does not currently have any shares available to buy." ); } new - Float: available_quantity = cache_get_field_content_float( 0, "SHARES" ); + Float: available_quantity = cache_get_field_content_float( 0, "SALE_SHARES" ); format( szBigString, sizeof ( szBigString ), @@ -228,7 +335,7 @@ thread StockMarket_OnShowShares( playerid ) rows = cache_get_row_count( ); if ( ! rows ) { - return SendError( playerid, "This stock does now have any shares available to buy." ); + return SendError( playerid, "You are not holding any shares of any company." ); } szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Total Shares\t"COL_WHITE"Current Price ($)\t"COL_GREEN"Value ($)\n"; @@ -243,6 +350,7 @@ thread StockMarket_OnShowShares( playerid ) new Float: current_price = StockMarket_GetCurrentPrice( stockid ); new Float: shares = cache_get_field_content_float( row, "SHARES" ); + format( szLargeString, sizeof( szLargeString ), "%s%s (%s)\t%s\t%s\t"COL_GREEN"%s\n", @@ -253,9 +361,12 @@ thread StockMarket_OnShowShares( playerid ) cash_format( current_price, .decimals = 2 ), cash_format( floatround( shares * current_price ) ) ); + + // store player stocks in a variable for reference + p_PlayerShares[ playerid ] [ stockid ] = shares; } } - return ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Sell", "Close" ), 1; + return ShowPlayerDialog( playerid, DIALOG_PLAYER_STOCKS, DIALOG_STYLE_TABLIST_HEADERS, ""COL_WHITE"Stock Market", szLargeString, "Sell", "Close" ), 1; } thread Stock_OnDividendPayout( stockid ) @@ -293,20 +404,35 @@ thread Stock_OnDividendPayout( stockid ) } } + // set the new price of the company + new // TODO: use parabola for factor difficulty? + Float: new_price = ( g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] / STOCK_MARKET_POOL_FACTOR) * STOCK_MARKET_PRICE_FACTOR + g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; + + if ( new_price > g_stockMarketData[ stockid ] [ E_MAX_PRICE ] ) { // dont want wild market caps + new_price = g_stockMarketData[ stockid ] [ E_MAX_PRICE ]; + } + else if ( new_price < g_stockMarketData[ stockid ] [ E_IPO_PRICE ] ) { // force a minimum of IPO price + new_price = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; + } + + g_stockMarketReportData[ stockid ] [ 0 ] [ E_PRICE ] = new_price; + // store temporary stock info new temp_stock_price_data[ MAX_STOCKS ] [ STOCK_REPORTING_PERIODS ] [ E_STOCK_MARKET_PRICE_DATA ]; temp_stock_price_data = g_stockMarketReportData; - // shift all earnings by one + // shift all report data by one for ( new r = 0; r < sizeof( g_stockMarketReportData[ ] ) - 2; r ++ ) { g_stockMarketReportData[ stockid ] [ r + 1 ] [ E_POOL ] = temp_stock_price_data[ stockid ] [ r ] [ E_POOL ]; + g_stockMarketReportData[ stockid ] [ r + 1 ] [ E_PRICE ] = temp_stock_price_data[ stockid ] [ r ] [ E_PRICE ]; } // reset earnings - g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] = 1.0; // set to 1 instead of 0 to prevent errors + g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] = 0.0; // set to 1 instead of 0 to prevent errors + g_stockMarketReportData[ stockid ] [ 0 ] [ E_PRICE ] = 0.0; // set to 0, it will be determined next day // insert to database the old information - mysql_format( dbHandle, szBigString, sizeof ( szBigString ), "INSERT INTO `STOCK_REPORTS` (`STOCK_ID`, `POOL`) VALUES (%d, %f)", stockid, g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] ); + mysql_format( dbHandle, szBigString, sizeof ( szBigString ), "INSERT INTO `STOCK_REPORTS` (`STOCK_ID`, `POOL`, `PRICE`) VALUES (%d, %f, %f)", stockid, g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ] ); mysql_tquery( dbHandle, szBigString, "StockMarket_InsertReport", "d", stockid ); return 1; } @@ -317,13 +443,40 @@ thread Stock_UpdateMaximumShares( stockid ) rows = cache_get_row_count( ); if ( rows ) { - g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = cache_get_field_content_float( 0, "TOTAL_SHARES" ); + g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = cache_get_field_content_float( 0, "SHARES_OWNED" ) + cache_get_field_content_float( 0, "SHARES_HELD" ); + printf("%f shares", g_stockMarketData[ stockid ] [ E_MAX_SHARES ]); } else { g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = g_stockMarketData[ stockid ] [ E_IPO_SHARES ]; } return 1; } +thread StockMarket_OnCancelOrder( playerid ) +{ + new + rows = cache_get_row_count( ); + + if ( rows ) + { + new + player_account = GetPlayerAccountID( playerid ); + + for ( new row = 0; row < rows; row ++ ) + { + new stockid = cache_get_field_content_int( row, "STOCK_ID" ); + new Float: shares = cache_get_field_content_float( row, "SHARES" ); + + mysql_single_query( sprintf( "DELETE FROM `STOCK_SELL_ORDERS` WHERE `STOCK_ID`=%d AND `USER_ID`=%d", stockid, player_account ) ); + StockMarket_GiveShares( stockid, player_account, shares ); + } + return 1; + } + else + { + return SendError( playerid, "You have no stock market sell orders to cancel." ), 1; + } +} + /* ** Command ** */ CMD:increase( playerid, params[ ] ) { StockMarket_UpdateEarnings( 0, strval( params ) ); @@ -338,7 +491,7 @@ CMD:newreport( playerid, params[ ] ) { CMD:stocks( playerid, params[ ] ) return cmd_stockmarkets( playerid, params ); CMD:stockmarkets( playerid, params[ ] ) { - szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Max Shares\t"COL_WHITE"Dividend Per Share ($)\n"; + szLargeString = ""COL_WHITE"Stock\t"COL_WHITE"Max Shares\t"COL_WHITE"Dividend Per Share ($)\t"COL_WHITE"Price ($)\n"; foreach ( new s : stockmarkets ) { @@ -347,12 +500,13 @@ CMD:stockmarkets( playerid, params[ ] ) format( szLargeString, sizeof( szLargeString ), - "%s%s (%s)\t%s\t"COL_GREEN"%s\n", + "%s%s (%s)\t%s\t"COL_GREEN"%s\t"COL_GREEN"%s\n", szLargeString, g_stockMarketData[ s ] [ E_NAME ], g_stockMarketData[ s ] [ E_SYMBOL ], number_format( g_stockMarketData[ s ] [ E_MAX_SHARES ], .decimals = 0 ), - cash_format( payout, .decimals = 2 ) + cash_format( payout, .decimals = 2 ), + cash_format( g_stockMarketReportData[ s ] [ 1 ] [ E_PRICE ], .decimals = 2 ) ); } @@ -362,12 +516,17 @@ CMD:stockmarkets( playerid, params[ ] ) CMD:shares( playerid, params[ ] ) { + if ( strmatch( params, "cancel" ) ) { + // todo: work with dialogs + mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_SELL_ORDERS` WHERE `USER_ID` = %d", GetPlayerAccountID( playerid ) ), "StockMarket_OnCancelOrder", "d", playerid ); + return 1; + } mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `USER_ID` = %d", GetPlayerAccountID( playerid ) ), "StockMarket_OnShowShares", "d", playerid ); return 1; } /* ** Functions ** */ -stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ipo_shares, Float: ipo_price ) +stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ipo_shares, Float: ipo_price, Float: max_price ) { if ( ! Iter_Contains( stockmarkets, stockid ) ) { @@ -376,17 +535,18 @@ stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ip g_stockMarketData[ stockid ] [ E_IPO_SHARES ] = ipo_shares; g_stockMarketData[ stockid ] [ E_IPO_PRICE ] = ipo_price; + g_stockMarketData[ stockid ] [ E_MAX_PRICE ] = max_price; // reset stock price information for ( new r = 0; r < sizeof( g_stockMarketReportData[ ] ); r ++ ) { - g_stockMarketReportData[ stockid ] [ r ] [ E_POOL ] = 1.0; + g_stockMarketReportData[ stockid ] [ r ] [ E_POOL ] = 0.0; } // load price information if there is mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_REPORTS` WHERE `STOCK_ID`=%d ORDER BY `REPORTING_TIME` DESC LIMIT %d", stockid, sizeof( g_stockMarketReportData[ ] ) ), "Stock_UpdateReportingPeriods", "d", stockid ); // load the maximum number of shares - mysql_tquery( dbHandle, sprintf( "SELECT SUM(`SHARES`) AS TOTAL_SHARES FROM `STOCK_OWNERS` WHERE `STOCK_ID`=0", stockid ), "Stock_UpdateMaximumShares", "d", stockid ); + mysql_tquery( dbHandle, sprintf( "SELECT (SELECT SUM(`SHARES`) FROM `STOCK_OWNERS` WHERE `STOCK_ID`=%d) AS `SHARES_OWNED`, (SELECT SUM(`SHARES`) FROM `STOCK_SELL_ORDERS` WHERE `STOCK_ID`=%d) AS `SHARES_HELD`", stockid, stockid ), "Stock_UpdateMaximumShares", "d", stockid ); // add to iterator Iter_Add( stockmarkets, stockid ); @@ -396,7 +556,7 @@ stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ip static stock StockMarket_ReleaseDividends( stockid ) { - mysql_format( dbHandle, szBigString, sizeof ( szBigString ), "SELECT * as `TOTAL_SHARES` FROM `STOCK_OWNERS` WHERE `STOCK_ID`=%d", stockid, stockid ); + mysql_format( dbHandle, szBigString, sizeof ( szBigString ), "SELECT * FROM `STOCK_OWNERS` WHERE `STOCK_ID`=%d", stockid ); mysql_tquery( dbHandle, szBigString, "Stock_OnDividendPayout", "d", stockid ); return 1; } @@ -422,20 +582,46 @@ stock StockMarket_GiveShares( stockid, accountid, Float: shares ) mysql_single_query( szBigString ); } +stock StockMarket_UpdateSellOrder( stockid, accountid, Float: shares ) +{ + mysql_format( + dbHandle, szBigString, sizeof ( szBigString ), + "INSERT INTO `STOCK_SELL_ORDERS` (`STOCK_ID`, `USER_ID`, `SHARES`) VALUES (%d, %d, %f) ON DUPLICATE KEY UPDATE `SHARES` = `SHARES` + %f", + stockid, accountid, shares, shares + ); + mysql_single_query( szBigString ); +} + static stock StockMarket_ShowBuySlip( playerid, stockid ) { - mysql_tquery( dbHandle, sprintf( "SELECT * FROM `STOCK_OWNERS` WHERE `STOCK_ID` = %d AND `USER_ID` = %d", stockid, STOCK_MM_USER_ID ), "StockMarket_OnShowBuySlip", "dd", playerid, stockid ); + mysql_tquery( dbHandle, sprintf( "SELECT SUM(`SHARES`) AS `SALE_SHARES` FROM `STOCK_SELL_ORDERS` WHERE `STOCK_ID`=%d", stockid ), "StockMarket_OnShowBuySlip", "dd", playerid, stockid ); + return 1; +} + +static stock StockMarket_ShowSellSlip( playerid, stockid ) +{ + format( + szLargeString, sizeof ( szLargeString ), + ""COL_WHITE"You can sell shares of %s for "COL_GREEN"%s"COL_WHITE" each.\n\nThough, you will have to wait until a person buys them.\n\nYou have %s available shares to sell.", + g_stockMarketData[ stockid ] [ E_NAME ], + cash_format( g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ), + number_format( p_PlayerShares[ playerid ] [ stockid ], .decimals = 3 ) + ); + ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET_SELL, DIALOG_STYLE_INPUT, ""COL_WHITE"Stock Market", szLargeString, "Sell", "Close" ); return 1; } /* + DROP TABLE `STOCK_REPORTS`; CREATE TABLE IF NOT EXISTS `STOCK_REPORTS` ( `ID` int(11) primary key auto_increment, `STOCK_ID` int(11), `POOL` float, + `PRICE` float, `REPORTING_TIME` TIMESTAMP default CURRENT_TIMESTAMP ); + DROP TABLE `STOCK_OWNERS`; CREATE TABLE IF NOT EXISTS `STOCK_OWNERS` ( `USER_ID` int(11), `STOCK_ID` int(11), @@ -443,10 +629,12 @@ static stock StockMarket_ShowBuySlip( playerid, stockid ) PRIMARY KEY (USER_ID, STOCK_ID) ); - CREATE TABLE IF NOT EXISTS `STOCK_TRADE_LOG` ( - `ID` int(11) primary key auto_increment, - `USER_ID` int(11), + DROP TABLE `STOCK_SELL_ORDERS`; + CREATE TABLE IF NOT EXISTS `STOCK_SELL_ORDERS` ( `STOCK_ID` int(11), + `USER_ID` int(11), `SHARES` float, - ) + `LIST_DATE` TIMESTAMP default CURRENT_TIMESTAMP, + PRIMARY KEY (STOCK_ID, USER_ID) + ); */ From 4cb9be95fb7f7899bf6521d34406f6ae7f973db8 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Wed, 31 Oct 2018 18:34:50 +1100 Subject: [PATCH 08/14] fix some dividend pool issue --- .../cnr/features/stocks/stocks.pwn | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index a68410d..64f2b8e 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -52,11 +52,6 @@ static stock Float: p_PlayerShares [ MAX_PLAYERS ] [ MAX_STOCKS ] ; -/* ** Forwards / Getters ** */ -stock Float: StockMarket_GetCurrentPrice( stockid ) { - return g_stockMarketReportData[ stockid ] [ 0 ] [ E_PRICE ]; -} - /* ** Hooks ** */ hook OnScriptInit( ) { @@ -347,10 +342,9 @@ thread StockMarket_OnShowShares( playerid ) if ( Iter_Contains( stockmarkets, stockid ) ) { - new Float: current_price = StockMarket_GetCurrentPrice( stockid ); + new Float: current_price = g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ]; new Float: shares = cache_get_field_content_float( row, "SHARES" ); - format( szLargeString, sizeof( szLargeString ), "%s%s (%s)\t%s\t%s\t"COL_GREEN"%s\n", @@ -442,10 +436,18 @@ thread Stock_UpdateMaximumShares( stockid ) new rows = cache_get_row_count( ); - if ( rows ) { + if ( rows ) + { g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = cache_get_field_content_float( 0, "SHARES_OWNED" ) + cache_get_field_content_float( 0, "SHARES_HELD" ); - printf("%f shares", g_stockMarketData[ stockid ] [ E_MAX_SHARES ]); - } else { + + // rows shown but still showing as 0 maximum shares? set it to the ipo issued amount + if ( ! g_stockMarketData[ stockid ] [ E_MAX_SHARES ] ) + { + g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = g_stockMarketData[ stockid ] [ E_IPO_SHARES ]; + } + } + else + { g_stockMarketData[ stockid ] [ E_MAX_SHARES ] = g_stockMarketData[ stockid ] [ E_IPO_SHARES ]; } return 1; @@ -468,6 +470,8 @@ thread StockMarket_OnCancelOrder( playerid ) mysql_single_query( sprintf( "DELETE FROM `STOCK_SELL_ORDERS` WHERE `STOCK_ID`=%d AND `USER_ID`=%d", stockid, player_account ) ); StockMarket_GiveShares( stockid, player_account, shares ); + + SendServerMessage( playerid, "You have cancelled your order of to sell %s shares of %s.", number_format( shares, .decimals = 3 ), g_stockMarketData[ stockid ] [ E_NAME ] ); } return 1; } @@ -566,9 +570,9 @@ stock StockMarket_UpdateEarnings( stockid, amount ) if ( ! Iter_Contains( stockmarkets, stockid ) ) return 0; - printf( "Current Pool: %f, Prior Pool: %f", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 1 ] [ E_POOL ] ); g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] += float( amount ); - mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `POOL` = `POOL` + %d WHERE `ID` = %d", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); + printf( "Current Pool: %f, Prior Pool: %f", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 1 ] [ E_POOL ] ); + mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `POOL`=%f WHERE `ID` = %d", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); return 1; } From 2ad6e41dd81ca5d61d33facadb6a674446a7cfc9 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Wed, 31 Oct 2018 19:01:35 +1100 Subject: [PATCH 09/14] add trading fee --- .../cnr/features/stocks/stocks.pwn | 39 ++++++++++++------- gamemodes/irresistible/servervars.pwn | 4 ++ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index 64f2b8e..da525c4 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -43,6 +43,7 @@ enum static const Float: STOCK_MARKET_POOL_FACTOR = 100000.0; // for every STOCK_MARKET_POOL_FACTOR ... STOCK_MARKET_PRICE_FACTOR added to the price static const Float: STOCK_MARKET_PRICE_FACTOR = 5.0; +static const Float: STOCK_MARKET_TRADING_FEE = 0.01; static stock g_stockMarketData [ MAX_STOCKS ] [ E_STOCK_MARKET_DATA ], @@ -57,6 +58,7 @@ hook OnScriptInit( ) { // server variables AddServerVariable( "stock_report_time", "0", GLOBAL_VARTYPE_INT ); + AddServerVariable( "stock_trading_fees", "0.0", GLOBAL_VARTYPE_FLOAT ); // create markets CreateStockMarket( 0, "The Mining Company", "MC", 100000.0, 25.0, 250.0 ); // 25m mcap max @@ -151,7 +153,7 @@ hook OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] ) StockMarket_GiveShares( stockid, GetPlayerAccountID( playerid ), -shares ); } StockMarket_UpdateSellOrder( stockid, GetPlayerAccountID( playerid ), shares ); - SendServerMessage( playerid, "You have placed a sell order for %s shares at %s each. "COL_ORANGE"To cancel your sell order, /shares cancel", number_format( shares, .decimals = 3 ), cash_format( g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ) ); + SendServerMessage( playerid, "You have placed a sell order for %s shares at %s each. "COL_ORANGE"To cancel your sell order, /shares cancel", number_format( shares, .decimals = 2 ), cash_format( g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ) ); return 1; } return StockMarket_ShowSellSlip( playerid, stockid ); @@ -243,8 +245,14 @@ thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) new Float: ask_price = g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ]; new purchase_cost = floatround( ask_price * shares ); - if ( GetPlayerCash( playerid ) < purchase_cost ) { - return SendError( playerid, "You need at least %s to purchase this many shares.", cash_format( purchase_cost ) ), StockMarket_ShowBuySlip( playerid, stockid ), 1; + new Float: purchase_fee = ask_price * shares * STOCK_MARKET_TRADING_FEE; + + UpdateServerVariableFloat( "stock_trading_fees", GetServerVariableFloat( "stock_trading_fees" ) + ( purchase_fee / 1000.0 ) ); + + new purchase_cost_plus_fee = purchase_cost + floatround( purchase_fee ); + + if ( GetPlayerCash( playerid ) < purchase_cost_plus_fee ) { + return SendError( playerid, "You need at least %s to purchase this many shares.", cash_format( purchase_cost_plus_fee ) ), StockMarket_ShowBuySlip( playerid, stockid ), 1; } new @@ -264,13 +272,18 @@ thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) } new Float: sold_shares = amount_remaining > sell_order_shares ? sell_order_shares : amount_remaining; - new sold_amount = floatround( sold_shares * ask_price ); + + new Float: sold_amount_fee = sold_shares * ask_price * STOCK_MARKET_TRADING_FEE; + + UpdateServerVariableFloat( "stock_trading_fees", GetServerVariableFloat( "stock_trading_fees" ) + ( sold_amount_fee / 1000.0 ) ); + + new sold_amount_minus_fee = floatround( sold_shares * ask_price - sold_amount_fee ); if ( 0 <= sellerid < MAX_PLAYERS && Iter_Contains( Player, sellerid ) ) { - GivePlayerBankMoney( sellerid, sold_amount ), Beep( sellerid ); - SendServerMessage( sellerid, "You have sold %s %s shares to %s(%d) for "COL_GOLD"%s"COL_WHITE"!", number_format( sold_shares, .decimals = 3 ), g_stockMarketData[ stockid ] [ E_NAME ], ReturnPlayerName( playerid ), playerid, cash_format( sold_amount ) ); + GivePlayerBankMoney( sellerid, sold_amount_minus_fee ), Beep( sellerid ); + SendServerMessage( sellerid, "You have sold %s %s shares to %s(%d) for "COL_GOLD"%s"COL_WHITE" (plus %0.1f%s fee)!", number_format( sold_shares, .decimals = 2 ), g_stockMarketData[ stockid ] [ E_NAME ], ReturnPlayerName( playerid ), playerid, cash_format( sold_amount_minus_fee ), STOCK_MARKET_TRADING_FEE * 100.0, "%%" ); } else { - mysql_single_query( sprintf( "UPDATE `USERS` SET `BANKMONEY` = `BANKMONEY` + %d WHERE `ID` = %d", sold_amount, sell_order_user_id ) ); + mysql_single_query( sprintf( "UPDATE `USERS` SET `BANKMONEY` = `BANKMONEY` + %d WHERE `ID` = %d", sold_amount_minus_fee, sell_order_user_id ) ); } // remove the sell order if there is little to no shares available @@ -296,8 +309,8 @@ thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) StockMarket_GiveShares( stockid, GetPlayerAccountID( playerid ), shares ); // reduce player balance and alert - GivePlayerCash( playerid, -purchase_cost ); - SendServerMessage( playerid, "You have successfully purchased %s shares of %s (@ %s/ea) for %s.", number_format( shares, .decimals = 3 ), g_stockMarketData[ stockid ] [ E_NAME ], cash_format( ask_price, .decimals = 2 ), cash_format( purchase_cost ) ); + GivePlayerCash( playerid, -purchase_cost_plus_fee ); + SendServerMessage( playerid, "You have purchased %s shares of %s (@ %s/ea) for %s. (inc. %0.1f%s fee)", number_format( shares, .decimals = 2 ), g_stockMarketData[ stockid ] [ E_NAME ], cash_format( ask_price, .decimals = 2 ), cash_format( purchase_cost_plus_fee ), STOCK_MARKET_TRADING_FEE * 100.0, "%%" ); return 1; } @@ -318,7 +331,7 @@ thread StockMarket_OnShowBuySlip( playerid, stockid ) ""COL_WHITE"You can buy shares of %s for "COL_GREEN"%s"COL_WHITE" each.\n\nThere are %s available shares to buy.", g_stockMarketData[ stockid ] [ E_NAME ], cash_format( g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ), - number_format( available_quantity, .decimals = 3 ) + number_format( available_quantity, .decimals = 2 ) ); ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET_BUY, DIALOG_STYLE_INPUT, ""COL_WHITE"Stock Market", szBigString, "Buy", "Close" ); return 1; @@ -351,7 +364,7 @@ thread StockMarket_OnShowShares( playerid ) szLargeString, g_stockMarketData[ stockid ] [ E_NAME ], g_stockMarketData[ stockid ] [ E_SYMBOL ], - number_format( shares, .decimals = 3 ), + number_format( shares, .decimals = 2 ), cash_format( current_price, .decimals = 2 ), cash_format( floatround( shares * current_price ) ) ); @@ -471,7 +484,7 @@ thread StockMarket_OnCancelOrder( playerid ) mysql_single_query( sprintf( "DELETE FROM `STOCK_SELL_ORDERS` WHERE `STOCK_ID`=%d AND `USER_ID`=%d", stockid, player_account ) ); StockMarket_GiveShares( stockid, player_account, shares ); - SendServerMessage( playerid, "You have cancelled your order of to sell %s shares of %s.", number_format( shares, .decimals = 3 ), g_stockMarketData[ stockid ] [ E_NAME ] ); + SendServerMessage( playerid, "You have cancelled your order of to sell %s shares of %s.", number_format( shares, .decimals = 2 ), g_stockMarketData[ stockid ] [ E_NAME ] ); } return 1; } @@ -609,7 +622,7 @@ static stock StockMarket_ShowSellSlip( playerid, stockid ) ""COL_WHITE"You can sell shares of %s for "COL_GREEN"%s"COL_WHITE" each.\n\nThough, you will have to wait until a person buys them.\n\nYou have %s available shares to sell.", g_stockMarketData[ stockid ] [ E_NAME ], cash_format( g_stockMarketReportData[ stockid ] [ 1 ] [ E_PRICE ], .decimals = 2 ), - number_format( p_PlayerShares[ playerid ] [ stockid ], .decimals = 3 ) + number_format( p_PlayerShares[ playerid ] [ stockid ], .decimals = 2 ) ); ShowPlayerDialog( playerid, DIALOG_STOCK_MARKET_SELL, DIALOG_STYLE_INPUT, ""COL_WHITE"Stock Market", szLargeString, "Sell", "Close" ); return 1; diff --git a/gamemodes/irresistible/servervars.pwn b/gamemodes/irresistible/servervars.pwn index f135fde..eb9fbc2 100644 --- a/gamemodes/irresistible/servervars.pwn +++ b/gamemodes/irresistible/servervars.pwn @@ -14,6 +14,7 @@ /* ** Macros ** */ #define GetServerVariableInt GetGVarInt +#define GetServerVariableFloat GetGVarFloat #define UpdateServerVariableString(%0,%1) \ (UpdateServerVariable(%0, 0, 0, %1, GLOBAL_VARTYPE_STRING)) @@ -21,6 +22,9 @@ #define UpdateServerVariableInt(%0,%1) \ (UpdateServerVariable(%0, %1, 0, "", GLOBAL_VARTYPE_INT)) +#define UpdateServerVariableFloat(%0,%1) \ + (UpdateServerVariable(%0, 0, %1, "", GLOBAL_VARTYPE_FLOAT)) + /* ** Hooks ** */ hook OnGameModeInit( ) { From 8ad01c0b0fb16d546d3f3f8c40ae6fc533b39614 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Wed, 31 Oct 2018 19:03:24 +1100 Subject: [PATCH 10/14] crushing ores at alca contributes to mining company --- gamemodes/irresistible/cnr/features/minijobs/mining.pwn | 1 + 1 file changed, 1 insertion(+) diff --git a/gamemodes/irresistible/cnr/features/minijobs/mining.pwn b/gamemodes/irresistible/cnr/features/minijobs/mining.pwn index 887e9f5..87fd9ca 100644 --- a/gamemodes/irresistible/cnr/features/minijobs/mining.pwn +++ b/gamemodes/irresistible/cnr/features/minijobs/mining.pwn @@ -208,6 +208,7 @@ hook OnPlayerEnterDynamicCP( playerid, checkpointid ) new earned_money = floatround( float( g_orePrices[ g_miningData[ ore ] [ E_ORE ] ] ) * 0.5 ); GivePlayerCash( playerid, earned_money ); + StockMarket_UpdateEarnings( E_STOCK_MINING_COMPANY, earned_money ); SendServerMessage( playerid, "You have crushed a "COL_GREY"%s"COL_WHITE" Ore and earned "COL_GOLD"%s"COL_WHITE".", getOreName( g_miningData[ ore ] [ E_ORE ] ), cash_format( earned_money ) ); RemoveEquippedOre( playerid ); } From 7645f3f8e8fae8a79e61934153de44f7c9bbe580 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Wed, 31 Oct 2018 19:04:59 +1100 Subject: [PATCH 11/14] create two reports by default when a new company is made --- gamemodes/irresistible/cnr/features/stocks/stocks.pwn | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index da525c4..fff3994 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -205,8 +205,10 @@ thread Stock_UpdateReportingPeriods( stockid ) // set current stock market prices to IPO g_stockMarketReportData[ stockid ] [ 0 ] [ E_PRICE ] = g_stockMarketData[ stockid ] [ E_IPO_PRICE ]; - // create report for the company using the IPO price - StockMarket_ReleaseDividends( stockid ); + // create 2 reports for the company using the IPO price ... this way the price is not $0 + for ( new i = 0; i < 2; i ++ ) { + StockMarket_ReleaseDividends( stockid ); + } // put market maker shares on the market StockMarket_UpdateSellOrder( stockid, STOCK_MM_USER_ID, g_stockMarketData[ stockid ] [ E_IPO_SHARES ] ); From 90cdbd9816a70a78abc0ef31adbf8304b49816a3 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Wed, 31 Oct 2018 20:05:47 +1100 Subject: [PATCH 12/14] add trade log --- .../cnr/features/stocks/stocks.pwn | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index fff3994..9e9d3e2 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -275,6 +275,8 @@ thread StockMarket_OnPurchaseOrder( playerid, stockid, Float: shares ) new Float: sold_shares = amount_remaining > sell_order_shares ? sell_order_shares : amount_remaining; + StockMarket_CreateTradeLog( stockid, GetPlayerAccountID( playerid ), sell_order_user_id, sold_shares, ask_price ); + new Float: sold_amount_fee = sold_shares * ask_price * STOCK_MARKET_TRADING_FEE; UpdateServerVariableFloat( "stock_trading_fees", GetServerVariableFloat( "stock_trading_fees" ) + ( sold_amount_fee / 1000.0 ) ); @@ -545,7 +547,7 @@ CMD:shares( playerid, params[ ] ) } /* ** Functions ** */ -stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ipo_shares, Float: ipo_price, Float: max_price ) +static stock CreateStockMarket( stockid, const name[ 64 ], const symbol[ 4 ], Float: ipo_shares, Float: ipo_price, Float: max_price ) { if ( ! Iter_Contains( stockmarkets, stockid ) ) { @@ -591,7 +593,7 @@ stock StockMarket_UpdateEarnings( stockid, amount ) return 1; } -stock StockMarket_GiveShares( stockid, accountid, Float: shares ) +static stock StockMarket_GiveShares( stockid, accountid, Float: shares ) { mysql_format( dbHandle, szBigString, sizeof ( szBigString ), @@ -601,7 +603,7 @@ stock StockMarket_GiveShares( stockid, accountid, Float: shares ) mysql_single_query( szBigString ); } -stock StockMarket_UpdateSellOrder( stockid, accountid, Float: shares ) +static stock StockMarket_UpdateSellOrder( stockid, accountid, Float: shares ) { mysql_format( dbHandle, szBigString, sizeof ( szBigString ), @@ -611,6 +613,16 @@ stock StockMarket_UpdateSellOrder( stockid, accountid, Float: shares ) mysql_single_query( szBigString ); } +static stock StockMarket_CreateTradeLog( stockid, buyer_acc, seller_acc, Float: shares, Float: price ) +{ + mysql_format( + dbHandle, szBigString, sizeof ( szBigString ), + "INSERT INTO `STOCK_TRADE_LOG` (`STOCK_ID`, `BUYER_ID`, `SELLER_ID`, `SHARES`, `PRICE`) VALUES (%d, %d, %d, %f, %f)", + stockid, buyer_acc, seller_acc, shares, price + ); + mysql_single_query( szBigString ); +} + static stock StockMarket_ShowBuySlip( playerid, stockid ) { mysql_tquery( dbHandle, sprintf( "SELECT SUM(`SHARES`) AS `SALE_SHARES` FROM `STOCK_SELL_ORDERS` WHERE `STOCK_ID`=%d", stockid ), "StockMarket_OnShowBuySlip", "dd", playerid, stockid ); @@ -656,4 +668,15 @@ static stock StockMarket_ShowSellSlip( playerid, stockid ) `LIST_DATE` TIMESTAMP default CURRENT_TIMESTAMP, PRIMARY KEY (STOCK_ID, USER_ID) ); + + DROP TABLE `STOCK_TRADE_LOG`; + CREATE TABLE IF NOT EXISTS `STOCK_TRADE_LOG` ( + `ID` int(11) primary key auto_increment, + `STOCK_ID` int(11), + `BUYER_ID` int(11), + `SELLER_ID` int(11), + `SHARES` float, + `PRICE` float, + `LIST_DATE` TIMESTAMP default CURRENT_TIMESTAMP + ); */ From 469e8ac865a1467a2a8db3054a123037d952cdc1 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Thu, 1 Nov 2018 17:48:25 +1100 Subject: [PATCH 13/14] stock market edits --- .../irresistible/cnr/features/stocks/stocks.pwn | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn index 9e9d3e2..6531c1e 100644 --- a/gamemodes/irresistible/cnr/features/stocks/stocks.pwn +++ b/gamemodes/irresistible/cnr/features/stocks/stocks.pwn @@ -1,8 +1,8 @@ /* * Irresistible Gaming (c) 2018 * Developed by Lorenc Pekaj - * Module: cnrs - * Purpose: + * Module: cnr\features\stocks\stocks.pwn + * Purpose: stock market system for players */ /* ** Includes ** */ @@ -11,7 +11,7 @@ /* ** Definitions ** */ #define MAX_STOCKS ( 32 ) -#define STOCK_REPORTING_PERIOD ( 600 ) // 1 day +#define STOCK_REPORTING_PERIOD ( 86400 ) // 1 day #define STOCK_REPORTING_PERIODS ( 30 ) // last 30 periods (days) @@ -61,7 +61,7 @@ hook OnScriptInit( ) AddServerVariable( "stock_trading_fees", "0.0", GLOBAL_VARTYPE_FLOAT ); // create markets - CreateStockMarket( 0, "The Mining Company", "MC", 100000.0, 25.0, 250.0 ); // 25m mcap max + CreateStockMarket( E_STOCK_MINING_COMPANY, "The Mining Company", "MC", 100000.0, 25.0, 250.0 ); // 25m mcap max return 1; } @@ -141,7 +141,7 @@ hook OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] ) if ( sscanf( inputtext, "d", input_shares ) ) SendError( playerid, "You must use a valid value." ); else if ( input_shares > floatround( p_PlayerShares[ playerid ] [ stockid ], floatround_floor ) ) SendError( playerid, "You do not have this many shares available to sell." ); - else if ( input_shares <= 1 ) SendError( playerid, "The minimum number of shares you can sell is 1." ); + else if ( input_shares < 1 ) SendError( playerid, "The minimum number of shares you can sell is 1." ); else { new @@ -408,7 +408,7 @@ thread Stock_OnDividendPayout( stockid ) if ( 0 <= shareholder < MAX_PLAYERS && Iter_Contains( Player, shareholder ) ) { GivePlayerBankMoney( shareholder, dividend_payout ), Beep( shareholder ); - SendServerMessage( shareholder, "A "COL_GOLD"%s"COL_WHITE" dividend (%0.2f%s) has been paid out for owning %s!", cash_format( dividend_payout ), dividend_rate * 100.0, "%%", g_stockMarketData[ stockid ] [ E_NAME ] ); + SendServerMessage( shareholder, "You have been paid a "COL_GOLD"%s"COL_WHITE" dividend (%0.2f%s) for owning %s!", cash_format( dividend_payout ), dividend_rate * 100.0, "%%", g_stockMarketData[ stockid ] [ E_NAME ] ); } else { mysql_single_query( sprintf( "UPDATE `USERS` SET `BANKMONEY` = `BANKMONEY` + %d WHERE `ID` = %d", dividend_payout, account_id ) ); } @@ -588,7 +588,7 @@ stock StockMarket_UpdateEarnings( stockid, amount ) return 0; g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ] += float( amount ); - printf( "Current Pool: %f, Prior Pool: %f", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 1 ] [ E_POOL ] ); + //printf( "Current Pool: %f, Prior Pool: %f", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 1 ] [ E_POOL ] ); mysql_single_query( sprintf( "UPDATE `STOCK_REPORTS` SET `POOL`=%f WHERE `ID` = %d", g_stockMarketReportData[ stockid ] [ 0 ] [ E_POOL ], g_stockMarketReportData[ stockid ] [ 0 ] [ E_SQL_ID ] ) ); return 1; } @@ -677,6 +677,6 @@ static stock StockMarket_ShowSellSlip( playerid, stockid ) `SELLER_ID` int(11), `SHARES` float, `PRICE` float, - `LIST_DATE` TIMESTAMP default CURRENT_TIMESTAMP + `DATE` TIMESTAMP default CURRENT_TIMESTAMP ); */ From f3afaa9af485fc1c20d1083a64e056833abbf434 Mon Sep 17 00:00:00 2001 From: Lorenc Pekaj Date: Thu, 1 Nov 2018 17:49:29 +1100 Subject: [PATCH 14/14] remove some useless hq --- .../cnr/static/player_objects.pwn | 221 ------------------ .../cnr/static/removed_buildings.pwn | 17 -- gamemodes/sf-cnr.pwn | 4 +- 3 files changed, 2 insertions(+), 240 deletions(-) diff --git a/gamemodes/irresistible/cnr/static/player_objects.pwn b/gamemodes/irresistible/cnr/static/player_objects.pwn index 8c2aabd..41e63ad 100644 --- a/gamemodes/irresistible/cnr/static/player_objects.pwn +++ b/gamemodes/irresistible/cnr/static/player_objects.pwn @@ -12934,227 +12934,6 @@ hook OnScriptInit( ) CreateDynamicObject( 3471, -1737.049682, 1084.507812, 45.405311, 0.000000, 0.000000, 90.000000 ); CreateDynamicObject( 18648, -1731.545654, 1082.613403, 47.365268, 0.000000, 0.000000, 90.000000 ); - // Kidz killer HQ - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2017.155517, 366.183319, 25.371856, 0.000000, 0.000000, 0.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2030.143676, 378.183349, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2055.143554, 378.183349, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2124.156982, 378.173339, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2136.394775, 366.173309, 25.373870, 0.000000, 0.000000, 0.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2136.392822, 342.825164, 25.375885, 0.000000, 0.000000, 0.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2123.486328, 330.823211, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2098.486328, 330.823211, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2073.486328, 330.823211, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2048.482421, 330.825225, 25.373870, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2029.156982, 330.823211, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2017.153564, 343.785858, 25.373870, 0.000000, 0.000000, 0.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2098.450439, 365.956604, 33.663879, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2073.450439, 365.956604, 33.663879, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2048.450439, 365.956604, 33.663879, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2025.840698, 365.956604, 33.661865, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2025.840698, 340.996765, 33.661865, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 5710, -2123.142578, 354.394378, 39.643890, 0.000000, 0.000000, 0.000000 ), 2, 8671, "vegassland62", "ws_stonewall", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2085.983398, 377.362457, 37.853912, 0.000000, 90.000000, -20.000000 ), 0, 8671, "vegassland62", "ws_stonewall", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2050.840820, 340.996765, 33.661865, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2123.450439, 365.956604, 33.663879, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2060.250000, 340.996765, 33.665863, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2100.656982, 378.173339, 25.371856, 0.000000, 0.000000, 90.000000 ), 0, 10350, "oc_flats_gnd_sfs", "veg_hedge1_256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2095.673828, 340.996765, 33.661865, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2120.268066, 340.996765, 33.661865, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2104.162841, 378.686676, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2095.657958, 378.686676, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2069.805175, 377.378417, 32.853912, 0.000000, 90.000000, 20.000000 ), 0, 5768, "sunrise05_lawn", "hedge1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2085.983398, 377.362457, 32.853912, 0.000000, 90.000000, -20.000000 ), 0, 5768, "sunrise05_lawn", "hedge1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2073.148925, 374.563323, 32.853912, 0.000000, 90.000000, 60.000000 ), 0, 5768, "sunrise05_lawn", "hedge1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2082.652587, 374.562194, 32.853912, 0.000000, 90.000000, -60.000000 ), 0, 5768, "sunrise05_lawn", "hedge1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2105.642578, 330.309082, 27.574066, 0.000000, 90.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2047.861083, 330.307067, 27.574066, 0.000000, 90.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2016.652832, 337.806976, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2016.652832, 352.786926, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2016.652832, 367.786987, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2016.652832, 371.196685, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2024.152954, 378.686706, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2039.153076, 378.686706, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2054.153076, 378.686706, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2060.145751, 378.686706, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18762, -2081.469238, 372.147338, 35.344055, 0.000000, 0.000000, 0.000000 ), 0, 5870, "sunst18_lawn", "greenwall3", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18762, -2074.336669, 372.147338, 35.344055, 0.000000, 0.000000, 0.000000 ), 0, 5870, "sunst18_lawn", "greenwall3", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2129.394775, 378.676696, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2119.154785, 378.686706, 3.824064, 90.000000, 0.000000, 0.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.896240, 371.187042, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.896240, 356.207244, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.896240, 341.207244, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 3924, "rc_warhoose", "white", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.896240, 337.807373, 3.824064, 90.000000, 0.000000, 90.000000 ), 0, 5768, "sunrise05_lawn", "hedge1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2077.927246, 370.362854, 34.093841, 0.000000, 90.000000, 90.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2077.927246, 359.862945, 34.093841, 0.000000, 90.000000, 90.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2046.495605, 354.312927, 34.093841, 0.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2056.994873, 354.312927, 34.093841, 0.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2067.493896, 354.312927, 34.093841, 0.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2077.964599, 357.522888, 34.095855, 0.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2088.416015, 354.312927, 34.095855, 0.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2098.912841, 354.312927, 34.095855, 0.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2109.395751, 354.312927, 34.095855, 0.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2090.005615, 341.534454, 29.471771, 0.000000, 90.000000, 0.000000 ), 0, 16640, "a51", "ws_metalpanel1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2077.943115, 331.544158, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2077.964599, 348.236968, 32.444885, 20.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2077.964599, 339.196624, 29.154632, 20.000000, 90.000000, 0.000000 ), 0, 6286, "santamonhus1", "ws_floortiles2", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 5710, -2030.530273, 354.394378, 39.643890, 0.000000, 0.000000, 180.000000 ), 2, 8671, "vegassland62", "ws_stonewall", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2082.652587, 374.562194, 37.853912, 0.000000, 90.000000, -60.000000 ), 0, 8671, "vegassland62", "ws_stonewall", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2065.005615, 341.534454, 29.471771, 0.000000, 90.000000, 0.000000 ), 0, 16640, "a51", "ws_metalpanel1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2072.263427, 353.534790, 21.641845, 0.000000, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2083.655273, 353.534790, 21.641845, 0.000000, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2052.943115, 331.544158, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2102.943115, 331.544158, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2120.934570, 344.523712, 21.651794, 0.000000, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2032.943115, 344.494140, 21.651794, 0.000000, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2059.396240, 354.353820, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2096.625976, 354.353820, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18980, -2072.253906, 344.330871, 33.651641, 0.000000, 90.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18980, -2083.665039, 344.330871, 33.651641, 0.000000, 90.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2095.673828, 341.496765, 33.658874, 0.000000, 90.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2059.278320, 341.496765, 33.658874, 0.000000, 90.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2115.005615, 341.534454, 29.471771, 0.000000, 90.000000, 0.000000 ), 0, 16640, "a51", "ws_metalpanel1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2040.005615, 341.534454, 29.471771, 0.000000, 90.000000, 0.000000 ), 0, 16640, "a51", "ws_metalpanel1", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2121.625976, 354.353820, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2127.943115, 331.544158, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2027.943115, 331.544158, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2034.396240, 354.353820, 21.651794, 0.000000, 0.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2120.673828, 341.496765, 33.658874, 0.000000, 90.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2034.278320, 341.496765, 33.658874, 0.000000, 90.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", -16 ); - CreateDynamicObject( 18765, -2125.995849, 361.193359, 33.561981, 0.000000, 0.000000, 0.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18765, -2125.995849, 352.033355, 33.566009, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18765, -2129.058837, 361.193359, 33.559967, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18765, -2128.908691, 352.033355, 33.557983, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - CreateDynamicObject( 19377, -2122.083740, 361.282012, 35.786132, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 19377, -2127.015380, 366.301971, 35.786132, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 19377, -2129.095458, 366.273956, 35.786132, 0.000000, 0.000000, 90.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2133.866210, 361.282012, 35.786132, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2133.876220, 352.052368, 35.786132, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - CreateDynamicObject( 19377, -2129.095458, 347.214202, 35.786132, 0.000000, 0.000000, 90.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2126.893310, 347.224212, 35.786132, 0.000000, 0.000000, 90.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19454, -2122.111083, 348.966033, 37.115997, 90.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19454, -2122.111083, 350.456024, 37.115997, 90.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - CreateDynamicObject( 18765, -2027.676147, 356.483520, 33.461975, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 18765, -2027.676147, 347.583831, 33.463989, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 18765, -2024.873779, 347.583831, 33.467956, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 18765, -2024.873779, 356.473541, 33.471984, 0.000000, 0.000000, 0.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18981, -2123.450439, 340.956604, 33.663879, 0.000000, 90.000000, 0.000000 ), 0, 6328, "sunset04_law2", "desgreengrass", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2031.545654, 347.509063, 35.891967, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2026.704956, 342.549072, 35.891967, 0.000000, 0.000000, 90.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2024.643066, 342.549072, 35.891967, 0.000000, 0.000000, 90.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2019.824340, 347.509063, 35.891967, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2019.824340, 356.649078, 35.891967, 0.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2024.643066, 361.549224, 35.891967, 0.000000, 0.000000, 90.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19377, -2026.834106, 361.549224, 35.891967, 0.000000, 0.000000, 90.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19454, -2031.541503, 359.992462, 36.307952, 90.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19454, -2031.541503, 358.302612, 36.307952, 90.000000, 0.000000, 0.000000 ), 0, 10959, "silconland_sfse", "desertstones256", 1 ); - CreateDynamicObject( 1535, -2019.868652, 353.643005, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2019.868652, 348.912994, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2019.868652, 358.352966, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2019.868652, 344.183044, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2023.539062, 342.593078, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 1535, -2029.340820, 342.593078, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 1535, -2031.438720, 344.253082, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2031.438720, 349.223144, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2023.528686, 361.462921, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 1535, -2029.309082, 361.462921, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 1535, -2031.439819, 357.922973, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2122.167968, 349.303100, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2122.167968, 358.092926, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2122.167968, 362.963043, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2133.753417, 348.913024, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2133.753417, 353.643096, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2133.753417, 358.343048, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2133.753417, 363.093200, 36.041961, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 1535, -2125.850585, 366.163024, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 1535, -2131.623535, 366.163024, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 1535, -2125.850585, 347.292785, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 1535, -2131.653076, 347.292785, 36.041961, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 638, -2083.102294, 363.670715, 34.811828, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 638, -2072.769287, 363.670715, 34.811828, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 638, -2083.102294, 369.670715, 34.811828, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 638, -2072.769287, 369.670715, 34.811828, 0.000000, 0.000000, 0.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2069.809326, 377.377777, 30.069793, 0.000000, 90.000000, 20.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2073.147705, 374.572296, 30.069793, 0.000000, 90.000000, 60.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2085.979980, 377.361602, 30.069793, 0.000000, 90.000000, -20.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2082.639892, 374.560455, 30.069793, 0.000000, 90.000000, -60.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - CreateDynamicObject( 712, -2118.144287, 342.962463, 43.471923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 712, -2118.144287, 367.172790, 43.221923, 0.000000, 0.000000, 0.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2073.148925, 374.563323, 37.853912, 0.000000, 90.000000, 60.000000 ), 0, 8671, "vegassland62", "ws_stonewall", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2069.805175, 377.378417, 37.853912, 0.000000, 90.000000, 20.000000 ), 0, 8671, "vegassland62", "ws_stonewall", 1 ); - CreateDynamicObject( 712, -2035.580200, 342.712554, 43.471923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 712, -2035.544677, 367.172790, 43.221923, 0.000000, 0.000000, 0.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2077.805908, 372.116149, 40.241821, 0.000000, 0.000000, 0.000000 ), 0, 5870, "sunst18_lawn", "greenwall3", 1 ); - CreateDynamicObject( 19861, -2120.433105, 338.419647, 32.431793, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 3604, -2056.936279, 372.088439, 36.649810, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 3604, -2097.162597, 372.088439, 36.649810, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 712, -2068.772460, 373.242797, 43.221923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 712, -2087.048583, 373.242797, 43.221923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 16096, -2112.773925, 370.482391, 35.993888, 0.000000, 0.000000, 128.500030 ); - CreateDynamicObject( 16096, -2041.115356, 372.045135, 35.993888, 0.000000, 0.000000, 55.000000 ); - CreateDynamicObject( 3587, -2057.597412, 338.467407, 36.663879, 0.000000, 0.000000, 180.000000 ); - CreateDynamicObject( 712, -2045.290527, 335.312500, 43.221923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 712, -2067.622558, 335.312500, 43.221923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 3587, -2098.391845, 338.467407, 36.663879, 0.000000, 0.000000, 180.000000 ); - CreateDynamicObject( 712, -2088.423583, 335.312500, 43.221923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 712, -2110.217773, 335.312500, 43.221923, 0.000000, 0.000000, 0.000000 ); - CreateDynamicObject( 3171, -2044.275634, 341.980041, 34.155853, 0.000000, 0.000000, -52.500000 ); - CreateDynamicObject( 16096, -2113.177734, 339.110656, 35.993888, 0.000000, 0.000000, -125.699943 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2032.280151, 378.678375, 11.448147, 90.000000, 0.000000, 180.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2052.045410, 378.678375, 11.448147, 90.000000, 0.000000, 180.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19543, -2105.636230, 330.327941, 35.171875, 0.000000, 90.000000, 90.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19543, -2047.911865, 330.327941, 35.171875, 0.000000, 90.000000, 90.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2016.708129, 345.898559, 11.448147, 90.000000, 0.000000, 90.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2016.708129, 363.078186, 11.448147, 90.000000, 0.000000, 90.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2136.875732, 363.078186, 11.448147, 90.000000, 0.000000, 90.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2136.875732, 345.938568, 11.448147, 90.000000, 0.000000, 90.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2016.708129, 363.078186, 11.448147, 90.000000, 0.000000, 90.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2103.727294, 378.678375, 11.448147, 90.000000, 0.000000, 180.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19853, -2121.260742, 378.678375, 11.448147, 90.000000, 0.000000, 180.000000 ), 0, 18202, "w_towncs_t", "concretebig4256128", 1 ); - CreateDynamicObject( 19861, -2120.433105, 347.699768, 32.431793, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 19861, -2033.460449, 347.699768, 32.431793, 0.000000, 0.000000, 90.000000 ); - CreateDynamicObject( 19861, -2033.451293, 338.419647, 32.431793, 0.000000, 0.000000, 90.000000 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2124.384765, 377.560394, 25.820850, 77.400154, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2100.689208, 377.560394, 25.820850, 77.400154, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2055.215332, 377.560394, 25.820850, 77.400154, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2030.233154, 377.560394, 25.820848, 77.400154, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2030.233154, 331.633087, 25.783863, 76.600166, 0.000000, 180.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2055.234130, 331.633087, 25.783863, 76.600166, 0.000000, 180.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2080.216064, 331.633087, 25.783863, 76.600166, 0.000000, 180.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2105.201904, 331.633087, 25.783863, 76.600166, 0.000000, 180.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19645, -2124.374755, 331.633087, 25.783863, 76.600166, 0.000000, 180.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2105.601074, 366.198822, 48.450839, 0.000000, 0.000000, 90.000000 ), 0, 6293, "lawland2", "lightglass", 0 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2105.601074, 342.738800, 48.446842, 0.000000, 0.000000, 90.000000 ), 0, 6293, "lawland2", "lightglass", 0 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2105.601074, 354.688873, 48.448841, 0.000000, 0.000000, 90.000000 ), 0, 6293, "lawland2", "lightglass", 0 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2049.155761, 366.198822, 48.448841, 0.000000, 0.000000, 90.000000 ), 0, 6293, "lawland2", "lightglass", 0 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2049.155761, 342.728942, 48.446842, 0.000000, 0.000000, 90.000000 ), 0, 6293, "lawland2", "lightglass", 0 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2049.155761, 353.809143, 48.448841, 0.000000, 0.000000, 90.000000 ), 0, 6293, "lawland2", "lightglass", 0 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18980, -2136.374267, 347.356109, 48.198833, 0.000000, 90.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18980, -2136.374267, 361.505981, 48.198833, 0.000000, 90.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18980, -2018.213012, 347.356109, 48.198833, 0.000000, 90.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18980, -2018.213012, 361.525878, 48.198833, 0.000000, 90.000000, 90.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19695, -2084.882324, 377.721374, 37.846611, 89.400047, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19695, -2072.402099, 377.721374, 37.846611, 89.400047, 0.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18762, -2084.792236, 377.828857, 38.071899, 0.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18762, -2079.802978, 377.828857, 38.071899, 0.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18762, -2074.814208, 377.828857, 38.071899, 0.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 18762, -2071.010253, 377.828857, 38.071899, 0.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2017.719360, 338.425872, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2017.719360, 370.155487, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2017.719360, 355.205871, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2017.719360, 347.775848, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.837890, 338.425872, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.837890, 348.735809, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.837890, 363.415863, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.837890, 371.155700, 16.685642, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", 1 ); - SetDynamicObjectMaterial( CreateDynamicObject( 19545, -2136.897949, 337.805847, 3.825637, 90.000000, 90.000000, 0.000000 ), 0, 16150, "ufo_bar", "GEwhite1_64", -16 ); - SetDynamicObjectMaterialText( CreateDynamicObject( 4731, -2077.434082, 378.000000, 42.921886, 0.000000, 0.000000, -149.800064 ), 0, "Virtual Titans\nHeadquarters", 120, "Narnia BLL", 64, 0, 0xE63224FF, 0, 1 ); - CreateDynamicObject( 3528, -2068.123535, 378.836273, 42.571815, 0.000000, 0.000000, 120.000000 ); - CreateDynamicObject( 3528, -2086.125732, 378.836273, 42.571815, 0.000000, 0.000000, 45.000000 ); - CreateDynamicObject( 3528, -2077.341064, 376.057586, 48.889625, 0.000000, 24.299999, 90.000000 ); - CreateDynamicObject( 3524, -2080.994384, 372.604339, 35.833873, 0.000000, 0.000000, 135.000000 ); - CreateDynamicObject( 3524, -2074.698486, 372.689086, 35.833873, 0.000000, 0.000000, -135.000000 ); - CreateDynamicObject( 16644, -2114.910888, 333.414978, 36.925888, 0.000000, 36.400035, -2.299999 ); - // iAshley Beach House SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2900.878417, -482.737640, 3.583874, 0.000000, 0.000000, 45.000000 ), 0, 5134, "wasteland_las2", "ws_bigstones", 0 ); SetDynamicObjectMaterial( CreateDynamicObject( 18766, -2894.511962, -482.723541, 4.573882, 0.000000, 0.000000, 135.000000 ), 0, 5134, "wasteland_las2", "ws_bigstones", 0 ); diff --git a/gamemodes/irresistible/cnr/static/removed_buildings.pwn b/gamemodes/irresistible/cnr/static/removed_buildings.pwn index e1f63da..1d09a66 100644 --- a/gamemodes/irresistible/cnr/static/removed_buildings.pwn +++ b/gamemodes/irresistible/cnr/static/removed_buildings.pwn @@ -208,15 +208,6 @@ stock removeExcessiveBuildings( playerid ) RemoveBuildingForPlayer(playerid, 9842, -2920.5703, 461.7969, -61.3906, 0.25); RemoveBuildingForPlayer(playerid, 9829, -2920.5703, 461.7969, -61.3906, 0.25); - // Kidz Killer Clan HQ - RemoveBuildingForPlayer(playerid, 1396, -1723.5781, 188.9219, 27.0313, 0.25); - RemoveBuildingForPlayer(playerid, 1397, -1728.9141, 188.9063, 22.5156, 0.25); - RemoveBuildingForPlayer(playerid, 10774, -1739.2109, 166.7109, 5.6875, 0.25); - RemoveBuildingForPlayer(playerid, 1376, -1728.9141, 188.9063, 22.5156, 0.25); - RemoveBuildingForPlayer(playerid, 1386, -1728.9141, 188.9063, 35.7422, 0.25); - RemoveBuildingForPlayer(playerid, 1377, -1754.3906, 188.9219, 29.8203, 0.25); - RemoveBuildingForPlayer(playerid, 1378, -1723.5781, 188.9219, 27.0313, 0.25); - // CHUKYCDM LS RemoveBuildingForPlayer(playerid, 1219, 1284.1797, -1239.6406, 12.9141, 0.25); RemoveBuildingForPlayer(playerid, 1227, 1322.1875, -1235.8828, 13.4375, 0.25); @@ -227,14 +218,6 @@ stock removeExcessiveBuildings( playerid ) // Crypton Base RemoveBuildingForPlayer(playerid, 626, 1243.5078, -1640.4609, 28.4219, 100.0); - // iTRiiPY Base [OP] - RemoveBuildingForPlayer(playerid, 11024, -2076.2734, 359.3203, 44.5938, 0.25); - RemoveBuildingForPlayer(playerid, 3868, -2022.0859, 347.1328, 49.1563, 125.0); - RemoveBuildingForPlayer(playerid, 3867, -2049.7344, 336.8672, 49.1563, 125.0); - RemoveBuildingForPlayer(playerid, 10946, -2076.6563, 354.3672, 35.7891, 0.25); - RemoveBuildingForPlayer(playerid, 11403, -2076.6563, 354.3672, 35.7891, 0.25); - RemoveBuildingForPlayer(playerid, 10949, -2076.2734, 359.3203, 44.5938, 0.25); - // Wang Cars RemoveBuildingForPlayer(playerid, 1615, -1966.8984, 252.1484, 43.6563, 0.25); RemoveBuildingForPlayer(playerid, 1616, -1940.5703, 250.8281, 45.3828, 0.25); diff --git a/gamemodes/sf-cnr.pwn b/gamemodes/sf-cnr.pwn index dc04397..ce6e60c 100644 --- a/gamemodes/sf-cnr.pwn +++ b/gamemodes/sf-cnr.pwn @@ -16,7 +16,7 @@ #pragma option -d3 #pragma dynamic 7200000 -#define DEBUG_MODE +//#define DEBUG_MODE #if defined DEBUG_MODE #pragma option -d3 @@ -33,7 +33,7 @@ /* ** Redefinitions ** */ #undef MAX_PLAYERS -#define MAX_PLAYERS 128 +#define MAX_PLAYERS 126 /* ** Sundry Includes ** */ #include < a_mysql >