OpenTTD

Tasklist

FS#5697 - Give GS control over buy and build action of the user

Attached to Project: OpenTTD
Opened by Ferenc Kiss (idioty) - Monday, 05 August 2013, 06:37 GMT
Last edited by andythenorth (andythenorth) - Friday, 18 August 2017, 22:11 GMT
Type Feature Request
Category Script → Goal/Game script
Status Closed
Assigned To andythenorth (andythenorth)
Operating System All
Severity Low
Priority Normal
Reported Version trunk
Due in Version Undecided
Due Date Undecided
Percent Complete 100%
Votes 0
Private No

Details

I want an overwrite function, like Save() or Load(version, table), but
like these:
CanPurshase(companyid, purchaseinfo) or CanBuild(companyid, buildinfo), and return true or false, before the game show the object (and before take the mony).

Example:
In my script companies can't purchase more than five truck. If a company complete any goal, become "truck" reward, and the compnay can purchase more two truck.
This task depends upon

Closed by  andythenorth (andythenorth)
Friday, 18 August 2017, 22:11 GMT
Reason for closing:  Won't implement
Additional comments about closing:  The OP's request isn't implementable as requested, GS is too slow / deliberately limited to handle player actions on an event bubbling basis. Zuu has suggested alternatives, but likely doesn't need this issue open to develop those further (if at all). Closing as part of Flyspray clean up. Thanks :)
Comment by Leif Linse (Zuu) - Friday, 16 August 2013, 18:24 GMT
If this is implemented using direct callbacks from OpenTTD, this has the consequence that you can probably not call DoCommands in CanPurshase to display an error message when something is denied. Your GS could have a message queue to which CanPurshase can push an error. Then back in the main-loop, you can detect a queue > 0 and display all queued errors and possible send an error to AIs.

Another consequence is that you will only have X number of oopcodes to provide an answer. If you overshot this, OpenTTD will need to crash your GS to avoid that a GS could hang OpenTTD.

It may be possible to implement it such that the GS may return True, False or a GSText with a custom error message for human players. Showing this error message only to the client that tried to execute the action is possible non-trivial. That said it is probably easier on the OpenTTD side than on GS side to make sure the error message is only shown for a specific client in the company. The returned GSText will however be of little use for AIs. So another option is to provide a few built-in error enums so that AIs can call GetLastError and get a bit more clue than just "script denied action".
Comment by Ferenc Kiss (idioty) - Saturday, 17 August 2013, 10:13 GMT
So, if my script denied the purchase, then send back a GSText, but if company can purchase the thing, my script send back null? Yes, this is more useful. :)
Comment by Ferenc Kiss (idioty) - Thursday, 12 September 2013, 07:49 GMT
I thought about the issue.
If the return type would be an class or table or enum with "GSText with a custom error message for human players" and "built-in error enums so that AIs can call GetLastError "?
But built-in errors must be contain an option: these type of vehicle (or station, etc...) can not purchase any more, or must contain a number, the AI know how much can buy these.
example: -2: can not purchase any more, -1: AI can buy any of number, 0: yet can not buy, and above the number of AI can purchase.

I hope you understand...
Comment by Leif Linse (Zuu) - Saturday, 14 September 2013, 09:59 GMT
An alternative solution that looks more appealing to me is (I will give pros/minus at the end):

/*
* Set the default number of allowed vehicles of the given
* engine id that a company is allowed to buy.
* This value is automatically applied to new engines when
* they become available.
* @param value The vehicle limit. Set to 0 to disable engines by default. Set to NO_LIMIT if you don't want any limit.
*/
GSEngine.SetDefaultAllowedVehicleCount(value)

/*
* Set the number of allowed vehicles of the given engine id
* that a company is allowed to buy.
* @param company_id The company that the limit should be set for or COMPANY_INVALID to update all companies.
* @param engine_id Which engine to update the vehicle limit.
* @param value The new vehicle count limit. Set to 0 to disable the engine completely. Set to NO_LIMIT if you don't want any limit.
*/
GSEngine.SetAllowedVehicleCount(company_id, engine_id, value)


Benefits (compared to a callback):
+ When a limit is changed we can invalidate all affected buy vehicle windows so that we can give visual information to players which engines they can build.
+ When a vehicle with limit 0 is changed to > 0, we can show the new vehicle news automatically without the GS needing to do anything.
+ It is cheap to check the limit of a vehicle to provide visual information to players about the limits
+ We avoid any problems related to a GS providing different information when you open the Buy window and when you click to buy a vehicle. (if the GS change its mind between these two points we get informed and can update the window)

Drawback:
- Concepts where the total limit is not per engine type or when the total is not per company are harder to enforce with this API design. The GS will not have the chance against a player to react on a vehicle being bought and reduce the other limits before a fast player buy a second engine.

Comment by Ferenc Kiss (idioty) - Saturday, 14 September 2013, 12:29 GMT
I like this: GSEngine.SetAllowedVehicleCount(company_id, engine_id, value)
But
"The GS will not have the chance against a player to react on a vehicle being bought and reduce the other limits before a fast player buy a second engine."
for this reason, I think this is the right solution: if these method implemented in script:
CanPurshase(companyid, purchaseinfo) or CanBuild(companyid, buildinfo)
Comment by Leif Linse (Zuu) - Saturday, 14 September 2013, 13:23 GMT
True, but you will then lose all benefits with having a API where the GS declare the limits to OpenTTD. I would like to have a more constructive and open discussion where it is possible to see benefits and drawbacks of each solutions. Both for GS and for OpenTTD.

A drawback with the callback is that it will be easier for a GS to mess up with multiple execution points in the code. What if the main "thread" did half-update the data used by the callback? Yes, it is possible to write the code as to reduce this problem but not all novice GS authors will be aware of it.

A drawback with callbacks is that they can do random things that are hard for other parts of the OpenTTD eco system to cope with. As a script author you should be aware of the different interesting things that NewGRF callbacks can cause. (eg. a vehicle with different properties than told by [AI|GS]Engine etc. etc.)

Another drawback with callbacks is multiplayer. Codewise it means that a human client need to ask the server over the network for which engines that it can buy when populating a vehicle purchase list. The purchase list must then have a non-populated state. These things makes this path tricky to implement on the OpenTTD side as well.
Comment by Ferenc Kiss (idioty) - Saturday, 14 September 2013, 20:13 GMT
You're right, I did not thik about these problems.
It will remain: GSEngine.SetAllowedVehicleCount(company_id, engine_id, value)
I thought also about this problem: "The GS will not have the chance against a player to react on a vehicle being bought and reduce the other limits before a fast player buy a second engine."
Only problem is if the player can choose between two or more engine, and it can buy only one engine.
A possible solution:
exp: GSEngine.SetChooseVehicles(company_id, engine_list)
and the OpenTTD game know: the company can purchase only one of these type of engine, and if the company bought, the company can't purchase other engine from the list (or the OpenTTD reset the previous state).

Loading...