parent
690b9db45b
commit
89215ef8e9
@ -0,0 +1,137 @@ |
||||
--! Previous: sha1:2a39b3b006a8d5ec5d30ac1e5c2158d282b99acb |
||||
--! Hash: sha1:1390238abcc8e013a87b670fbcb3ae127a51ece8 |
||||
--! Message: daily and VGNYJ error functions |
||||
|
||||
--- Calculates the date at which /daily can be used again after the given timestamp. |
||||
CREATE OR REPLACE FUNCTION NextDailyCommand(IN lastDaily timestamp with time zone) |
||||
RETURNS timestamp with time zone |
||||
CALLED ON NULL INPUT |
||||
IMMUTABLE |
||||
AS |
||||
$$ |
||||
SELECT (COALESCE(lastDaily, TIMESTAMP '-infinity' AT TIME ZONE 'UTC')::date + 1)::timestamp AT TIME ZONE 'UTC' |
||||
$$ LANGUAGE 'sql'; |
||||
|
||||
--- Raises the VGNYJ error. |
||||
CREATE OR REPLACE PROCEDURE Error_NotYetJoined() AS |
||||
$$ |
||||
BEGIN |
||||
RAISE EXCEPTION USING |
||||
ERRCODE = 'VGNYJ', |
||||
MESSAGE = 'Not yet joined', |
||||
DETAIL = 'You haven''t joined the game yet, and can''t use this command until you do.', |
||||
HINT = 'Use the /join command to join the game!'; |
||||
END; |
||||
$$ LANGUAGE 'plpgsql'; |
||||
|
||||
--- Runs the full /daily command. |
||||
--- Error codes: |
||||
--- VGBCG: Bad channel (game). This is not a valid channel to send game commands in. |
||||
--- VGBGG: Bad guild (game). This is not a valid guild to send game commands in. |
||||
--- VGNYJ: Not yet joined. The Discord user using has not joined the game yet. |
||||
--- VGDLY: Already used today. |
||||
CREATE OR REPLACE FUNCTION Command_Daily( |
||||
IN requestedChannel DiscordChannel.discordId%TYPE, |
||||
IN requestedGuild DiscordChannel.guildId%TYPE, |
||||
IN forId DiscordUser.discordId%TYPE, |
||||
IN newUsername DiscordUser.username%TYPE, |
||||
IN newDiscriminator DiscordUser.discriminator%TYPE, |
||||
OUT newLastDaily Player.lastDaily%TYPE, |
||||
OUT newNextDaily Player.lastDaily%TYPE, |
||||
OUT newCurrency Player.currency%TYPE, |
||||
OUT bonus Player.currency%TYPE) |
||||
STRICT |
||||
VOLATILE |
||||
AS |
||||
$$ |
||||
DECLARE |
||||
playerId Player.id%TYPE; |
||||
playerLastDaily Player.lastDaily%TYPE; |
||||
playerNextDaily Player.lastDaily%TYPE; |
||||
BEGIN |
||||
CALL CheckGameCommandIn(requestedChannel, requestedGuild); |
||||
SELECT InvokingPlayer.id, InvokingPlayer.lastDaily, NextDailyCommand(InvokingPlayer.lastdaily) |
||||
INTO playerId, playerLastDaily, playerNextDaily |
||||
FROM GetInvokingPlayer(forId, newUsername, newDiscriminator) AS InvokingPlayer; |
||||
IF playerId IS NULL THEN |
||||
CALL Error_NotYetJoined(); |
||||
END IF; |
||||
bonus = 100; |
||||
IF playerNextDaily > NOW() THEN |
||||
RAISE EXCEPTION USING |
||||
ERRCODE = 'VGDLY', |
||||
MESSAGE = 'Already used today', |
||||
DETAIL = format('You last used your daily command at <t:%s>. You can use it again at <t:%s>.', |
||||
extract(EPOCH FROM playerLastDaily)::INT, |
||||
extract(EPOCH FROM playerNextDaily)::INT), |
||||
HINT = format('Wait <t:%s:R> and you can use the /daily command again to get some more currency!', |
||||
extract(EPOCH FROM playerNextDaily)::INT); |
||||
ELSE |
||||
UPDATE Player |
||||
SET currency = currency + bonus, |
||||
lastDaily = NOW() |
||||
WHERE id = playerId |
||||
RETURNING lastDaily, currency INTO newLastDaily, newCurrency; |
||||
newNextDaily = NextDailyCommand(newLastDaily); |
||||
END IF; |
||||
END; |
||||
$$ LANGUAGE 'plpgsql'; |
||||
|
||||
--- Runs the full /pull command. |
||||
--- Error codes: |
||||
--- VGBCG: Bad channel (game). This is not a valid channel to send game commands in. |
||||
--- VGBGG: Bad guild (game). This is not a valid guild to send game commands in. |
||||
--- VGNYJ: Not yet joined. The Discord user using has not joined the game yet. |
||||
--- VGNEC: Not enough currency. |
||||
CREATE OR REPLACE FUNCTION Command_Pull( |
||||
IN requestedChannel DiscordChannel.discordId%TYPE, |
||||
IN requestedGuild DiscordChannel.guildId%TYPE, |
||||
IN forId DiscordUser.discordId%TYPE, |
||||
IN newUsername DiscordUser.username%TYPE, |
||||
IN newDiscriminator DiscordUser.discriminator%TYPE, |
||||
IN count INT |
||||
) |
||||
RETURNS TABLE |
||||
( |
||||
summonedUnitInstanceId SummonedUnit.instanceId%TYPE, |
||||
summonedUnitId Unit.id%TYPE, |
||||
summonedUnitName Unit.name%TYPE, |
||||
summonedUnitSubtitle Unit.subtitle%TYPE, |
||||
summonedUnitTierName UnitTier.name%TYPE, |
||||
firstTimePull BOOLEAN, |
||||
wasAlreadySummoned BOOLEAN |
||||
) |
||||
STRICT |
||||
VOLATILE |
||||
ROWS 10 |
||||
AS |
||||
$$ |
||||
DECLARE |
||||
playerId Player.id%TYPE; |
||||
cost Player.currency%TYPE; |
||||
oldCurrency Player.currency%TYPE; |
||||
playerLastDaily Player.lastDaily%TYPE; |
||||
BEGIN |
||||
CALL CheckGameCommandIn(requestedChannel, requestedGuild); |
||||
SELECT InvokingPlayer.id, InvokingPlayer.currency, InvokingPlayer.lastDaily |
||||
INTO playerId, oldCurrency, playerLastDaily |
||||
FROM GetInvokingPlayer(forId, newUsername, newDiscriminator) AS InvokingPlayer; |
||||
IF playerId IS NULL THEN |
||||
CALL Error_NotYetJoined(); |
||||
END IF; |
||||
cost = 10 * count; |
||||
UPDATE Player SET currency = currency - cost WHERE id = playerId AND currency >= cost; |
||||
IF NOT FOUND THEN |
||||
RAISE EXCEPTION USING |
||||
ERRCODE = 'VGNEC', |
||||
MESSAGE = 'Not enough currency', |
||||
DETAIL = format('Pulling %s heroines would cost %s currency, but you only have %s currency.', |
||||
count, cost, oldCurrency), |
||||
HINT = CASE NextDailyCommand(playerLastDaily) > NOW() |
||||
WHEN FALSE THEN 'You can use the /daily command to get some more currency for today!' |
||||
ELSE format('You can use the /daily command again <t:%s:R> to get some more currency!', |
||||
extract(EPOCH FROM NextDailyCommand(playerLastDaily))::INT) |
||||
END; |
||||
END IF; |
||||
END; |
||||
$$ LANGUAGE 'plpgsql'; |
@ -0,0 +1,47 @@ |
||||
import {CommandContext, SlashCommand, SlashCreator} from "slash-create"; |
||||
import {Snowflake} from "discord-api-types"; |
||||
import {Pool} from "pg"; |
||||
import {sendErrorMessage} from "../../queries/ErrorCodes.js"; |
||||
import {singleRowQueryResult} from "../../queries/QueryHelpers.js"; |
||||
|
||||
export class DailyCommand extends SlashCommand { |
||||
readonly pool: Pool |
||||
|
||||
constructor(creator: SlashCreator, {pool, gameGuildIds}: { |
||||
pool: Pool, |
||||
gameGuildIds: Snowflake[] |
||||
}) { |
||||
super(creator, { |
||||
name: "daily", |
||||
guildIDs: gameGuildIds, |
||||
description: "Gathers your daily currency once per day, resetting at midnight UTC.", |
||||
options: [] |
||||
}); |
||||
|
||||
this.pool = pool |
||||
} |
||||
|
||||
async run(ctx: CommandContext): Promise<any> { |
||||
try { |
||||
const result = singleRowQueryResult(await this.pool.query<{ newlastdaily: Date, newnextdaily: Date, newcurrency: number, bonus: number }>({ |
||||
text: `SELECT *
|
||||
FROM Command_Daily($1, $2, $3, $4, $5)`,
|
||||
values: [ctx.channelID, ctx.guildID, ctx.user.id, ctx.user.username, ctx.user.discriminator], |
||||
})) |
||||
if (result === undefined) { |
||||
await ctx.send({ |
||||
content: "Unexpectedly got no results!!", |
||||
ephemeral: true, |
||||
}) |
||||
console.log("Unexpectedly empty Command_Join result!") |
||||
return; |
||||
} |
||||
return ctx.send({ |
||||
content: `Nice! You reap ${result.bonus} currency from the void, leaving you with a total of ${result.newcurrency}!\n\nYou collected your currency now at <t:${Math.floor(result.newlastdaily.getTime() / 1000)}> and can gather it again in <t:${Math.floor(result.newnextdaily.getTime() / 1000)}:R> at <t:${Math.floor(result.newnextdaily.getTime() / 1000)}>.`, |
||||
ephemeral: true, |
||||
}) |
||||
} catch (e) { |
||||
await sendErrorMessage(ctx, e) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue