Skip to content

Commit

Permalink
[MIRROR] Paintings update: Curators get a cut on patronage + zoom in/…
Browse files Browse the repository at this point in the history
…out buttons on UI (#2066)

* Paintings update: Curators get a cut on patronage + zoom in/out buttons on UI (#81500)

## About The Pull Request
(Roundstart) Curators now get a 22.5% cut on credits spent on painting
patronages (divided by the number of curators). The service department
also gets another, 12.5% cut.

This PR also adds zoom in/out buttons to the painting canvas UI. So you
don't have to stare at a blob of such enormous squares while the UI is
open, which is only good when drawing.

Screenshot copypaste in paint:

## Why It's Good For The Game
The painting feature is mostly an end in itself, which is totally fine.
I've put quite a few quality-of-life changes into it through the years,
and I still want to kick in some stuff. However, I think the curator
should actually benefit from them in a more "mechanical" way.
Furthermore, I personally prefer them over the random written crap that
players make.

Also, as I said above, the canvas UI can feel a tad too big at times.

* Paintings update: Curators get a cut on patronage + zoom in/out buttons on UI

---------

Co-authored-by: NovaBot <[email protected]>
Co-authored-by: Ghom <[email protected]>
  • Loading branch information
3 people authored Feb 20, 2024
1 parent eb01546 commit c2bff1e
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 31 deletions.
2 changes: 2 additions & 0 deletions code/controllers/subsystem/economy.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ SUBSYSTEM_DEF(economy)
* A list of sole account datums can be obtained with flatten_list(), another variable would be redundant rn.
*/
var/list/bank_accounts_by_id = list()
/// A list of bank accounts indexed by their assigned job.
var/list/bank_accounts_by_job = list()
///List of the departmental budget cards in existance.
var/list/dep_cards = list()
/// A var that collects the total amount of credits owned in player accounts on station, reset and recounted on fire()
Expand Down
55 changes: 51 additions & 4 deletions code/modules/art/paintings.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#define MAX_PAINTING_ZOOM_OUT 3

///////////
// EASEL //
Expand Down Expand Up @@ -67,10 +68,13 @@
var/framed_offset_y = 10

/**
* How big the grid cells that compose the painting are in the UI.
* How big the grid cells that compose the painting are in the UI (multiplied by zoom).
* This impacts the size of the UI, so smaller values are generally better for bigger canvases and viceversa
*/
var/pixels_per_unit = 24
var/pixels_per_unit = 9

///A list that keeps track of the current zoom value for each current viewer.
var/list/zoom_by_observer

SET_BASE_PIXEL(11, 10)

Expand Down Expand Up @@ -118,10 +122,12 @@
/obj/item/canvas/ui_static_data(mob/user)
. = ..()
.["px_per_unit"] = pixels_per_unit
.["max_zoom"] = MAX_PAINTING_ZOOM_OUT

/obj/item/canvas/ui_data(mob/user)
. = ..()
.["grid"] = grid
.["zoom"] = LAZYACCESS(zoom_by_observer, user.key) || (finalized ? 1 : MAX_PAINTING_ZOOM_OUT)
.["name"] = painting_metadata.title
.["author"] = painting_metadata.creator_name
.["patron"] = painting_metadata.patron_name
Expand Down Expand Up @@ -202,6 +208,24 @@
if("patronage")
. = TRUE
patron(user)
if("zoom_in")
. = TRUE
LAZYINITLIST(zoom_by_observer)
if(!zoom_by_observer[user.key])
zoom_by_observer[user.key] = 2
else
zoom_by_observer[user.key] = min(zoom_by_observer[user.key] + 1, MAX_PAINTING_ZOOM_OUT)
if("zoom_out")
. = TRUE
LAZYINITLIST(zoom_by_observer)
if(!zoom_by_observer[user.key])
zoom_by_observer[user.key] = MAX_PAINTING_ZOOM_OUT - 1
else
zoom_by_observer[user.key] = max(zoom_by_observer[user.key] - 1, 1)

/obj/item/canvas/ui_close(mob/user)
. = ..()
LAZYREMOVE(zoom_by_observer, user.key)

/obj/item/canvas/proc/finalize(mob/user)
if(painting_metadata.loaded_from_json || finalized)
Expand All @@ -218,6 +242,9 @@

SStgui.update_uis(src)

#define CURATOR_PERCENTILE_CUT 0.225
#define SERVICE_PERCENTILE_CUT 0.125

/obj/item/canvas/proc/patron(mob/user)
if(!finalized || !isliving(user))
return
Expand Down Expand Up @@ -245,6 +272,19 @@
if(!account.adjust_money(-offer_amount, "Painting: Patron of [painting_metadata.title]"))
to_chat(user, span_warning("Transaction failure. Please try again."))
return

var/datum/bank_account/service_account = SSeconomy.get_dep_account(ACCOUNT_SRV)
service_account.adjust_money(offer_amount * SERVICE_PERCENTILE_CUT)
///We give the curator(s) a cut (unless they're themselves the patron), as it's their job to curate and promote art among other things.
var/list/curator_accounts = SSeconomy.bank_accounts_by_job[/datum/job/curator] - account
var/curators_length = length(curator_accounts)
if(curators_length)
var/curator_cut = round(offer_amount * CURATOR_PERCENTILE_CUT / curators_length)
if(curator_cut)
for(var/datum/bank_account/curator as anything in curator_accounts)
curator.adjust_money(curator_cut, "Painting: Patronage cut")
curator.bank_card_talk("Cut on patronage received, account now holds [curator.account_balance] cr.")

painting_metadata.patron_ckey = user.ckey
painting_metadata.patron_name = user.real_name
painting_metadata.credit_value = offer_amount
Expand All @@ -260,6 +300,9 @@
SStgui.close_uis(src) // Close the examine ui so that the radial menu doesn't end up covered by it and people don't get confused.
select_new_frame(user, possible_frames)

#undef CURATOR_PERCENTILE_CUT
#undef SERVICE_PERCENTILE_CUT

/obj/item/canvas/proc/select_new_frame(mob/user, list/candidates)
var/possible_frames = candidates || SSpersistent_paintings.get_available_frames(painting_metadata.credit_value)
var/list/radial_options = list()
Expand Down Expand Up @@ -386,6 +429,7 @@
SET_BASE_PIXEL(5, 7)
framed_offset_x = 5
framed_offset_y = 7
pixels_per_unit = 8

/obj/item/canvas/twentythree_twentythree
name = "canvas (23x23)"
Expand All @@ -395,6 +439,7 @@
SET_BASE_PIXEL(5, 5)
framed_offset_x = 5
framed_offset_y = 5
pixels_per_unit = 8

/obj/item/canvas/twentyfour_twentyfour
name = "canvas (24x24) (AI Universal Standard)"
Expand All @@ -405,6 +450,7 @@
SET_BASE_PIXEL(4, 4)
framed_offset_x = 4
framed_offset_y = 4
pixels_per_unit = 8

/obj/item/canvas/thirtysix_twentyfour
name = "canvas (36x24)"
Expand All @@ -415,7 +461,7 @@
SET_BASE_PIXEL(-4, 4)
framed_offset_x = 14
framed_offset_y = 4
pixels_per_unit = 20
pixels_per_unit = 7
w_class = WEIGHT_CLASS_BULKY

custom_price = PAYCHECK_CREW * 1.25
Expand All @@ -435,7 +481,7 @@
SET_BASE_PIXEL(-8, 2)
framed_offset_x = 9
framed_offset_y = 4
pixels_per_unit = 18
pixels_per_unit = 6
w_class = WEIGHT_CLASS_BULKY

custom_price = PAYCHECK_CREW * 1.75
Expand Down Expand Up @@ -798,3 +844,4 @@
current_color = chosen_color

#undef AVAILABLE_PALETTE_SPACE
#undef MAX_PAINTING_ZOOM_OUT
3 changes: 3 additions & 0 deletions code/modules/economy/account.dm
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
/datum/bank_account/Destroy()
if(add_to_accounts)
SSeconomy.bank_accounts_by_id -= "[account_id]"
SSeconomy.bank_accounts_by_job[account_job] -= src
QDEL_LIST(redeemed_coupons)
return ..()

Expand All @@ -70,6 +71,8 @@
if(SSeconomy.bank_accounts_by_id["[account_id]"])
stack_trace("Unable to find a unique account ID, substituting currently existing account of id [account_id].")
SSeconomy.bank_accounts_by_id["[account_id]"] = src
if(account_job)
LAZYADD(SSeconomy.bank_accounts_by_job[account_job], src)

/datum/bank_account/vv_edit_var(var_name, var_value) // just so you don't have to do it manually
var/old_id = account_id
Expand Down
1 change: 1 addition & 0 deletions strings/tips.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ As a Cultist, check the alert in the upper-right of your screen for all the deta
As a Cultist, do not cause too much chaos before your objective is completed. If the shuttle gets called too soon, you may not have enough time to win.
As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists of Nar'Sie nearby, but will create a fire where the rune stands on use.
As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face.
As a Curator, you earn a 22% cut (divided by number of curators) of all credits spent on painting patronages. Turn others' patience and artistry skills into your own income!
As a Cyborg, choose your model carefully, as only cutting and mending your reset wire will let you re-pick it. If possible, refrain from choosing a model until a situation that requires one occurs.
As a Cyborg, you are extremely vulnerable to EMPs as EMPs both stun you and damage you. The ion rifle in the armory or a traitor with an EMP kit can kill you in seconds.
As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes can easily stunlock you and you cannot do any precision work as you lack hands.
Expand Down
84 changes: 57 additions & 27 deletions tgui/packages/tgui/interfaces/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type PaintCanvasProps = Partial<{
drawing_color: string | null;
has_palette: boolean;
show_grid: boolean;
zoom: number;
}>;

type PointData = {
Expand All @@ -45,13 +46,15 @@ class PaintCanvas extends Component<PaintCanvasProps> {
onCanvasDropper: (x: number, y: number) => void;
drawing: boolean;
drawing_color: string;
zoom: number;

constructor(props) {
super(props);
this.canvasRef = createRef<HTMLCanvasElement>();
this.modifiedElements = [];
this.is_grid_shown = false;
this.drawing = false;
this.zoom = props.zoom;
this.onCanvasModified = props.onCanvasModifiedHandler;
this.onCanvasDropper = props.onCanvasDropperHandler;

Expand All @@ -67,8 +70,12 @@ class PaintCanvas extends Component<PaintCanvasProps> {
}

componentDidUpdate() {
if (this.zoom !== this.props.zoom) {
this.prepareCanvas();
this.syncCanvas();
}
// eslint-disable-next-line max-len
if (
else if (
(this.props.value !== undefined &&
JSON.stringify(this.baseImageData) !==
JSON.stringify(fromDM(this.props.value))) ||
Expand All @@ -79,6 +86,7 @@ class PaintCanvas extends Component<PaintCanvasProps> {
}

prepareCanvas() {
this.zoom = this.props.zoom as number;
const canvas = this.canvasRef.current!;
const ctx = canvas.getContext('2d');
const width = this.props.width || canvas.width || 360;
Expand Down Expand Up @@ -242,22 +250,24 @@ type CanvasData = {
date: string | null;
show_plaque: boolean;
show_grid: boolean;
zoom: number;
max_zoom: number;
};

export const Canvas = (props) => {
const { act, data } = useBackend<CanvasData>();
const [width, height] = getImageSize(data.grid);
const scaled_width = width * data.px_per_unit;
const scaled_height = height * data.px_per_unit;
const scaled_width = width * data.px_per_unit * data.zoom;
const scaled_height = height * data.px_per_unit * data.zoom;
const average_plaque_height = 90;
const palette_height = 44;
const palette_height = 38;
const griddy = !!data.show_grid && !!data.editable && !!data.paint_tool_color;
return (
<Window
width={scaled_width + 72}
width={Math.max(scaled_width + 72, 262)}
height={
scaled_height +
75 +
80 +
(data.show_plaque ? average_plaque_height : 0) +
(data.editable && data.paint_tool_palette ? palette_height : 0)
}
Expand All @@ -269,13 +279,12 @@ export const Canvas = (props) => {
<Tooltip
content={
multiline`
You can Right-Click the canvas to change the color of
the painting tool to that of the clicked pixel.
Right-Click a pixel on the canvas to copy its color.
` +
(data.editable
? multiline`
\n You can also select a color from the
palette at the bottom of the UI,
\n Left-Click the palette at the
bottom of the UI to select a color,
or input a new one with Right-Click.
`
: '')
Expand All @@ -296,26 +305,47 @@ export const Canvas = (props) => {
/>
</Flex.Item>
)}
<Flex.Item>
<Button
tooltip="Zoom Out"
icon="search-minus"
disabled={data.zoom <= 1}
onClick={() => act('zoom_out')}
m={0.5}
/>
</Flex.Item>
<Flex.Item>
<Button
tooltip="Zoom In"
icon="search-plus"
disabled={data.zoom >= data.max_zoom}
onClick={() => act('zoom_in')}
m={0.5}
/>
</Flex.Item>
</Flex>
<Box textAlign="center">
<PaintCanvas
value={data.grid}
imageWidth={width}
imageHeight={height}
width={scaled_width}
height={scaled_height}
drawing_color={data.paint_tool_color}
show_grid={griddy}
onCanvasModifiedHandler={(changed) =>
act('paint', { data: toMassPaintFormat(changed) })
}
onCanvasDropperHandler={(x, y) =>
act('select_color_from_coords', { px: x, py: y })
}
editable={data.editable}
has_palette={!!data.paint_tool_palette}
/>
<Flex align="center" justify="center" direction="column">
<Flex.Item>
<PaintCanvas
value={data.grid}
imageWidth={width}
imageHeight={height}
width={scaled_width}
height={scaled_height}
drawing_color={data.paint_tool_color}
show_grid={griddy}
zoom={data.zoom}
onCanvasModifiedHandler={(changed) =>
act('paint', { data: toMassPaintFormat(changed) })
}
onCanvasDropperHandler={(x, y) =>
act('select_color_from_coords', { px: x, py: y })
}
editable={data.editable}
has_palette={!!data.paint_tool_palette}
/>
</Flex.Item>
{!!data.editable && !!data.paint_tool_palette && (
<Flex.Item>
{data.paint_tool_palette.map((element, index) => (
Expand Down

0 comments on commit c2bff1e

Please sign in to comment.