Change: move "give money" from client-list to company window

This is a much better location for this button, as you send
money from one company to another company, not from player
to player.

This is based on work done by JGRPP in:
f820543391
and surrounding commits, which took the work from estys:
https://www.tt-forums.net/viewtopic.php?p=1183311#p1183311

We did modify it to fix several bugs and clean up the code while
here anyway.

The callback was removed, as it meant a modified client could
prevent anyone from seeing money was transfered. The message
is now generated in the command itself, making that impossible.
pull/217/head
Patric Stout 3 years ago committed by Patric Stout
parent d6e15d4943
commit 62cdadb582

@ -96,7 +96,6 @@ CommandCallback CcBuildIndustry;
CommandCallback CcPlaySound_EXPLOSION;
CommandCallback CcPlaceSign;
CommandCallback CcTerraform;
CommandCallback CcGiveMoney;
/* rail_gui.cpp */
CommandCallback CcPlaySound_SPLAT_RAIL;

@ -11,6 +11,7 @@
#include "company_base.h"
#include "company_func.h"
#include "company_gui.h"
#include "core/backup_type.hpp"
#include "town.h"
#include "news_func.h"
#include "cmd_helper.h"
@ -1179,3 +1180,50 @@ uint32 CompanyInfrastructure::GetTramTotal() const
}
return total;
}
/**
* Transfer funds (money) from one company to another.
* To prevent abuse in multiplayer games you can only send money to other
* companies if you have paid off your loan (either explicitly, or implicitly
* given the fact that you have more money than loan).
* @param tile unused
* @param flags operation to perform
* @param p1 the amount of money to transfer; max 20.000.000
* @param p2 the company to transfer the money to
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdGiveMoney(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
if (!_settings_game.economy.give_money) return CMD_ERROR;
const Company *c = Company::Get(_current_company);
CommandCost amount(EXPENSES_OTHER, min((Money)p1, (Money)20000000LL));
CompanyID dest_company = (CompanyID)p2;
/* You can only transfer funds that is in excess of your loan */
if (c->money - c->current_loan < amount.GetCost() || amount.GetCost() < 0) return_cmd_error(STR_ERROR_INSUFFICIENT_FUNDS);
if (!Company::IsValidID(dest_company)) return CMD_ERROR;
if (flags & DC_EXEC) {
/* Add money to company */
Backup<CompanyID> cur_company(_current_company, dest_company, FILE_LINE);
SubtractMoneyFromCompany(CommandCost(EXPENSES_OTHER, -amount.GetCost()));
cur_company.Restore();
if (_networking) {
char dest_company_name[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH];
SetDParam(0, dest_company);
GetString(dest_company_name, STR_COMPANY_NAME, lastof(dest_company_name));
char from_company_name[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH];
SetDParam(0, _current_company);
GetString(from_company_name, STR_COMPANY_NAME, lastof(from_company_name));
NetworkTextMessage(NETWORK_ACTION_GIVE_MONEY, GetDrawStringCompanyColour(_current_company), false, from_company_name, dest_company_name, amount.GetCost());
}
}
/* Subtract money from local-company */
return amount;
}

@ -8,6 +8,7 @@
/** @file company_gui.cpp %Company related GUIs. */
#include "stdafx.h"
#include "currency.h"
#include "error.h"
#include "gui.h"
#include "window_gui.h"
@ -2207,6 +2208,12 @@ static const NWidgetPart _nested_company_widgets[] = {
/* Multi player buttons. */
NWidget(NWID_VERTICAL), SetPIP(4, 2, 4),
NWidget(NWID_SPACER), SetFill(0, 1),
NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
NWidget(NWID_SPACER), SetFill(1, 0),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_GIVE_MONEY),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_GIVE_MONEY), SetDataTip(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON, STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP),
EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_C_HAS_PASSWORD),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_MULTIPLAYER),
@ -2329,6 +2336,14 @@ struct CompanyWindow : Window
reinit = true;
}
/* Enable/disable 'Give money' button. */
plane = ((local || _local_company == COMPANY_SPECTATOR || !_settings_game.economy.give_money) ? SZSP_NONE : 0);
wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_GIVE_MONEY);
if (plane != wi->shown_plane) {
wi->SetDisplayedPlane(plane);
reinit = true;
}
/* Multiplayer buttons. */
plane = ((!_networking) ? (int)SZSP_NONE : (int)(local ? CWP_MP_C_PWD : CWP_MP_C_JOIN));
wi = this->GetWidget<NWidgetStacked>(WID_C_SELECT_MULTIPLAYER);
@ -2402,17 +2417,18 @@ struct CompanyWindow : Window
case WID_C_BUILD_HQ:
case WID_C_RELOCATE_HQ:
case WID_C_VIEW_INFRASTRUCTURE:
case WID_C_GIVE_MONEY:
case WID_C_COMPANY_PASSWORD:
case WID_C_COMPANY_JOIN:
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width);
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_BUILD_HQ_BUTTON).width);
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width);
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width);
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width);
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width);
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width);
break;
case WID_C_HAS_PASSWORD:
*size = maxdim(*size, GetSpriteSize(SPR_LOCK));
break;
@ -2607,6 +2623,11 @@ struct CompanyWindow : Window
ShowCompanyInfrastructure((CompanyID)this->window_number);
break;
case WID_C_GIVE_MONEY:
this->query_widget = WID_C_GIVE_MONEY;
ShowQueryString(STR_EMPTY, STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION, 30, this, CS_NUMERAL, QSF_NONE);
break;
case WID_C_BUY_SHARE:
DoCommandP(0, this->window_number, 0, CMD_BUY_SHARE_IN_COMPANY | CMD_MSG(STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS));
break;
@ -2663,6 +2684,14 @@ struct CompanyWindow : Window
switch (this->query_widget) {
default: NOT_REACHED();
case WID_C_GIVE_MONEY: {
Money money = (Money)(strtoull(str, nullptr, 10) / _currency->rate);
uint32 money_c = Clamp(ClampToI32(money), 0, 20000000); // Clamp between 20 million and 0
DoCommandP(0, money_c, this->window_number, CMD_GIVE_MONEY | CMD_MSG(STR_ERROR_CAN_T_GIVE_MONEY));
break;
}
case WID_C_PRESIDENT_NAME:
DoCommandP(0, 0, 0, CMD_RENAME_PRESIDENT | CMD_MSG(STR_ERROR_CAN_T_CHANGE_PRESIDENT), nullptr, str);
break;

@ -20,7 +20,6 @@
struct Window;
/* main_gui.cpp */
void HandleOnEditText(const char *str);
void InitializeGUI();
/* settings_gui.cpp */

@ -2122,7 +2122,6 @@ STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company
# Network client list
STR_NETWORK_CLIENTLIST_KICK :Kick
STR_NETWORK_CLIENTLIST_BAN :Ban
STR_NETWORK_CLIENTLIST_GIVE_MONEY :Give money
STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Speak to all
STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Speak to company
STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Private message
@ -2131,8 +2130,6 @@ STR_NETWORK_SERVER :Server
STR_NETWORK_CLIENT :Client
STR_NETWORK_SPECTATORS :Spectators
STR_NETWORK_GIVE_MONEY_CAPTION :{WHITE}Enter the amount of money you want to give
# Network set password
STR_COMPANY_PASSWORD_CANCEL :{BLACK}Do not save the entered password
STR_COMPANY_PASSWORD_OK :{BLACK}Give the company the new password
@ -2235,8 +2232,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {RAW_STRING
STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {RAW_STRING} has started a new company (#{2:NUM})
STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {RAW_STRING} has left the game ({2:STRING})
STR_NETWORK_MESSAGE_NAME_CHANGE :*** {RAW_STRING} has changed his/her name to {RAW_STRING}
STR_NETWORK_MESSAGE_GIVE_MONEY :*** {RAW_STRING} gave your company {2:CURRENCY_LONG}
STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY :*** You gave {1:RAW_STRING} {2:CURRENCY_LONG}
STR_NETWORK_MESSAGE_GIVE_MONEY :*** {RAW_STRING} gave {2:CURRENCY_LONG} to {1:RAW_STRING}
STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The server closed the session
STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait...
STR_NETWORK_MESSAGE_KICKED :*** {RAW_STRING} was kicked. Reason: ({RAW_STRING})
@ -3377,6 +3373,8 @@ STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Relocate
STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Rebuild company headquarters elsewhere for 1% cost of company value. Shift+Click shows estimated cost without relocating HQ
STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}Details
STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}View detailed infrastructure counts
STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Give money
STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}Give money to this company
STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}New Face
STR_COMPANY_VIEW_NEW_FACE_TOOLTIP :{BLACK}Select new face for manager
@ -3394,6 +3392,7 @@ STR_COMPANY_VIEW_SELL_SHARE_TOOLTIP :{BLACK}Sell 25%
STR_COMPANY_VIEW_COMPANY_NAME_QUERY_CAPTION :Company Name
STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_CAPTION :Manager's Name
STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION :Enter the amount of money you want to give
STR_BUY_COMPANY_MESSAGE :{WHITE}We are looking for a transport company to take-over our company.{}{}Do you want to purchase {COMPANY} for {CURRENCY_LONG}?
@ -4345,6 +4344,7 @@ STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}... no l
STR_ERROR_CURRENCY_REQUIRED :{WHITE}... {CURRENCY_LONG} required
STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}Can't repay loan...
STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}Can't give away money that is loaned from the bank...
STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}Can't give away money to this company...
STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}Can't buy company...
STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}Can't build company headquarters...
STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Can't buy 25% share in this company...

@ -48,45 +48,6 @@
#include "safeguards.h"
static int _rename_id = 1;
static int _rename_what = -1;
void CcGiveMoney(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
{
if (result.Failed() || !_settings_game.economy.give_money) return;
/* Inform the company of the action of one of its clients (controllers). */
char msg[64];
SetDParam(0, p2);
GetString(msg, STR_COMPANY_NAME, lastof(msg));
if (!_network_server) {
NetworkClientSendChat(NETWORK_ACTION_GIVE_MONEY, DESTTYPE_TEAM, p2, msg, p1);
} else {
NetworkServerSendChat(NETWORK_ACTION_GIVE_MONEY, DESTTYPE_TEAM, p2, msg, CLIENT_ID_SERVER, p1);
}
}
void HandleOnEditText(const char *str)
{
switch (_rename_what) {
case 3: { // Give money, you can only give money in excess of loan
const Company *c = Company::GetIfValid(_local_company);
if (c == nullptr) break;
Money money = min(c->money - c->current_loan, (Money)(strtoull(str, nullptr, 10) / _currency->rate));
uint32 money_c = Clamp(ClampToI32(money), 0, 20000000); // Clamp between 20 million and 0
/* Give 'id' the money, and subtract it from ourself */
DoCommandP(0, money_c, _rename_id, CMD_GIVE_MONEY | CMD_MSG(STR_ERROR_INSUFFICIENT_FUNDS), CcGiveMoney, str);
break;
}
default: NOT_REACHED();
}
_rename_id = _rename_what = -1;
}
/**
* This code is shared for the majority of the pushbuttons.
* Handles e.g. the pressing of a button (to build things), playing of click sound and sets certain parameters
@ -120,14 +81,6 @@ void CcPlaySound_EXPLOSION(const CommandCost &result, TileIndex tile, uint32 p1,
if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_12_EXPLOSION, tile);
}
void ShowNetworkGiveMoneyWindow(CompanyID company)
{
_rename_id = company;
_rename_what = 3;
ShowQueryString(STR_EMPTY, STR_NETWORK_GIVE_MONEY_CAPTION, 30, nullptr, CS_NUMERAL, QSF_NONE);
}
/**
* Zooms a viewport in a window in or out.
* @param how Zooming direction.

@ -236,38 +236,3 @@ CommandCost CmdChangeBankBalance(TileIndex tile, DoCommandFlag flags, uint32 p1,
CommandCost zero_cost(expenses_type, 0);
return zero_cost;
}
/**
* Transfer funds (money) from one company to another.
* To prevent abuse in multiplayer games you can only send money to other
* companies if you have paid off your loan (either explicitly, or implicitly
* given the fact that you have more money than loan).
* @param tile unused
* @param flags operation to perform
* @param p1 the amount of money to transfer; max 20.000.000
* @param p2 the company to transfer the money to
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdGiveMoney(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
if (!_settings_game.economy.give_money) return CMD_ERROR;
const Company *c = Company::Get(_current_company);
CommandCost amount(EXPENSES_OTHER, min((Money)p1, (Money)20000000LL));
CompanyID dest_company = (CompanyID)p2;
/* You can only transfer funds that is in excess of your loan */
if (c->money - c->current_loan < amount.GetCost() || amount.GetCost() < 0) return CMD_ERROR;
if (!_networking || !Company::IsValidID(dest_company)) return CMD_ERROR;
if (flags & DC_EXEC) {
/* Add money to company */
Backup<CompanyID> cur_company(_current_company, dest_company, FILE_LINE);
SubtractMoneyFromCompany(CommandCost(EXPENSES_OTHER, -amount.GetCost()));
cur_company.Restore();
}
/* Subtract money from local-company */
return amount;
}

@ -1047,13 +1047,9 @@ struct QueryStringWindow : public Window
void OnOk()
{
if (this->editbox.orig == nullptr || strcmp(this->editbox.text.buf, this->editbox.orig) != 0) {
/* If the parent is nullptr, the editbox is handled by general function
* HandleOnEditText */
if (this->parent != nullptr) {
this->parent->OnQueryTextFinished(this->editbox.text.buf);
} else {
HandleOnEditText(this->editbox.text.buf);
}
assert(this->parent != nullptr);
this->parent->OnQueryTextFinished(this->editbox.text.buf);
this->editbox.handled = true;
}
}
@ -1113,13 +1109,14 @@ static WindowDesc _query_string_desc(
* @param str StringID for the text shown in the textbox
* @param caption StringID of text shown in caption of querywindow
* @param maxsize maximum size in bytes or characters (including terminating '\0') depending on flags
* @param parent pointer to a Window that will handle the events (ok/cancel) of this
* window. If nullptr, results are handled by global function HandleOnEditText
* @param parent pointer to a Window that will handle the events (ok/cancel) of this window.
* @param afilter filters out unwanted character input
* @param flags various flags, @see QueryStringFlags
*/
void ShowQueryString(StringID str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
{
assert(parent != nullptr);
DeleteWindowByClass(WC_QUERY_STRING);
new QueryStringWindow(str, caption, ((flags & QSF_LEN_IN_CHARS) ? MAX_CHAR_LENGTH : 1) * maxsize, maxsize, &_query_string_desc, parent, afilter, flags);
}

@ -245,7 +245,7 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send,
break;
case NETWORK_ACTION_LEAVE: strid = STR_NETWORK_MESSAGE_CLIENT_LEFT; break;
case NETWORK_ACTION_NAME_CHANGE: strid = STR_NETWORK_MESSAGE_NAME_CHANGE; break;
case NETWORK_ACTION_GIVE_MONEY: strid = self_send ? STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY : STR_NETWORK_MESSAGE_GIVE_MONEY; break;
case NETWORK_ACTION_GIVE_MONEY: strid = STR_NETWORK_MESSAGE_GIVE_MONEY; break;
case NETWORK_ACTION_CHAT_COMPANY: strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY; break;
case NETWORK_ACTION_CHAT_CLIENT: strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT; break;
case NETWORK_ACTION_KICKED: strid = STR_NETWORK_MESSAGE_KICKED; break;

@ -1000,11 +1000,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHAT(Packet *p)
ci = NetworkClientInfo::GetByClientID(_network_own_client_id);
break;
/* For speaking to company or giving money, we need the company-name */
case NETWORK_ACTION_GIVE_MONEY:
if (!Company::IsValidID(ci_to->client_playas)) return NETWORK_RECV_STATUS_OKAY;
FALLTHROUGH;
/* For speaking to company, we need the company-name */
case NETWORK_ACTION_CHAT_COMPANY: {
StringID str = Company::IsValidID(ci_to->client_playas) ? STR_COMPANY_NAME : STR_NETWORK_SPECTATORS;
SetDParam(0, ci_to->client_playas);

@ -39,7 +39,7 @@ static CommandCallback * const _callback_table[] = {
/* 0x11 */ CcTerraform,
/* 0x12 */ CcAI,
/* 0x13 */ CcCloneVehicle,
/* 0x14 */ CcGiveMoney,
/* 0x14 */ nullptr,
/* 0x15 */ CcCreateGroup,
/* 0x16 */ CcFoundRandomTown,
/* 0x17 */ CcRoadStop,

@ -1632,8 +1632,7 @@ NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company)
}
/* The window below gives information about the connected clients
* and also makes able to give money to them, kick them (if server)
* and stuff like that. */
* and also makes able to kick them (if server) and stuff like that. */
extern void DrawCompanyIcon(CompanyID cid, int x, int y);
@ -1665,11 +1664,6 @@ static void ClientList_Ban(const NetworkClientInfo *ci)
NetworkServerKickOrBanIP(ci->client_id, true, nullptr);
}
static void ClientList_GiveMoney(const NetworkClientInfo *ci)
{
ShowNetworkGiveMoneyWindow(ci->client_playas);
}
static void ClientList_SpeakToClient(const NetworkClientInfo *ci)
{
ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, ci->client_id);
@ -1726,13 +1720,6 @@ struct NetworkClientListPopupWindow : Window {
}
this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, &ClientList_SpeakToAll);
if (_network_own_client_id != ci->client_id) {
/* We are no spectator and the company we want to give money to is no spectator and money gifts are allowed. */
if (Company::IsValidID(_local_company) && Company::IsValidID(ci->client_playas) && _settings_game.economy.give_money) {
this->AddAction(STR_NETWORK_CLIENTLIST_GIVE_MONEY, &ClientList_GiveMoney);
}
}
/* A server can kick clients (but not himself). */
if (_network_server && _network_own_client_id != ci->client_id) {
this->AddAction(STR_NETWORK_CLIENTLIST_KICK, &ClientList_Kick);

@ -16,7 +16,6 @@
#include "network_type.h"
void ShowNetworkNeedPassword(NetworkPasswordType npt);
void ShowNetworkGiveMoneyWindow(CompanyID company);
void ShowNetworkChatQueryWindow(DestType type, int dest);
void ShowJoinStatusWindow();
void ShowNetworkGameWindow();

@ -1382,9 +1382,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet *p)
NetworkClientInfo *ci = this->GetInfo();
switch (action) {
case NETWORK_ACTION_GIVE_MONEY:
if (!Company::IsValidID(ci->client_playas)) break;
FALLTHROUGH;
case NETWORK_ACTION_CHAT:
case NETWORK_ACTION_CHAT_CLIENT:
case NETWORK_ACTION_CHAT_COMPANY:

@ -46,6 +46,9 @@ enum CompanyWidgets {
WID_C_VIEW_INFRASTRUCTURE, ///< Panel about infrastructure.
WID_C_SELECT_GIVE_MONEY, ///< Selection widget for the give money button.
WID_C_GIVE_MONEY, ///< Button to give money.
WID_C_HAS_PASSWORD, ///< Has company password lock.
WID_C_SELECT_MULTIPLAYER, ///< Multiplayer selection panel.
WID_C_COMPANY_PASSWORD, ///< Button to set company password.

Loading…
Cancel
Save