Skip to content

Commit

Permalink
Dynamic price for omnisentryguns round 3 (#5702)
Browse files Browse the repository at this point in the history
# About the pull request

just copy of #5226 but gona
place it all here too

Adds dinamic price to sentrguns, starts at 300 and goes up 100 points
after each purchase. Aims to keep the anti spam meshures in place (makes
them stricter in fact), while allowing players to purchase few of them
for reasonable price.



# Explain why it's good for the game

Please before you put angry comments read this.

Omnisentryguns are not used at all, and that is a shame, due to the
changes that were were put in place to prevent sentryspam(they also
weakened the sentrgun a lot along the way, it was not just price change)
, but made them too expensive to buy just one.

This PR changes it by setting the initial price to 300 and it goes up
100 points after each purchase.
This makes it so that no pilot has reason to not buy at least one.
It is balanced around 3 sentrguns for 400 points each, 5 sentrguns 500
points each ( old price) and 11 sentryguns 800 points each(that means
for 8800 points, same as now), and if you try to buy more then current
roundstart maximum of 11, they will cost more then they do now on
avarage.

This should enshure that FTLs and engineers can count with 1- 3
sentryguns each round and plan acordingly, decide if they want some for
FOB, comms and hopfuly have one left to set up frontal outpost.

In terms of effect on xeno gameplay, the sentrygun is quite weak after
the nerfs, alone it can be taken down by basicly any two xenos with just
a bit of efford and fold when t3 or defender attacks it. That means it
serves the intended purpose, covering medics and engineers from skirmish
castes, while slightly helping agains bigger threats while it is part of
defended outpost.

Please mind that the sentryguns can not be moved, so when marines
overextend past it or are forced to retreat from it the sentry is good
as dead.

In terms of pilot gameplay it kind of nerfs experienced pilots who get
loads of calls as they will be expected to spend some points on
sentryguns and have less ammo as resoult and less expirienced pilots who
would not be able to make use of the ammo anyway are given another tool
that is easier to use and makes them contribute to the round.


# Testing Photographs and Procedure
<details>
<summary>Screenshots & Videos</summary>

Put screenshots and videos here with an empty line between the
screenshots and the `<details>` tags.

</details>


# Changelog
:cl:
balance: omnisentrygun price down to 300, but it goes up 100 points
after each purchase
/:cl:

---------

Co-authored-by: Zonespace <[email protected]>
Co-authored-by: vincibrv <[email protected]>
  • Loading branch information
3 people authored Feb 28, 2024
1 parent 85da46d commit bc444a7
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 87 deletions.
2 changes: 1 addition & 1 deletion code/modules/cm_marines/dropship_ammo.dm
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@
max_ammo_count = 1
ammo_name = "area denial sentry"
travelling_time = 0 // handled by droppod
point_cost = 800
point_cost = 800 //handled by printer
accuracy_range = 0 // pinpoint
max_inaccuracy = 0
/// Special structures it needs to break with drop pod
Expand Down
158 changes: 90 additions & 68 deletions code/modules/cm_marines/vehicle_part_fabricator.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
icon_state = "drone_fab_idle"
var/busy = FALSE
var/generate_points = TRUE
var/valid_parts = null
var/valid_ammo = null
var/omnisentry_price_scale = 100
var/omnisentry_price = 300

/obj/structure/machinery/part_fabricator/New()
..()
Expand All @@ -28,7 +28,8 @@

/obj/structure/machinery/part_fabricator/dropship/ui_data(mob/user)
return list(
"points" = get_point_store()
"points" = get_point_store(),
"omnisentrygun_price" = omnisentry_price
)

/obj/structure/machinery/part_fabricator/power_change()
Expand All @@ -53,11 +54,15 @@
/obj/structure/machinery/part_fabricator/proc/build_part(part_type, cost, mob/user)
set waitfor = 0
if(stat & NOPOWER) return
if(ispath(part_type,/obj/structure/ship_ammo/sentry))
cost = omnisentry_price
if(get_point_store() < cost)
to_chat(user, SPAN_WARNING("You don't have enough points to build that."))
return
visible_message(SPAN_NOTICE("[src] starts printing something."))
spend_point_store(cost)
if(ispath(part_type,/obj/structure/ship_ammo/sentry))
omnisentry_price += omnisentry_price_scale
icon_state = "drone_fab_active"
busy = TRUE
addtimer(CALLBACK(src, PROC_REF(do_build_part), part_type), 10 SECONDS)
Expand All @@ -68,7 +73,7 @@
new part_type(get_step(src, SOUTHEAST))
icon_state = "drone_fab_idle"

/obj/structure/machinery/part_fabricator/ui_act(action, params)
/obj/structure/machinery/part_fabricator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
Expand All @@ -77,27 +82,33 @@
to_chat(usr, SPAN_WARNING("The [name] is busy. Please wait for completion of previous operation."))
return

if(action == "produce")
var/produce = text2path(params["path"])
var/cost = text2num(params["cost"])
var/exploiting = TRUE

if (valid_parts && ispath(produce, valid_parts))
exploiting = FALSE
else if (valid_ammo && ispath(produce, valid_ammo))
exploiting = FALSE
var/mob/user = ui.user

if (cost < 0)
exploiting = TRUE
if(action == "produce")
var/cost = 0
var/is_ammo = params["is_ammo"]
var/index = params["index"]

if(is_ammo == 0)
var/obj/structure/dropship_equipment/produce = (typesof(/obj/structure/dropship_equipment))[index]
if(SSticker.mode && MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_COMBAT_CAS) && produce.combat_equipment)
log_admin("Bad topic: [user] may be trying to HREF exploit [src] to bypass no combat cas")
return
cost = initial(produce.point_cost)
build_part(produce, cost, user)
return

if (exploiting)
log_admin("Bad topic: [usr] may be trying to HREF exploit [src] with [produce], [cost]")
else
var/obj/structure/ship_ammo/produce = (typesof(/obj/structure/ship_ammo))[index]
if(SSticker.mode && MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_COMBAT_CAS) && produce.combat_equipment)
log_admin("Bad topic: [user] may be trying to HREF exploit [src] to bypass no combat cas")
return
cost = initial(produce.point_cost)
build_part(produce, cost, user)
return

build_part(produce, cost, usr)
return
else
log_admin("Bad topic: [usr] may be trying to HREF exploit [src]")
log_admin("Bad topic: [user] may be trying to HREF exploit [src]")
return

/obj/structure/machinery/part_fabricator/attack_hand(mob/user)
Expand All @@ -116,12 +127,11 @@
name = "dropship part fabricator"
desc = "A large automated 3D printer for producing dropship parts. You can recycle parts or ammo in it, and get 80% of your points back, by clicking it while holding them in a powerloader claw."
req_access = list(ACCESS_MARINE_DROPSHIP)
valid_parts = /obj/structure/dropship_equipment
valid_ammo = /obj/structure/ship_ammo

unslashable = TRUE
unacidable = TRUE


/obj/structure/machinery/part_fabricator/dropship/get_point_store()
return GLOB.supply_controller.dropship_points

Expand All @@ -134,86 +144,100 @@
/obj/structure/machinery/part_fabricator/dropship/ui_static_data(mob/user)
var/list/static_data = list()
static_data["Equipment"] = list()
var/is_ammo = 0
var/index = 1
for(var/build_type in typesof(/obj/structure/dropship_equipment))
var/obj/structure/dropship_equipment/DE = build_type
if(SSticker.mode && MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_COMBAT_CAS) && initial(DE.combat_equipment))
var/obj/structure/dropship_equipment/dropship_equipment_data = build_type
if(SSticker.mode && MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_COMBAT_CAS) && dropship_equipment_data.combat_equipment)
index += 1
continue
var/build_name = initial(DE.name)
var/build_description = initial(DE.desc)
var/build_cost = initial(DE.point_cost)
var/build_name = initial(dropship_equipment_data.name)
var/build_description = initial(dropship_equipment_data.desc)
var/build_cost = initial(dropship_equipment_data.point_cost)
if(build_cost)
static_data["Equipment"] += list(list(
"name" = capitalize_first_letters(build_name),
"desc" = build_description,
"path" = build_type,
"cost" = build_cost
"cost" = build_cost,
"index" = index,
"is_ammo" = is_ammo
))
index += 1

static_data["Ammo"] = list()
is_ammo = 1
index = 1
for(var/build_type in typesof(/obj/structure/ship_ammo))
var/obj/structure/ship_ammo/SA = build_type
if(SSticker.mode && MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_COMBAT_CAS) && initial(SA.combat_equipment))
var/obj/structure/ship_ammo/ship_ammo_data = build_type
if(SSticker.mode && MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_COMBAT_CAS) && ship_ammo_data.combat_equipment)
index = index + 1
continue
var/build_name = initial(SA.name)
var/build_description = initial(SA.desc)
var/build_cost = initial(SA.point_cost)
var/build_name = initial(ship_ammo_data.name)
var/build_description = initial(ship_ammo_data.desc)
var/build_cost = initial(ship_ammo_data.point_cost)
if(build_cost)
static_data["Ammo"] += list(list(
"name" = capitalize_first_letters(build_name),
"desc" = build_description,
"path" = build_type,
"cost" = build_cost
"cost" = build_cost,
"index" = index,
"is_ammo" = is_ammo
))
index += 1

return static_data

/obj/structure/machinery/part_fabricator/dropship/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/powerloader_clamp))
var/obj/item/powerloader_clamp/PC = I
recycle_equipment(PC, user)
var/obj/item/powerloader_clamp/powerloader_clamp_used = I
recycle_equipment(powerloader_clamp_used, user)
return
return ..()

/obj/structure/machinery/part_fabricator/dropship/proc/recycle_equipment(obj/item/powerloader_clamp/PC, mob/living/user)
if(!PC.loaded)
to_chat(user, SPAN_WARNING("There is nothing loaded in \the [PC]."))
/obj/structure/machinery/part_fabricator/dropship/proc/recycle_equipment(obj/item/powerloader_clamp/powerloader_clamp_used, mob/living/user)
if(!powerloader_clamp_used.loaded)
to_chat(user, SPAN_WARNING("There is nothing loaded in \the [powerloader_clamp_used]."))
return

var/recycle_points
if(istype(PC.loaded, /obj/structure/dropship_equipment))
var/obj/structure/dropship_equipment/SE = PC.loaded
recycle_points = SE.point_cost
else if(istype(PC.loaded, /obj/structure/ship_ammo))
var/obj/structure/ship_ammo/SE = PC.loaded
if(!SE.ammo_count)
to_chat(user, SPAN_WARNING("\The [SE] is empty!"))
if(istype(powerloader_clamp_used.loaded, /obj/structure/dropship_equipment))
var/obj/structure/dropship_equipment/sold_eqipment = powerloader_clamp_used.loaded
recycle_points = sold_eqipment.point_cost
else if(istype(powerloader_clamp_used.loaded, /obj/structure/ship_ammo))
var/obj/structure/ship_ammo/sold_eqipment = powerloader_clamp_used.loaded
if(!sold_eqipment.ammo_count)
to_chat(user, SPAN_WARNING("\The [sold_eqipment] is empty!"))
return
if(SE.ammo_count != SE.max_ammo_count)
recycle_points = (SE.point_cost * (SE.ammo_count / SE.max_ammo_count))
to_chat(user, SPAN_WARNING("\The [SE] is not fully loaded, and less points will be able to be refunded."))
if(sold_eqipment.ammo_count != sold_eqipment.max_ammo_count)
recycle_points = (sold_eqipment.point_cost * (sold_eqipment.ammo_count / sold_eqipment.max_ammo_count))
to_chat(user, SPAN_WARNING("\The [sold_eqipment] is not fully loaded, and less points will be able to be refunded."))
else
recycle_points = SE.point_cost
recycle_points = sold_eqipment.point_cost
if(istype(powerloader_clamp_used.loaded, /obj/structure/ship_ammo/sentry))
recycle_points = omnisentry_price - omnisentry_price_scale

if(!recycle_points)
to_chat(user, SPAN_WARNING("\The [PC.loaded] can't be recycled!"))
to_chat(user, SPAN_WARNING("\The [powerloader_clamp_used.loaded] can't be recycled!"))
return

var/thing_to_recycle = PC.loaded
to_chat(user, SPAN_WARNING("You start recycling \the [PC.loaded]!"))
var/thing_to_recycle = powerloader_clamp_used.loaded
to_chat(user, SPAN_WARNING("You start recycling \the [powerloader_clamp_used.loaded]!"))
playsound(loc, 'sound/machines/hydraulics_1.ogg', 40, 1)
if(!user || !do_after(user, (7 SECONDS) * user.get_skill_duration_multiplier(SKILL_ENGINEER), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE, PC.loaded, INTERRUPT_ALL))
if(!user || !do_after(user, (7 SECONDS) * user.get_skill_duration_multiplier(SKILL_ENGINEER), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE, powerloader_clamp_used.loaded, INTERRUPT_ALL))
to_chat(user, SPAN_NOTICE("You stop recycling \the [thing_to_recycle]."))
return
for(var/obj/thing as anything in PC.loaded)
if(istype(powerloader_clamp_used.loaded, /obj/structure/ship_ammo/sentry))
omnisentry_price -= omnisentry_price_scale
for(var/obj/thing as anything in powerloader_clamp_used.loaded)
thing.forceMove(loc) // no sentries popping out when we qdel please
qdel(thing)
qdel(PC.loaded)
PC.loaded = null
qdel(powerloader_clamp_used.loaded)
powerloader_clamp_used.loaded = null
to_chat(user, SPAN_NOTICE("You recycle \the [thing_to_recycle] into [src], and get back [round(recycle_points * 0.8)] points."))
msg_admin_niche("[key_name(user)] recycled a [thing_to_recycle] into \the [src] for [round(recycle_points * 0.8)] points.")
add_to_point_store(round(recycle_points * 0.8))
playsound(loc, 'sound/machines/fax.ogg', 40, 1)
PC.update_icon()
powerloader_clamp_used.update_icon()


// WARNING: IF YOU DECIDE TO READD THIS, GIVE THE HARDPOINTS POINT COSTS
Expand All @@ -223,8 +247,6 @@
desc = "A large automated 3D printer for producing vehicle parts."
req_access = list(ACCESS_MARINE_CREWMAN)
generate_points = FALSE
valid_parts = /obj/item/hardpoint
valid_ammo = /obj/item/ammo_magazine/hardpoint

unacidable = TRUE
indestructible = TRUE
Expand All @@ -242,9 +264,9 @@
var/list/static_data = list()
static_data["Equipment"] = list()
for(var/build_type in typesof(/obj/item/hardpoint))
var/obj/item/hardpoint/TE = build_type
var/build_name = initial(TE.name)
var/build_description = initial(TE.desc)
var/obj/item/hardpoint/hardpoint_data = build_type
var/build_name = initial(hardpoint_data.name)
var/build_description = initial(hardpoint_data.desc)
var/build_cost = 0
if(build_cost)
static_data["Equipment"] += list(list(
Expand All @@ -256,9 +278,9 @@

static_data["Ammo"] = list()
for(var/build_type in typesof(/obj/item/ammo_magazine/hardpoint))
var/obj/item/ammo_magazine/hardpoint/TA = build_type
var/build_name = initial(TA.name)
var/build_description = initial(TA.desc)
var/obj/item/ammo_magazine/hardpoint/ammo_data = build_type
var/build_name = initial(ammo_data.name)
var/build_description = initial(ammo_data.desc)
var/build_cost = 0
if(build_cost)
static_data["Ammo"] += list(list(
Expand Down
51 changes: 33 additions & 18 deletions tgui/packages/tgui/interfaces/PartFabricator.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ export const PartFabricator = (props) => {
);
};

const GeneralPanel = (props) => {
const { act, data } = useBackend();
const { points, Equipment, Ammo } = data;

const GeneralPanel = (props, context) => {
const { act, data } = useBackend(context);
const { points, omnisentrygun_price, Equipment, Ammo } = data;
return (
<div>
<Section>Points: {points}</Section>
Expand All @@ -37,8 +36,8 @@ const GeneralPanel = (props) => {
tooltipPosition="left"
onClick={() =>
act('produce', {
path: Equipment.path,
cost: Equipment.cost,
index: Equipment.index,
is_ammo: Equipment.is_ammo,
})
}
/>
Expand All @@ -57,18 +56,34 @@ const GeneralPanel = (props) => {
label={Ammo.name}
className="underline"
buttons={
<Button
content={'Fabricate (' + Ammo.cost + ')'}
icon="wrench"
tooltip={Ammo.desc}
tooltipPosition="left"
onClick={() =>
act('produce', {
path: Ammo.path,
cost: Ammo.cost,
})
}
/>
Ammo.name === 'A/C-49-P Air Deployable Sentry' ? (
<Button
content={'Fabricate (' + omnisentrygun_price + ')'}
icon="wrench"
tooltip={Ammo.desc}
tooltipPosition="left"
cost={omnisentrygun_price}
onClick={() =>
act('produce', {
index: Ammo.index,
is_ammo: Ammo.is_ammo,
})
}
/>
) : (
<Button
content={'Fabricate (' + Ammo.cost + ')'}
icon="wrench"
tooltip={Ammo.desc}
tooltipPosition="left"
onClick={() =>
act('produce', {
index: Ammo.index,
is_ammo: Ammo.is_ammo,
})
}
/>
)
}
/>
))}
Expand Down

0 comments on commit bc444a7

Please sign in to comment.