Merge remote-tracking branch 'origin/master' into hardsync-1.5
This commit is contained in:
@@ -54,24 +54,7 @@
|
||||
output += "<p>[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]</p>"
|
||||
|
||||
if(!IsGuestKey(src.key))
|
||||
if (SSdbcore.Connect())
|
||||
var/isadmin = 0
|
||||
if(src.client && src.client.holder)
|
||||
isadmin = 1
|
||||
var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")")
|
||||
var/rs = REF(src)
|
||||
if(query_get_new_polls.Execute())
|
||||
var/newpoll = 0
|
||||
if(query_get_new_polls.NextRow())
|
||||
newpoll = 1
|
||||
|
||||
if(newpoll)
|
||||
output += "<p><b><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A> (NEW!)</b></p>"
|
||||
else
|
||||
output += "<p><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A></p>"
|
||||
qdel(query_get_new_polls)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
output += playerpolls()
|
||||
|
||||
output += "</center>"
|
||||
|
||||
@@ -81,6 +64,41 @@
|
||||
popup.set_content(output)
|
||||
popup.open(FALSE)
|
||||
|
||||
/mob/dead/new_player/proc/playerpolls()
|
||||
var/output = "" //hey tg why is this a list?
|
||||
if (SSdbcore.Connect())
|
||||
var/isadmin = FALSE
|
||||
if(client?.holder)
|
||||
isadmin = TRUE
|
||||
var/datum/db_query/query_get_new_polls = SSdbcore.NewQuery({"
|
||||
SELECT id FROM [format_table_name("poll_question")]
|
||||
WHERE (adminonly = 0 OR :isadmin = 1)
|
||||
AND Now() BETWEEN starttime AND endtime
|
||||
AND deleted = 0
|
||||
AND id NOT IN (
|
||||
SELECT pollid FROM [format_table_name("poll_vote")]
|
||||
WHERE ckey = :ckey
|
||||
AND deleted = 0
|
||||
)
|
||||
AND id NOT IN (
|
||||
SELECT pollid FROM [format_table_name("poll_textreply")]
|
||||
WHERE ckey = :ckey
|
||||
AND deleted = 0
|
||||
)
|
||||
"}, list("isadmin" = isadmin, "ckey" = ckey))
|
||||
var/rs = REF(src)
|
||||
if(!query_get_new_polls.Execute())
|
||||
qdel(query_get_new_polls)
|
||||
return
|
||||
if(query_get_new_polls.NextRow())
|
||||
output += "<p><b><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A> (NEW!)</b></p>"
|
||||
else
|
||||
output += "<p><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A></p>"
|
||||
qdel(query_get_new_polls)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
return output
|
||||
|
||||
/mob/dead/new_player/proc/age_gate()
|
||||
var/list/dat = list("<center>")
|
||||
dat += "Enter your date of birth here, to confirm that you are over 18.<BR>"
|
||||
@@ -123,71 +141,76 @@
|
||||
if(!client.set_db_player_flags())
|
||||
message_admins("Blocked [src] from new player panel because age gate could not access player database flags.")
|
||||
return FALSE
|
||||
else
|
||||
var/dbflags = client.prefs.db_flags
|
||||
if(dbflags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE) //they have not completed age gate
|
||||
var/age_verification = age_gate()
|
||||
if(age_verification != 1)
|
||||
client.add_system_note("Automated-Age-Gate", "Failed automatic age gate process.")
|
||||
//ban them and kick them
|
||||
|
||||
//parameters used by sql line, easier to read:
|
||||
var/bantype_str = "ADMIN_PERMABAN"
|
||||
var/reason = "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification."
|
||||
var/duration = -1
|
||||
var/sql_ckey = sanitizeSQL(client.ckey)
|
||||
var/computerid = client.computer_id
|
||||
if(!computerid)
|
||||
computerid = "0"
|
||||
var/sql_computerid = sanitizeSQL(computerid)
|
||||
var/ip = client.address
|
||||
if(!ip)
|
||||
ip = "0.0.0.0"
|
||||
var/sql_ip = sanitizeSQL(ip)
|
||||
if(!(client.prefs.db_flags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE)) //completed? Skip
|
||||
return TRUE
|
||||
|
||||
//parameter not used as there's no job but i want to fill out all parameters for the insert line
|
||||
var/sql_job
|
||||
var/age_verification = age_gate()
|
||||
//ban them and kick them
|
||||
if(age_verification != 1)
|
||||
// this isn't code, this is paragraphs.
|
||||
var/player_ckey = ckey(client.ckey)
|
||||
// record all admins and non-admins online at the time
|
||||
var/list/clients_online = GLOB.clients.Copy()
|
||||
var/list/admins_online = GLOB.admins.Copy() //list() // remove the GLOB.admins.Copy() and the comments if you want the pure admins_online check
|
||||
// for(var/client/C in clients_online)
|
||||
// if(C.holder) //deadmins aren't included since they wouldn't show up on adminwho
|
||||
// admins_online += C
|
||||
var/who = clients_online.Join(", ")
|
||||
var/adminwho = admins_online.Join(", ")
|
||||
|
||||
// these are typically the banning admin's, but it's the system so we leave them null, but they're still here for the sake of a full set of values
|
||||
var/sql_a_ckey
|
||||
var/sql_a_computerid
|
||||
var/sql_a_ip
|
||||
var/datum/db_query/query_add_ban = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("ban")]
|
||||
(bantime, server_ip, server_port , round_id, bantype, reason, job, duration, expiration_time, ckey, computerid, ip, a_ckey, a_computerid, a_ip, who, adminwho)
|
||||
VALUES (Now(), INET_ATON(:server_ip), :server_port, :round_id, :bantype_str, :reason, :role, :duration, Now() + INTERVAL :duration MINUTE, :ckey, :computerid, INET_ATON(:ip), :a_ckey, :a_computerid, INET_ATON(:a_ip), :who, :adminwho)"},
|
||||
list(
|
||||
// Server info
|
||||
"server_ip" = world.internet_address || 0,
|
||||
"server_port" = world.port,
|
||||
"round_id" = GLOB.round_id,
|
||||
// Client ban info
|
||||
"bantype_str" = "ADMIN_PERMABAN",
|
||||
"reason" = "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification.",
|
||||
"role" = null,
|
||||
"duration" = -1,
|
||||
"ckey" = player_ckey,
|
||||
"ip" = client.address || null,
|
||||
"computerid" = client.computer_id || null,
|
||||
// Admin banning info
|
||||
"a_ckey" = "SYSTEM (Automated-Age-Gate)", // the server
|
||||
"a_ip" = null, //key_name
|
||||
"a_computerid" = "0",
|
||||
"who" = who,
|
||||
"adminwho" = adminwho
|
||||
))
|
||||
|
||||
// record all admins and non-admins online at the time
|
||||
var/who
|
||||
for(var/client/C in GLOB.clients)
|
||||
if(!who)
|
||||
who = "[C]"
|
||||
else
|
||||
who += ", [C]"
|
||||
client.add_system_note("Automated-Age-Gate", "Failed automatic age gate process.")
|
||||
if(!query_add_ban.Execute())
|
||||
// this is the part where you should panic.
|
||||
qdel(query_add_ban)
|
||||
message_admins("WARNING! Failed to ban [ckey] for failing the automatic age gate.")
|
||||
send2tgs_adminless_only("WARNING! Failed to ban [ckey] for failing the automatic age gate.")
|
||||
qdel(client)
|
||||
return FALSE
|
||||
qdel(query_add_ban)
|
||||
|
||||
var/adminwho
|
||||
for(var/client/C in GLOB.admins)
|
||||
if(!adminwho)
|
||||
adminwho = "[C]"
|
||||
else
|
||||
adminwho += ", [C]"
|
||||
create_message("note", player_ckey, "SYSTEM (Automated-Age-Gate)", "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification.", null, null, 0, 0, null, 0, "high")
|
||||
|
||||
var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[sql_job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[sql_ckey]', '[sql_computerid]', INET_ATON('[sql_ip]'), '[sql_a_ckey]', '[sql_a_computerid]', INET_ATON('[sql_a_ip]'), '[who]', '[adminwho]')"
|
||||
var/datum/DBQuery/query_add_ban = SSdbcore.NewQuery(sql)
|
||||
qdel(query_add_ban)
|
||||
// announce this
|
||||
message_admins("[ckey] has been banned for failing the automatic age gate.")
|
||||
send2tgs_adminless_only("[ckey] has been banned for failing the automatic age gate.")
|
||||
|
||||
// announce this
|
||||
message_admins("[html_encode(client.ckey)] has been banned for failing the automatic age gate.")
|
||||
send2irc("[html_encode(client.ckey)] has been banned for failing the automatic age gate.")
|
||||
// removing the client disconnects them
|
||||
qdel(client)
|
||||
|
||||
// removing the client disconnects them
|
||||
qdel(client)
|
||||
return FALSE
|
||||
|
||||
//they claim to be of age, so allow them to continue and update their flags
|
||||
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_COMPLETE, TRUE)
|
||||
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_INCOMPLETE, FALSE)
|
||||
//log this
|
||||
message_admins("[ckey] has joined through the automated age gate process.")
|
||||
|
||||
return FALSE
|
||||
else
|
||||
//they claim to be of age, so allow them to continue and update their flags
|
||||
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_COMPLETE, TRUE)
|
||||
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_INCOMPLETE, FALSE)
|
||||
//log this
|
||||
message_admins("[ckey] has joined through the automated age gate process.")
|
||||
return TRUE
|
||||
return TRUE
|
||||
|
||||
/mob/dead/new_player/Topic(href, href_list[])
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
|
||||
/mob/dead/new_player/proc/handle_player_polling()
|
||||
if(!SSdbcore.IsConnected())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
var/datum/DBQuery/query_poll_get = SSdbcore.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() BETWEEN starttime AND endtime [(client.holder ? "" : "AND adminonly = false")]")
|
||||
var/datum/db_query/query_poll_get = SSdbcore.NewQuery({"
|
||||
SELECT id, question
|
||||
FROM [format_table_name("poll_question")]
|
||||
WHERE Now() BETWEEN starttime AND endtime [(client.holder ? "" : "AND adminonly = false")]
|
||||
"})
|
||||
if(!query_poll_get.warn_execute())
|
||||
qdel(query_poll_get)
|
||||
return
|
||||
@@ -27,9 +31,15 @@
|
||||
if(!pollid)
|
||||
return
|
||||
if (!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
var/datum/DBQuery/query_poll_get_details = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid]")
|
||||
var/datum/db_query/query_poll_get_details = SSdbcore.NewQuery({"
|
||||
SELECT starttime, endtime, question, polltype, multiplechoiceoptions
|
||||
FROM [format_table_name("poll_question")]
|
||||
WHERE id = :id
|
||||
"}, list(
|
||||
"id" = pollid
|
||||
))
|
||||
if(!query_poll_get_details.warn_execute())
|
||||
qdel(query_poll_get_details)
|
||||
return
|
||||
@@ -47,7 +57,14 @@
|
||||
qdel(query_poll_get_details)
|
||||
switch(polltype)
|
||||
if(POLLTYPE_OPTION)
|
||||
var/datum/DBQuery/query_option_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
|
||||
var/datum/db_query/query_option_get_votes = SSdbcore.NewQuery({"
|
||||
SELECT optionid
|
||||
FROM [format_table_name("poll_vote")]
|
||||
WHERE pollid = :id AND ckey = :ckey
|
||||
"}, list(
|
||||
"id" = pollid,
|
||||
"ckey" = ckey
|
||||
))
|
||||
if(!query_option_get_votes.warn_execute())
|
||||
qdel(query_option_get_votes)
|
||||
return
|
||||
@@ -56,7 +73,13 @@
|
||||
votedoptionid = text2num(query_option_get_votes.item[1])
|
||||
qdel(query_option_get_votes)
|
||||
var/list/datum/polloption/options = list()
|
||||
var/datum/DBQuery/query_option_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
|
||||
var/datum/db_query/query_option_options = SSdbcore.NewQuery({"
|
||||
SELECT id, text
|
||||
FROM [format_table_name("poll_option")]
|
||||
WHERE pollid = :id
|
||||
"}, list(
|
||||
"id" = pollid
|
||||
))
|
||||
if(!query_option_options.warn_execute())
|
||||
qdel(query_option_options)
|
||||
return
|
||||
@@ -92,7 +115,14 @@
|
||||
src << browse(null ,"window=playerpolllist")
|
||||
src << browse(output,"window=playerpoll;size=500x250")
|
||||
if(POLLTYPE_TEXT)
|
||||
var/datum/DBQuery/query_text_get_votes = SSdbcore.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
|
||||
var/datum/db_query/query_text_get_votes = SSdbcore.NewQuery({"
|
||||
SELECT replytext
|
||||
FROM [format_table_name("poll_textreply")]
|
||||
WHERE pollid = :id AND ckey = :ckey
|
||||
"}, list(
|
||||
"id" = pollid,
|
||||
"ckey" = ckey
|
||||
))
|
||||
if(!query_text_get_votes.warn_execute())
|
||||
qdel(query_text_get_votes)
|
||||
return
|
||||
@@ -120,7 +150,13 @@
|
||||
src << browse(null ,"window=playerpolllist")
|
||||
src << browse(output,"window=playerpoll;size=500x500")
|
||||
if(POLLTYPE_RATING)
|
||||
var/datum/DBQuery/query_rating_get_votes = SSdbcore.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, [format_table_name("poll_vote")] v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid")
|
||||
var/datum/db_query/query_rating_get_votes = SSdbcore.NewQuery({"
|
||||
SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, [format_table_name("poll_vote")] v
|
||||
WHERE o.pollid = :id AND v.ckey = :ckey AND o.id = v.optionid
|
||||
"}, list(
|
||||
"id" = pollid,
|
||||
"ckey" = ckey
|
||||
))
|
||||
if(!query_rating_get_votes.warn_execute())
|
||||
qdel(query_rating_get_votes)
|
||||
return
|
||||
@@ -140,7 +176,13 @@
|
||||
output += "<input type='hidden' name='votetype' value=[POLLTYPE_RATING]>"
|
||||
var/minid = 999999
|
||||
var/maxid = 0
|
||||
var/datum/DBQuery/query_rating_options = SSdbcore.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
|
||||
var/datum/db_query/query_rating_options = SSdbcore.NewQuery({"
|
||||
SELECT id, text, minval, maxval, descmin, descmid, descmax
|
||||
FROM [format_table_name("poll_option")]
|
||||
WHERE pollid = :id
|
||||
"}, list(
|
||||
"id" = pollid
|
||||
))
|
||||
if(!query_rating_options.warn_execute())
|
||||
qdel(query_rating_options)
|
||||
return
|
||||
@@ -177,7 +219,14 @@
|
||||
src << browse(null ,"window=playerpolllist")
|
||||
src << browse(output,"window=playerpoll;size=500x500")
|
||||
if(POLLTYPE_MULTI)
|
||||
var/datum/DBQuery/query_multi_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
|
||||
var/datum/db_query/query_multi_get_votes = SSdbcore.NewQuery({"
|
||||
SELECT optionid
|
||||
FROM [format_table_name("poll_vote")]
|
||||
WHERE pollid = :id AND ckey = :ckey
|
||||
"}, list(
|
||||
"id" = pollid,
|
||||
"ckey" = ckey
|
||||
))
|
||||
if(!query_multi_get_votes.warn_execute())
|
||||
qdel(query_multi_get_votes)
|
||||
return
|
||||
@@ -188,7 +237,13 @@
|
||||
var/list/datum/polloption/options = list()
|
||||
var/maxoptionid = 0
|
||||
var/minoptionid = 0
|
||||
var/datum/DBQuery/query_multi_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
|
||||
var/datum/db_query/query_multi_options = SSdbcore.NewQuery({"
|
||||
SELECT id, text
|
||||
FROM [format_table_name("poll_option")]
|
||||
WHERE pollid = :id
|
||||
"}, list(
|
||||
"id" = pollid
|
||||
))
|
||||
if(!query_multi_options.warn_execute())
|
||||
qdel(query_multi_options)
|
||||
return
|
||||
@@ -231,8 +286,10 @@
|
||||
if(POLLTYPE_IRV)
|
||||
var/datum/asset/irv_assets = get_asset_datum(/datum/asset/group/irv)
|
||||
irv_assets.send(src)
|
||||
|
||||
var/datum/DBQuery/query_irv_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
|
||||
var/datum/db_query/query_irv_get_votes = SSdbcore.NewQuery({"
|
||||
SELECT optionid FROM [format_table_name("poll_vote")]
|
||||
WHERE pollid = :pollid AND ckey = :ckey AND deleted = 0
|
||||
"}, list("pollid" = pollid, "ckey" = ckey))
|
||||
if(!query_irv_get_votes.warn_execute())
|
||||
qdel(query_irv_get_votes)
|
||||
return
|
||||
@@ -244,7 +301,13 @@
|
||||
|
||||
var/list/datum/polloption/options = list()
|
||||
|
||||
var/datum/DBQuery/query_irv_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
|
||||
var/datum/db_query/query_irv_options = SSdbcore.NewQuery({"
|
||||
SELECT id, text
|
||||
FROM [format_table_name("poll_option")]
|
||||
WHERE pollid = :id
|
||||
"}, list(
|
||||
"id" = pollid
|
||||
))
|
||||
if(!query_irv_options.warn_execute())
|
||||
qdel(query_irv_options)
|
||||
return
|
||||
@@ -352,16 +415,23 @@
|
||||
if (text)
|
||||
table = "poll_textreply"
|
||||
if (!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
|
||||
return
|
||||
var/datum/DBQuery/query_hasvoted = SSdbcore.NewQuery("SELECT id FROM `[format_table_name(table)]` WHERE pollid = [pollid] AND ckey = '[ckey]'")
|
||||
var/datum/db_query/query_hasvoted = SSdbcore.NewQuery({"
|
||||
SELECT id
|
||||
FROM `[format_table_name(table)]`
|
||||
WHERE pollid = :id AND ckey = :ckey
|
||||
"}, list(
|
||||
"id" = pollid,
|
||||
"ckey" = ckey
|
||||
))
|
||||
if(!query_hasvoted.warn_execute())
|
||||
qdel(query_hasvoted)
|
||||
return
|
||||
if(query_hasvoted.NextRow())
|
||||
qdel(query_hasvoted)
|
||||
if(!silent)
|
||||
to_chat(usr, "<span class='danger'>You've already replied to this poll.</span>")
|
||||
to_chat(usr, "<span class='danger'>You've already replied to this poll.</span>", confidential = TRUE)
|
||||
return TRUE
|
||||
qdel(query_hasvoted)
|
||||
return FALSE
|
||||
@@ -376,24 +446,31 @@
|
||||
/mob/dead/new_player/proc/vote_rig_check()
|
||||
if (usr != src)
|
||||
if (!usr || !src)
|
||||
return 0
|
||||
return FALSE
|
||||
//we gots ourselfs a dirty cheater on our hands!
|
||||
log_game("[key_name(usr)] attempted to rig the vote by voting as [key]")
|
||||
message_admins("[key_name_admin(usr)] attempted to rig the vote by voting as [key]")
|
||||
to_chat(usr, "<span class='danger'>You don't seem to be [key].</span>")
|
||||
to_chat(src, "<span class='danger'>Something went horribly wrong processing your vote. Please contact an administrator, they should have gotten a message about this</span>")
|
||||
return 0
|
||||
return 1
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/mob/dead/new_player/proc/vote_valid_check(pollid, holder, type)
|
||||
if (!SSdbcore.Connect())
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return 0
|
||||
return
|
||||
pollid = text2num(pollid)
|
||||
if (!pollid || pollid < 0)
|
||||
return 0
|
||||
//validate the poll is actually the right type of poll and its still active
|
||||
var/datum/DBQuery/query_validate_poll = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime AND polltype = '[type]' [(holder ? "" : "AND adminonly = false")]")
|
||||
var/datum/db_query/query_validate_poll = SSdbcore.NewQuery({"
|
||||
SELECT id
|
||||
FROM [format_table_name("poll_question")]
|
||||
WHERE id = :id AND Now() BETWEEN starttime AND endtime AND polltype = :type [(holder ? "" : "AND adminonly = false")]
|
||||
"}, list(
|
||||
"id" = pollid,
|
||||
"type" = type
|
||||
))
|
||||
if(!query_validate_poll.warn_execute())
|
||||
qdel(query_validate_poll)
|
||||
return 0
|
||||
@@ -403,88 +480,66 @@
|
||||
qdel(query_validate_poll)
|
||||
return 1
|
||||
|
||||
/**
|
||||
* Processes vote form data and saves results to the database for an IRV type poll.
|
||||
*
|
||||
*/
|
||||
/mob/dead/new_player/proc/vote_on_irv_poll(pollid, list/votelist)
|
||||
if (!SSdbcore.Connect())
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return 0
|
||||
if (!vote_rig_check())
|
||||
return 0
|
||||
pollid = text2num(pollid)
|
||||
if (!pollid || pollid < 0)
|
||||
return 0
|
||||
if (!votelist || !istype(votelist) || !votelist.len)
|
||||
return 0
|
||||
if (!client)
|
||||
return 0
|
||||
//save these now so we can still process the vote if the client goes away while we process.
|
||||
var/datum/admins/holder = client.holder
|
||||
var/rank = "Player"
|
||||
if (holder)
|
||||
rank = holder.rank.name
|
||||
var/ckey = client.ckey
|
||||
var/address = client.address
|
||||
|
||||
//validate the poll
|
||||
if (!vote_valid_check(pollid, holder, POLLTYPE_IRV))
|
||||
return
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
if(!vote_rig_check())
|
||||
return
|
||||
if(!pollid)
|
||||
return
|
||||
// var/list/votelist = splittext(href_list["IRVdata"], ",")
|
||||
if(!length(votelist))
|
||||
to_chat(src, "<span class='danger'>No ordering data found. Please try again or contact an administrator.</span>")
|
||||
var/admin_rank = "Player"
|
||||
if(!QDELETED(client) && client.holder)
|
||||
admin_rank = client.holder.rank.name
|
||||
if (!vote_valid_check(pollid, client?.holder, POLLTYPE_IRV))
|
||||
return 0
|
||||
|
||||
//lets collect the options
|
||||
var/datum/DBQuery/query_irv_id = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
|
||||
if(!query_irv_id.warn_execute())
|
||||
qdel(query_irv_id)
|
||||
return 0
|
||||
var/list/optionlist = list()
|
||||
while (query_irv_id.NextRow())
|
||||
optionlist += text2num(query_irv_id.item[1])
|
||||
qdel(query_irv_id)
|
||||
var/list/special_columns = list(
|
||||
"datetime" = "NOW()",
|
||||
"ip" = "INET_ATON(?)",
|
||||
)
|
||||
|
||||
//validate their votes are actually in the list of options and actually numbers
|
||||
var/list/numberedvotelist = list()
|
||||
for (var/vote in votelist)
|
||||
vote = text2num(vote)
|
||||
numberedvotelist += vote
|
||||
if (!vote) //this is fine because voteid starts at 1, so it will never be 0
|
||||
to_chat(src, "<span class='danger'>Error: Invalid (non-numeric) votes in the vote data.</span>")
|
||||
return 0
|
||||
if (!(vote in optionlist))
|
||||
to_chat(src, "<span class='danger'>Votes for choices that do not appear to be in the poll detected.</span>")
|
||||
return 0
|
||||
if (!numberedvotelist.len)
|
||||
to_chat(src, "<span class='danger'>Invalid vote data</span>")
|
||||
return 0
|
||||
|
||||
//lets add the vote, first we generate an insert statement.
|
||||
|
||||
var/sqlrowlist = ""
|
||||
for (var/vote in numberedvotelist)
|
||||
if (sqlrowlist != "")
|
||||
sqlrowlist += ", " //a comma (,) at the start of the first row to insert will trigger a SQL error
|
||||
sqlrowlist += "(Now(), [pollid], [vote], '[sanitizeSQL(ckey)]', INET_ATON('[sanitizeSQL(address)]'), '[sanitizeSQL(rank)]')"
|
||||
|
||||
//now lets delete their old votes (if any)
|
||||
var/datum/DBQuery/query_irv_del_old = SSdbcore.NewQuery("DELETE FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
|
||||
if(!query_irv_del_old.warn_execute())
|
||||
qdel(query_irv_del_old)
|
||||
return 0
|
||||
qdel(query_irv_del_old)
|
||||
|
||||
//now to add the new ones.
|
||||
var/datum/DBQuery/query_irv_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES [sqlrowlist]")
|
||||
if(!query_irv_vote.warn_execute())
|
||||
qdel(query_irv_vote)
|
||||
return 0
|
||||
qdel(query_irv_vote)
|
||||
if(!QDELETED(src))
|
||||
src << browse(null,"window=playerpoll")
|
||||
return 1
|
||||
var/sql_votes = list()
|
||||
for(var/o in votelist)
|
||||
var/voteid = text2num(o)
|
||||
if(!voteid)
|
||||
continue
|
||||
sql_votes += list(list(
|
||||
"pollid" = pollid,
|
||||
"optionid" = voteid,
|
||||
"ckey" = ckey,
|
||||
"ip" = client.address,
|
||||
"adminrank" = admin_rank
|
||||
))
|
||||
//IRV results are calculated based on id order, we delete all of a user's votes to avoid potential errors caused by revoting and option editing
|
||||
var/datum/db_query/query_delete_irv_votes = SSdbcore.NewQuery({"
|
||||
UPDATE [format_table_name("poll_vote")] SET deleted = 1 WHERE pollid = :pollid AND ckey = :ckey
|
||||
"}, list("pollid" = pollid, "ckey" = ckey))
|
||||
if(!query_delete_irv_votes.warn_execute())
|
||||
qdel(query_delete_irv_votes)
|
||||
return
|
||||
qdel(query_delete_irv_votes)
|
||||
SSdbcore.MassInsert(format_table_name("poll_vote"), sql_votes, special_columns = special_columns)
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/dead/new_player/proc/vote_on_poll(pollid, optionid)
|
||||
if (!SSdbcore.Connect())
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return 0
|
||||
if (!vote_rig_check())
|
||||
return 0
|
||||
return
|
||||
if(!vote_rig_check())
|
||||
return
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
if(!pollid || !optionid)
|
||||
return
|
||||
//validate the poll
|
||||
@@ -493,10 +548,19 @@
|
||||
var/voted = poll_check_voted(pollid)
|
||||
if(isnull(voted) || voted) //Failed or already voted.
|
||||
return
|
||||
var/adminrank = sanitizeSQL(poll_rank())
|
||||
var/adminrank = poll_rank()
|
||||
if(!adminrank)
|
||||
return
|
||||
var/datum/DBQuery/query_option_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
|
||||
var/datum/db_query/query_option_vote = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank)
|
||||
VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank)
|
||||
"}, list(
|
||||
"pollid" = pollid,
|
||||
"optionid" = optionid,
|
||||
"ckey" = ckey,
|
||||
"address" = client.address,
|
||||
"adminrank" = adminrank
|
||||
))
|
||||
if(!query_option_vote.warn_execute())
|
||||
qdel(query_option_vote)
|
||||
return
|
||||
@@ -506,11 +570,13 @@
|
||||
return 1
|
||||
|
||||
/mob/dead/new_player/proc/log_text_poll_reply(pollid, replytext)
|
||||
if (!SSdbcore.Connect())
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return 0
|
||||
if (!vote_rig_check())
|
||||
return 0
|
||||
return
|
||||
if(!vote_rig_check())
|
||||
return
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
if(!pollid)
|
||||
return
|
||||
//validate the poll
|
||||
@@ -522,18 +588,23 @@
|
||||
var/voted = poll_check_voted(pollid, text = TRUE, silent = TRUE)
|
||||
if(isnull(voted))
|
||||
return
|
||||
var/adminrank = sanitizeSQL(poll_rank())
|
||||
var/adminrank = poll_rank()
|
||||
if(!adminrank)
|
||||
return
|
||||
replytext = sanitizeSQL(replytext)
|
||||
if(!(length(replytext) > 0) || !(length(replytext) <= 8000))
|
||||
to_chat(usr, "The text you entered was invalid or too long. Please correct the text and submit again.")
|
||||
return
|
||||
var/datum/DBQuery/query_text_vote
|
||||
var/datum/db_query/query_text_vote
|
||||
if(!voted)
|
||||
query_text_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (Now(), [pollid], '[ckey]', INET_ATON('[client.address]'), '[replytext]', '[adminrank]')")
|
||||
query_text_vote = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("poll_textreply")] (datetime, pollid, ckey, ip, replytext, adminrank)
|
||||
VALUES (Now(), :pollid, :ckey, INET_ATON(:address), :replytext, :adminrank)
|
||||
"}, list("pollid" = pollid, "ckey" = ckey, "address" = client.address, "replytext" = replytext, "adminrank" = adminrank))
|
||||
else
|
||||
query_text_vote = SSdbcore.NewQuery("UPDATE [format_table_name("poll_textreply")] SET datetime = Now(), ip = INET_ATON('[client.address]'), replytext = '[replytext]' WHERE pollid = '[pollid]' AND ckey = '[ckey]'")
|
||||
query_text_vote = SSdbcore.NewQuery({"
|
||||
UPDATE [format_table_name("poll_textreply")]
|
||||
SET datetime = Now(), ip = INET_ATON(:address), replytext = :replytext WHERE pollid = :pollid AND ckey = :ckey
|
||||
"}, list("address" = client.address, "replytext" = replytext, "pollid" = pollid, "ckey" = ckey))
|
||||
if(!query_text_vote.warn_execute())
|
||||
qdel(query_text_vote)
|
||||
return
|
||||
@@ -543,17 +614,26 @@
|
||||
return 1
|
||||
|
||||
/mob/dead/new_player/proc/vote_on_numval_poll(pollid, optionid, rating)
|
||||
if (!SSdbcore.Connect())
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return 0
|
||||
if (!vote_rig_check())
|
||||
return 0
|
||||
return
|
||||
if(!vote_rig_check())
|
||||
return
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
if(!pollid || !optionid || !rating)
|
||||
return
|
||||
//validate the poll
|
||||
if (!vote_valid_check(pollid, client.holder, POLLTYPE_RATING))
|
||||
return 0
|
||||
var/datum/DBQuery/query_numval_hasvoted = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND ckey = '[ckey]'")
|
||||
var/datum/db_query/query_numval_hasvoted = SSdbcore.NewQuery({"
|
||||
SELECT id
|
||||
FROM [format_table_name("poll_vote")]
|
||||
WHERE optionid = :id AND ckey = :ckey
|
||||
"}, list(
|
||||
"id" = optionid,
|
||||
"ckey" = ckey
|
||||
))
|
||||
if(!query_numval_hasvoted.warn_execute())
|
||||
qdel(query_numval_hasvoted)
|
||||
return
|
||||
@@ -565,8 +645,10 @@
|
||||
var/adminrank = "Player"
|
||||
if(client.holder)
|
||||
adminrank = client.holder.rank.name
|
||||
adminrank = sanitizeSQL(adminrank)
|
||||
var/datum/DBQuery/query_numval_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]', [(isnull(rating)) ? "null" : rating])")
|
||||
var/datum/db_query/query_numval_vote = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating)
|
||||
VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank, :rating)
|
||||
"}, list("pollid" = pollid, "optionid" = optionid, "ckey" = ckey, "address" = client.address, "adminrank" = adminrank, "rating" = isnull(rating) ? "null" : rating))
|
||||
if(!query_numval_vote.warn_execute())
|
||||
qdel(query_numval_vote)
|
||||
return
|
||||
@@ -575,46 +657,59 @@
|
||||
usr << browse(null,"window=playerpoll")
|
||||
return 1
|
||||
|
||||
/**
|
||||
* Processes vote form data and saves results to the database for a multiple choice type poll.
|
||||
*
|
||||
*/
|
||||
/mob/dead/new_player/proc/vote_on_multi_poll(pollid, optionid)
|
||||
if (!SSdbcore.Connect())
|
||||
if(!SSdbcore.Connect())
|
||||
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
|
||||
return 0
|
||||
if (!vote_rig_check())
|
||||
return 0
|
||||
if(!pollid || !optionid)
|
||||
return 1
|
||||
return
|
||||
if(!vote_rig_check())
|
||||
return
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
//validate the poll
|
||||
if (!vote_valid_check(pollid, client.holder, POLLTYPE_MULTI))
|
||||
return 0
|
||||
var/datum/DBQuery/query_multi_choicelen = SSdbcore.NewQuery("SELECT multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid]")
|
||||
if(!query_multi_choicelen.warn_execute())
|
||||
qdel(query_multi_choicelen)
|
||||
return 1
|
||||
var/i
|
||||
if(query_multi_choicelen.NextRow())
|
||||
i = text2num(query_multi_choicelen.item[1])
|
||||
qdel(query_multi_choicelen)
|
||||
var/datum/DBQuery/query_multi_hasvoted = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
|
||||
if(!query_multi_hasvoted.warn_execute())
|
||||
qdel(query_multi_hasvoted)
|
||||
return 1
|
||||
while(i)
|
||||
if(query_multi_hasvoted.NextRow())
|
||||
i--
|
||||
else
|
||||
break
|
||||
qdel(query_multi_hasvoted)
|
||||
if(!i)
|
||||
return 2
|
||||
var/adminrank = "Player"
|
||||
if(!QDELETED(client) && client.holder)
|
||||
adminrank = client.holder.rank.name
|
||||
adminrank = sanitizeSQL(adminrank)
|
||||
var/datum/DBQuery/query_multi_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
|
||||
if(!query_multi_vote.warn_execute())
|
||||
qdel(query_multi_vote)
|
||||
return 1
|
||||
qdel(query_multi_vote)
|
||||
if(!QDELETED(usr))
|
||||
usr << browse(null,"window=playerpoll")
|
||||
return 0
|
||||
if(!vote_valid_check(pollid, client.holder, POLLTYPE_MULTI))
|
||||
return
|
||||
if(!pollid || !optionid)
|
||||
return
|
||||
// if(length(href_list) > 2)
|
||||
// href_list.Cut(1,3) //first two values aren't options
|
||||
// else
|
||||
// to_chat(src, "<span class='danger'>No options were selected.</span>")
|
||||
|
||||
var/special_columns = list(
|
||||
"datetime" = "NOW()",
|
||||
"ip" = "INET_ATON(?)",
|
||||
)
|
||||
|
||||
var/sql_votes = list()
|
||||
// var/vote_count = 0
|
||||
// for(var/h in href_list)
|
||||
// if(vote_count == poll.options_allowed)
|
||||
// to_chat(src, "<span class='danger'>Allowed option count exceeded, only the first [poll.options_allowed] selected options have been saved.</span>")
|
||||
// break
|
||||
// vote_count++
|
||||
// var/datum/poll_option/option = locate(h) in poll.options
|
||||
var/admin_rank = "Player"
|
||||
if(!QDELETED(client) && client?.holder)
|
||||
admin_rank = client.holder.rank.name
|
||||
sql_votes += list(list(
|
||||
"pollid" = pollid,
|
||||
"optionid" = optionid,
|
||||
"ckey" = ckey,
|
||||
"ip" = client.address,
|
||||
"adminrank" = admin_rank
|
||||
))
|
||||
/*with revoting and poll editing possible there can be an edge case where a poll is changed to allow less multiple choice options than a user has already voted on
|
||||
rather than trying to calculate which options should be updated and which deleted, we just delete all of a user's votes and re-insert as needed*/
|
||||
var/datum/db_query/query_delete_multi_votes = SSdbcore.NewQuery({"
|
||||
UPDATE [format_table_name("poll_vote")] SET deleted = 1 WHERE pollid = :pollid AND ckey = :ckey
|
||||
"}, list("pollid" = pollid, "ckey" = ckey))
|
||||
if(!query_delete_multi_votes.warn_execute())
|
||||
qdel(query_delete_multi_votes)
|
||||
return
|
||||
qdel(query_delete_multi_votes)
|
||||
SSdbcore.MassInsert(format_table_name("poll_vote"), sql_votes, special_columns = special_columns)
|
||||
return TRUE
|
||||
|
||||
@@ -143,10 +143,10 @@
|
||||
parry_efficiency_considered_successful = 0.01
|
||||
parry_efficiency_to_counterattack = 0.01
|
||||
parry_max_attacks = INFINITY
|
||||
parry_failed_cooldown_duration = 3 SECONDS
|
||||
parry_failed_stagger_duration = 2 SECONDS
|
||||
parry_cooldown = 3 SECONDS
|
||||
parry_failed_clickcd_duration = 0.8 SECONDS
|
||||
parry_failed_cooldown_duration = 1.5 SECONDS
|
||||
parry_failed_stagger_duration = 1 SECONDS
|
||||
parry_cooldown = 0
|
||||
parry_failed_clickcd_duration = 0.8
|
||||
|
||||
parry_data = list( // yeah it's snowflake
|
||||
"UNARMED_PARRY_STAGGER" = 3 SECONDS,
|
||||
|
||||
@@ -80,6 +80,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/list/inherent_traits = list()
|
||||
var/inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
|
||||
|
||||
var/list/blacklisted_quirks = list() // Quirks that will be removed upon gaining this species, to be defined by species
|
||||
var/list/removed_quirks = list() // Quirks that got removed due to being blacklisted, and will be restored when on_species_loss() is called
|
||||
|
||||
var/attack_verb = "punch" // punch-specific attack verb
|
||||
var/sound/attack_sound = 'sound/weapons/punch1.ogg'
|
||||
var/sound/miss_sound = 'sound/weapons/punchmiss.ogg'
|
||||
@@ -342,6 +345,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
for(var/X in inherent_traits)
|
||||
ADD_TRAIT(C, X, SPECIES_TRAIT)
|
||||
|
||||
//lets remove those conflicting quirks
|
||||
remove_blacklisted_quirks(C)
|
||||
|
||||
if(TRAIT_VIRUSIMMUNE in inherent_traits)
|
||||
for(var/datum/disease/A in C.diseases)
|
||||
A.cure(FALSE)
|
||||
@@ -395,6 +401,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
for(var/X in inherent_traits)
|
||||
REMOVE_TRAIT(C, X, SPECIES_TRAIT)
|
||||
|
||||
// lets restore the quirks that got removed when gaining this species
|
||||
restore_quirks(C)
|
||||
|
||||
C.remove_movespeed_modifier(/datum/movespeed_modifier/species)
|
||||
|
||||
if(mutant_bodyparts["meat_type"])
|
||||
@@ -424,6 +433,26 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
|
||||
|
||||
// shamelessly inspired by antag_datum.remove_blacklisted_quirks()
|
||||
/datum/species/proc/remove_blacklisted_quirks(mob/living/carbon/C)
|
||||
var/mob/living/L = C.mind?.current
|
||||
if(istype(L))
|
||||
var/list/my_quirks = L.client?.prefs.all_quirks.Copy()
|
||||
SSquirks.filter_quirks(my_quirks, blacklisted_quirks)
|
||||
for(var/q in L.roundstart_quirks)
|
||||
var/datum/quirk/Q = q
|
||||
if(!(SSquirks.quirk_name_by_path(Q.type) in my_quirks))
|
||||
L.remove_quirk(Q.type)
|
||||
removed_quirks += Q.type
|
||||
|
||||
// restore any quirks that we removed
|
||||
/datum/species/proc/restore_quirks(mob/living/carbon/C)
|
||||
var/mob/living/L = C.mind?.current
|
||||
if(istype(L))
|
||||
for(var/q in removed_quirks)
|
||||
L.add_quirk(q)
|
||||
|
||||
|
||||
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
|
||||
H.remove_overlay(HAIR_LAYER)
|
||||
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
|
||||
@@ -1454,9 +1483,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
|
||||
var/armor_block = target.run_armor_check(affecting, "melee")
|
||||
if(HAS_TRAIT(user, TRAIT_MAULER)) // maulers get 15 armorpierce because if you're going to punch someone you might as well do a good job of it
|
||||
armor_block = target.run_armor_check(affecting, "melee", armour_penetration = 15) // lot of good that sec jumpsuit did you
|
||||
|
||||
playsound(target.loc, user.dna.species.attack_sound, 25, 1, -1)
|
||||
target.visible_message("<span class='danger'>[user] [atk_verb]ed [target]!</span>", \
|
||||
"<span class='userdanger'>[user] [atk_verb]ed you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
|
||||
@@ -1473,9 +1499,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
target.apply_damage(damage*1.5, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
|
||||
target.apply_damage(damage*0.5, STAMINA, affecting, armor_block)
|
||||
log_combat(user, target, "kicked")
|
||||
else if(HAS_TRAIT(user, TRAIT_MAULER)) // mauler punches deal 1.1x raw damage + 1.3x stam damage, and have some armor pierce
|
||||
target.apply_damage(damage*1.1, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
|
||||
target.apply_damage(damage*1.3, STAMINA, affecting, armor_block)
|
||||
else if(HAS_TRAIT(user, TRAIT_MAULER)) // mauler punches deal 1.2x raw damage but nstam
|
||||
target.apply_damage(damage*1.2, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
|
||||
log_combat(user, target, "punched (mauler)")
|
||||
else //other attacks deal full raw damage + 2x in stamina damage
|
||||
target.apply_damage(damage, attack_type, affecting, armor_block, wound_bonus = punchwoundbonus)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
armor = 20 // 120 damage to KO a zombie, which kills it
|
||||
speedmod = 1.6 // they're very slow
|
||||
mutanteyes = /obj/item/organ/eyes/night_vision/zombie
|
||||
blacklisted_quirks = list(/datum/quirk/nonviolent)
|
||||
var/heal_rate = 1
|
||||
var/regen_cooldown = 0
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
break
|
||||
var/msg = "[key_name_admin(src)] [ADMIN_JMP(src)] was found to have no .loc with an attached client, if the cause is unknown it would be wise to ask how this was accomplished."
|
||||
message_admins(msg)
|
||||
send2irc_adminless_only("Mob", msg, R_ADMIN)
|
||||
send2tgs_adminless_only("Mob", msg, R_ADMIN)
|
||||
log_game("[key_name(src)] was found to have no .loc with an attached client.")
|
||||
|
||||
// This is a temporary error tracker to make sure we've caught everything
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE))
|
||||
to_chat(src, "<span class='warning'>You are not something that can parry attacks.</span>")
|
||||
return
|
||||
if(!(mobility_flags & MOBILITY_STAND))
|
||||
to_chat(src, "<span class='warning'>You aren't able to parry without solid footing!</span>")
|
||||
return
|
||||
// Prioritize item, then martial art, then unarmed.
|
||||
// yanderedev else if time
|
||||
var/obj/item/using_item = get_active_held_item()
|
||||
|
||||
@@ -78,6 +78,15 @@
|
||||
projectiletype = /obj/item/projectile/neurotox
|
||||
projectilesound = 'sound/weapons/pierce.ogg'
|
||||
|
||||
/mob/living/simple_animal/hostile/alien/sentinel/cube
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
health = 220
|
||||
maxHealth = 220
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
del_on_death = TRUE
|
||||
loot = list(/obj/effect/mob_spawn/alien/corpse/humanoid/sentinel)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/alien/queen
|
||||
name = "alien queen"
|
||||
|
||||
@@ -425,7 +425,7 @@ Difficulty: Very Hard
|
||||
|
||||
/obj/machinery/anomalous_crystal/honk //Strips and equips you as a clown. I apologize for nothing
|
||||
observer_desc = "This crystal strips and equips its targets as clowns."
|
||||
possible_methods = list(ACTIVATE_MOB_BUMP, ACTIVATE_SPEECH)
|
||||
possible_methods = list(ACTIVATE_TOUCH) //Because We love AOE transformations!
|
||||
activation_sound = 'sound/items/bikehorn.ogg'
|
||||
|
||||
/obj/machinery/anomalous_crystal/honk/ActivationReaction(mob/user)
|
||||
|
||||
@@ -399,6 +399,14 @@ Difficulty: Medium
|
||||
crusher_loot = list()
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/transformed //ash drake balanced around player control
|
||||
name = "transformed ash drake"
|
||||
desc = "A sentient being transformed into an ash drake"
|
||||
mob_size = MOB_SIZE_HUMAN //prevents crusher vulnerability
|
||||
move_force = MOVE_FORCE_NORMAL //stops them from destroying and unanchoring shit by walking into it
|
||||
environment_smash = ENVIRONMENT_SMASH_STRUCTURES //no we dont want sentient megafauna be able to delete the entire station in a minute flat
|
||||
damage_coeff = list(BRUTE = 0.7, BURN = 0.5, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) //200 health but not locked to standard movespeed, needs armor befitting of a dragon
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype)
|
||||
return
|
||||
|
||||
@@ -414,7 +422,8 @@ Difficulty: Medium
|
||||
if(L in hit_list || L == source)
|
||||
continue
|
||||
hit_list += L
|
||||
L.adjustFireLoss(20)
|
||||
L.adjustFireLoss(5)
|
||||
L.adjust_fire_stacks(6)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by [source]'s fire breath!</span>")
|
||||
|
||||
// deals damage to mechs
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
A.GiveTarget(target)
|
||||
A.friends = friends
|
||||
A.faction = faction.Copy()
|
||||
if(!A == /mob/living/simple_animal/hostile/poison/bees/toxin)
|
||||
A.my_creator = type
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/AttackingTarget()
|
||||
@@ -88,6 +90,7 @@
|
||||
density = FALSE
|
||||
del_on_death = 1
|
||||
var/swarming = FALSE
|
||||
var/my_creator = null
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/Initialize()
|
||||
. = ..()
|
||||
@@ -205,11 +208,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/proc/infest(mob/living/carbon/human/H)
|
||||
visible_message("<span class='warning'>[name] burrows into the flesh of [H]!</span>")
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L
|
||||
if(HAS_TRAIT(H, TRAIT_DWARF)) //dwarf legions aren't just fluff!
|
||||
L = new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf(H.loc)
|
||||
else
|
||||
L = new(H.loc)
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L = check_infest_type(H)
|
||||
visible_message("<span class='warning'>[L] staggers to [L.p_their()] feet!</span>")
|
||||
H.death()
|
||||
H.adjustBruteLoss(1000)
|
||||
@@ -217,6 +216,20 @@
|
||||
H.forceMove(L)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/proc/check_infest_type(mob/living/carbon/human/human)
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L
|
||||
var/list/blacklisted_types = list(/mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf)
|
||||
if(HAS_TRAIT(human, TRAIT_DWARF)) //dwarf legions aren't just fluff!
|
||||
L = new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf(human.loc)
|
||||
else if(my_creator)
|
||||
if(my_creator in blacklisted_types)
|
||||
L = new(human.loc)
|
||||
else
|
||||
L = new my_creator(human.loc)
|
||||
else
|
||||
L = new(human.loc)
|
||||
return L
|
||||
|
||||
//Advanced Legion is slightly tougher to kill and can raise corpses (revive other legions)
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/advanced
|
||||
stat_attack = DEAD
|
||||
|
||||
@@ -178,10 +178,7 @@
|
||||
|
||||
new /obj/effect/temp_visual/monkeyify/humanify(loc)
|
||||
|
||||
transformation_timer = addtimer(CALLBACK(src, .proc/finish_humanize, tr_flags), TRANSFORMATION_DURATION, TIMER_UNIQUE)
|
||||
|
||||
/mob/living/carbon/proc/finish_humanize(tr_flags)
|
||||
transformation_timer = null
|
||||
sleep(TRANSFORMATION_DURATION) //This entire proc CANNOT be split into two
|
||||
|
||||
var/list/stored_implants = list()
|
||||
var/list/int_organs = list()
|
||||
|
||||
Reference in New Issue
Block a user