KitKatiscool
I might quit this game:/
Rule 14: Do not post about project plans without proof that you have started
moveset: homing attack, time stop, and time boost.
moveset: homing attack, time stop, and time boost.
P_Homing
, for instance. Heck, there's a pre-coded homing attack (CA_HOMINGTHOK
) that you can just slap into your S_SKIN
. Time boost might also be as simple as P_Thrust
or some other speed-increasing function or action. The time stop, however… I have a character who's intended to be capable of slowing and rewinding time, and I still haven't figured out how I'm going to do anything more than monkeying with the HUD timer.Yeah, I know what Speed Break and Time Break are, I played Sonic and the Secret Rings.think of the time boost as a pre curser to the double boost from sonic forces i also want it to we're you need a certin amount of rings
for
loop, so I think I might be able to handle Time Break after all. I'm currently at a bit of an impasse with my own mod, so I might as well make some code for this to help out while I wait to be let back into the Discord server.ability
in your S_SKIN
CA_HOMINGTHOK
, a variation of CA_THOK
(Sonic's ability) that doesn't give quite as much speed for your character's actionspd
value, but homes in on nearby enemies to attack them. Note that this has a 3-second time limit by default, based on the built-in player.homing
variable. If that's not your style and you'd rather fine-tune it more, you could use P_HomingAttack
, which behaves more or less identically, but doesn't have to be the air action (though for the purpose of the moveset you want, it would be), and you can use your own variables to set its timer by repeatedly calling it in a PlayerThink
as long as some timer variable stays above 0. Speaking of which, here's how you'd set up a timer variable (put this code in a PlayerThink
hook to run it on every tic):if player.mo.timer == nil then
player.mo.timer = 0
end
if player.mo.timer > 0 then
if (leveltime%TICRATE) then
player.mo.timer = $ - 1
end
end
player.homingattack
, since we're using it for that purpose. For instance:addHook("JumpSpecial", function(player) --A JumpSpecial hook defines what happens when you press the Jump button in midair after having already jumped off the ground
for player in players.iterate() do --tie the hook to the player
if player.mo.skin ~= "satsr_sonic" then --check that the player is [U]Sonic and the Secret Rings[/U] Sonic
continue --stop running the hook if the player isn't [U]Sonic and the Secret Rings[/U] Sonic
end
if not (player.pflags & PF_THOKKED) --if the player hasn't used an air action or shield action in the current jump
and (player.pflags & PF_JUMPED) then --if the player jumped off the ground (note that this will [I]not[/I] allow the player to use an air action off of a spring, and you'd need to ask someone else how to set that up)
player.target = P_LookForEnemies(player, true, true) --look for an enemy, monitor, or spring within 1024 fracunits of the player up to 30 degrees above or below them and select the nearest enemy, monitor, or spring as the player's target (yes, those distance and angle caveats are inherent to P_LookForEnemies, and no, I don't know how to help you with any other enemy-targeting functions or actions ([I]please[/I] put in a good word for me with the Discord server mods so I can come back sooner rather than later... I need expert advice on so many things...))
if (player.target and player.target.valid) then --if a target was found
player.homingattack = 5*TICRATE --start a 5-second timer for the homing attack (player.homing is the game's own homing variable used for CA_HOMINGTHOK)
player.pflags = $1|PF_THOKKED --set the PF_THOKKED flag to prevent consecutive air actions (PF_THOKKED should automatically be removed when the player touches the ground or otherise has their PF_JUMPED flag removed to allow them to jump from the ground again)
else
P_InstaThrust(player.mo, player.mo.angle, player.actionspd*FRACUNIT) --launch the player in the direction they're facing at their actionspd as defined in their S_SKIN (with actionspd being multiplied by FRACUNIT in order to work properly)
player.pflags = $1|PF_THOKKED --set the PF_THOKKED flag to prevent consecutive air actions (PF_THOKKED should automatically be removed when the player touches the ground or otherise has their PF_JUMPED flag removed to allow them to jump from the ground again)
end
end
end
end)
addHook("PlayerThink", function(player)
if player.mo and player.mo.valid then --check that the player object exists
if player.mo.skin ~= "satsr_sonic" then --check which skin the player is using
return --immediately end the hook if the player is not [U]Sonic and the Secret Rings[/U] Sonic
end
if (player.target and player.target.valid) and player.homingattack > 0 then
P_HomingAttack(player.mo, player.target) --have the player lock on to the target and lunge towards them at a rate of movement equal to 2*player.actionspd/3; since this is in a PlayerThink hook, it will be called every tic if its conditions are met
if P_HomingAttack(player.mo, player.target) == true then --if the homing attack connects
player.pflags = $&~PF_THOKKED --remove the PF_THOKKED flag to enable another air action
end
end
end
end)
CA_THOK
and CA_HOMINGTHOK
to allow a full-speed thok in the absence of enemies while also having a homing attack when enemies are present, while also allowing the Homing Attack to be chained like in Sonic and the Secret Rings and other 3D Sonic games. satsr_sonic
would be the value of your name
parameter in your S_SKIN
; you can set it to whatever you want, but I presume that's what you'd like to call it.player.mo
could alternatively be preceded by p
, p.mo
, or player
. p
is an alternative way to refer to player
(but you have to stick with one or the other throughout your Lua script, otherwise you'll run into errors). p
/player
stores your variable within player_t
(the player), while p.mo
/player.mo
stores your variable within the mobj_t
tied to the MT_PLAYER
object that the player controls. Sometimes it matters which userdata structure you put a variable in, but I've found that it doesn't matter for the majority of purposes. So while you can use either of them for custom variables throughout your script and interchange them (for pre-existing variables, you have to use what the game already puts them in; see the wiki's page on userdata structures), you have to be consistent with using p
/player
or p.mo
/player.mo
for a given custom variable once you've set it up to use one userdata structure or the other (call the wrong userdata structure, and you won't get your variable; however, you can use a p
/player
variable and a p.mo
/player.mo
variable with the same name if you really want to, given that they would actually be two separate variables, but I wouldn't recommend it, since that would get confusing in a hurry).//Defining variables
addHook ("PlayerThink", function(player)
if player.mo and player.mo.valid then
if player.mo.skin ~= "satsr_sonic" then
return
end
if player.custom1down == nil then --if the player does not have this variable yet
player.custom1down = 0 --give it a starting number
end
if player.cmd.buttons & BT_CUSTOM1 > 0 then --if the player is pressing Custom 1
player.custom1down = $1 + 1 --increment custom1 variable
else
player.custom1down = 0 --reset custom1 variable
end
if player.custom2down == nil then --if the player does not have this variable yet
player.custom2down = 0 --give it a starting number
end
if player.cmd.buttons & BT_CUSTOM2 > 0 then --if the player is pressing Custom 2
player.custom2down = $1 + 1 --increment custom2 variable
else
player.custom2down = 0 --reset custom2 variable
end
if player.custom3down == nil then --if the player does not have this variable yet
player.custom3down = 0 --give it a starting number
end
if player.cmd.buttons & BT_CUSTOM3 > 0 then --if the player is pressing Custom 3
player.custom3down = $1 + 1 --increment custom3 variable
else
player.custom3down = 0 --reset custom3 variable
end
if player.tossflagdown == nil then --if the player does not have this variable yet
player.tossflagdown = 0 --give it a starting number
end
if player.cmd.buttons & BT_TOSSFLAG > 0 then --if the player is pressing Toss Flag
player.tossflagdown = $1 + 1 --increment tossflag variable
else
player.tossflagdown = 0 --reset tossflag variable
end
if player.firenormaldown == nil then --if the player does not have this variable yet
player.firenormaldown = 0 --give it a starting number
end
if player.cmd.buttons & BT_FIRENORMAL > 0 then --if the player is pressing Fire Normal
player.firenormaldown = $1 + 1 --increment firenormal variable
else
player.firenormaldown = 0 --reset firenormal variable
end
if player.cmd.buttons & BT_ATTACK > 0 then --if the player is pressing Fire
player.firedown = $1 + 1 --increment fire variable
else
player.firedown = 0 --reset fire variable
end
if player.homingattack == nil then
player.homingattack = 0
end
if player.mo.soulgauge == nil then
player.mo.soulgauge = 200
end
if player.slowedtime == nil then
player.slowedtime = 0
end
if player.mo.timebreak == nil then
player.mo.timebreak = 0
end
if player.mo.speedbreak == nil then
player.mo.speedbreak = 0
end
end
end)
custom1down
and custom2down
, but it's probably good practice to bind them all. homingattack
will serve as a timer for the Homing Attack. soulgauge
will be the fuel for Time Break and Speed Break. slowedtime
will reduce player.realtime
based on how long Time Break has been in use. timebreak
and speedbreak
will tell the game when Time Break and Speed Break are in use, as well as measuring how long they've been in use if that is necessary for some reason.we can talk about this more on my discord catsarecool#1419Hey, @KitKatiscool, I'm starting on the code for your Sonic and the Secret Rings Sonic mod. I'm also setting up an outline/lesson plan of sorts so you can follow along with what the code is intended to do. As I said earlier, my own characters can already do the stuff that you want your character to do, so I can just take the code from there; I'll just need to tweak it a bit. It's gonna take me a while to get it all ready, though.
Post automatically merged:
All right, here's the outline/lesson plan of what your code could look like, based in part on the code for some of the similar things my custom characters can do. Bear in mind that I'm still working out some bugs and unintended side effects of this code myself, but I'll warn you when that comes up. Also, this is gonna be long, so I'll have to break it up into chunks and/or stuff it in a text file so you can actually read it.
First, let's add the Homing Attack. This can be as simple as making yourability
in yourS_SKIN
CA_HOMINGTHOK
, a variation ofCA_THOK
(Sonic's ability) that doesn't give quite as much speed for your character'sactionspd
value, but homes in on nearby enemies to attack them. Note that this has a 3-second time limit by default, based on the built-inplayer.homing
variable. If that's not your style and you'd rather fine-tune it more, you could useP_HomingAttack
, which behaves more or less identically, but doesn't have to be the air action (though for the purpose of the moveset you want, it would be), and you can use your own variables to set its timer by repeatedly calling it in aPlayerThink
as long as some timer variable stays above 0. Speaking of which, here's how you'd set up a timer variable (put this code in aPlayerThink
hook to run it on every tic):
How to make a timer:if player.mo.timer == nil then player.mo.timer = 0 end if player.mo.timer > 0 then if (leveltime%TICRATE) then player.mo.timer = $ - 1 end end
Then you'd set your timer to whatever number of tics you want it to last whenever you want it to be started, and set up some other thing to happen or not be able to happen as long as the timer variable is above 0 or not 0. Let's call the timer variableplayer.homingattack
, since we're using it for that purpose. For instance:
A jump dash/homing attack combo:addHook("JumpSpecial", function(player) --A JumpSpecial hook defines what happens when you press the Jump button in midair after having already jumped off the ground for player in players.iterate() do --tie the hook to the player if player.mo.skin ~= "satsr_sonic" then --check that the player is [U]Sonic and the Secret Rings[/U] Sonic continue --stop running the hook if the player isn't [U]Sonic and the Secret Rings[/U] Sonic end if not (player.pflags & PF_THOKKED) --if the player hasn't used an air action or shield action in the current jump and (player.pflags & PF_JUMPED) then --if the player jumped off the ground (note that this will [I]not[/I] allow the player to use an air action off of a spring, and you'd need to ask someone else how to set that up) player.target = P_LookForEnemies(player, true, true) --look for an enemy, monitor, or spring within 1024 fracunits of the player up to 30 degrees above or below them and select the nearest enemy, monitor, or spring as the player's target (yes, those distance and angle caveats are inherent to P_LookForEnemies, and no, I don't know how to help you with any other enemy-targeting functions or actions ([I]please[/I] put in a good word for me with the Discord server mods so I can come back sooner rather than later... I need expert advice on so many things...)) if (player.target and player.target.valid) then --if a target was found player.homingattack = 5*TICRATE --start a 5-second timer for the homing attack (player.homing is the game's own homing variable used for CA_HOMINGTHOK) player.pflags = $1|PF_THOKKED --set the PF_THOKKED flag to prevent consecutive air actions (PF_THOKKED should automatically be removed when the player touches the ground or otherise has their PF_JUMPED flag removed to allow them to jump from the ground again) else P_InstaThrust(player.mo, player.mo.angle, player.actionspd*FRACUNIT) --launch the player in the direction they're facing at their actionspd as defined in their S_SKIN (with actionspd being multiplied by FRACUNIT in order to work properly) player.pflags = $1|PF_THOKKED --set the PF_THOKKED flag to prevent consecutive air actions (PF_THOKKED should automatically be removed when the player touches the ground or otherise has their PF_JUMPED flag removed to allow them to jump from the ground again) end end end end) addHook("PlayerThink", function(player) if player.mo and player.mo.valid then --check that the player object exists if player.mo.skin ~= "satsr_sonic" then --check which skin the player is using return --immediately end the hook if the player is not [U]Sonic and the Secret Rings[/U] Sonic end if (player.target and player.target.valid) and player.homingattack > 0 then P_HomingAttack(player.mo, player.target) --have the player lock on to the target and lunge towards them at a rate of movement equal to 2*player.actionspd/3; since this is in a PlayerThink hook, it will be called every tic if its conditions are met if P_HomingAttack(player.mo, player.target) == true then --if the homing attack connects player.pflags = $&~PF_THOKKED --remove the PF_THOKKED flag to enable another air action end end end end)
These hooks, assuming that I got everything right, combineCA_THOK
andCA_HOMINGTHOK
to allow a full-speed thok in the absence of enemies while also having a homing attack when enemies are present, while also allowing the Homing Attack to be chained like in Sonic and the Secret Rings and other 3D Sonic games.satsr_sonic
would be the value of yourname
parameter in yourS_SKIN
; you can set it to whatever you want, but I presume that's what you'd like to call it.
Also, any variable whose name is preceded byplayer.mo
could alternatively be preceded byp
,p.mo
, orplayer
.p
is an alternative way to refer toplayer
(but you have to stick with one or the other throughout your Lua script, otherwise you'll run into errors).p
/player
stores your variable withinplayer_t
(the player), whilep.mo
/player.mo
stores your variable within themobj_t
tied to theMT_PLAYER
object that the player controls. Sometimes it matters which userdata structure you put a variable in, but I've found that it doesn't matter for the majority of purposes. So while you can use either of them for custom variables throughout your script and interchange them (for pre-existing variables, you have to use what the game already puts them in; see the wiki's page on userdata structures), you have to be consistent with usingp
/player
orp.mo
/player.mo
for a given custom variable once you've set it up to use one userdata structure or the other (call the wrong userdata structure, and you won't get your variable; however, you can use ap
/player
variable and ap.mo
/player.mo
variable with the same name if you really want to, given that they would actually be two separate variables, but I wouldn't recommend it, since that would get confusing in a hurry).
Post automatically merged:
I've dedicated the first Lua file to binding all of the buttons and other variables that will be used throughout the rest of the script:
Defining variables://Defining variables addHook ("PlayerThink", function(player) if player.mo and player.mo.valid then if player.mo.skin ~= "satsr_sonic" then return end if player.custom1down == nil then --if the player does not have this variable yet player.custom1down = 0 --give it a starting number end if player.cmd.buttons & BT_CUSTOM1 > 0 then --if the player is pressing Custom 1 player.custom1down = $1 + 1 --increment custom1 variable else player.custom1down = 0 --reset custom1 variable end if player.custom2down == nil then --if the player does not have this variable yet player.custom2down = 0 --give it a starting number end if player.cmd.buttons & BT_CUSTOM2 > 0 then --if the player is pressing Custom 2 player.custom2down = $1 + 1 --increment custom2 variable else player.custom2down = 0 --reset custom2 variable end if player.custom3down == nil then --if the player does not have this variable yet player.custom3down = 0 --give it a starting number end if player.cmd.buttons & BT_CUSTOM3 > 0 then --if the player is pressing Custom 3 player.custom3down = $1 + 1 --increment custom3 variable else player.custom3down = 0 --reset custom3 variable end if player.tossflagdown == nil then --if the player does not have this variable yet player.tossflagdown = 0 --give it a starting number end if player.cmd.buttons & BT_TOSSFLAG > 0 then --if the player is pressing Toss Flag player.tossflagdown = $1 + 1 --increment tossflag variable else player.tossflagdown = 0 --reset tossflag variable end if player.firenormaldown == nil then --if the player does not have this variable yet player.firenormaldown = 0 --give it a starting number end if player.cmd.buttons & BT_FIRENORMAL > 0 then --if the player is pressing Fire Normal player.firenormaldown = $1 + 1 --increment firenormal variable else player.firenormaldown = 0 --reset firenormal variable end if player.cmd.buttons & BT_ATTACK > 0 then --if the player is pressing Fire player.firedown = $1 + 1 --increment fire variable else player.firedown = 0 --reset fire variable end if player.homingattack == nil then player.homingattack = 0 end if player.mo.soulgauge == nil then player.mo.soulgauge = 200 end if player.slowedtime == nil then player.slowedtime = 0 end if player.mo.timebreak == nil then player.mo.timebreak = 0 end if player.mo.speedbreak == nil then player.mo.speedbreak = 0 end end end)
The only button variables I actually plan to use here arecustom1down
andcustom2down
, but it's probably good practice to bind them all.homingattack
will serve as a timer for the Homing Attack.soulgauge
will be the fuel for Time Break and Speed Break.slowedtime
will reduceplayer.realtime
based on how long Time Break has been in use.timebreak
andspeedbreak
will tell the game when Time Break and Speed Break are in use, as well as measuring how long they've been in use if that is necessary for some reason.