-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
314 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1 @@ | ||
# NSModTemplate | ||
A template repository for Northstar mods with a ~~mostly~~ pre-configured github action for publishing to Thunderstore | ||
|
||
## Usage | ||
<ol> | ||
<li> Click the <code>Use this template</code> button on the top right of the repo's landing page (<a href="https://github.com/GreenTF/NSModTemplate">here</a>)</li> | ||
<li> Give the new repo a name and make sure it's set to <code>public</code></li> | ||
<li> <details><summary> In the <code>settings</code> tab, under <code>actions</code> -> <code>general</code>, set <code>Actions permissions</code> to <code>Allow all actions and reusable workflows</code></summary> | ||
<img src="https://user-images.githubusercontent.com/4367791/180306016-04bfc321-b60f-4ed0-ac0c-5a6065036e2c.png" /> | ||
</details></li> | ||
<li> <details><summary> Also in <code>settings</code>, under <code>secrets</code> -> <code>actions</code>, add your Thunderstore token as a secret named <code>TS_KEY</code> (Steps for getting a token can be found <a href="https://github.com/GreenTF/upload-thunderstore-package/wiki">here</a>)</summary> | ||
<img src="https://user-images.githubusercontent.com/4367791/180306285-60dd51ec-0448-44af-aa92-682599c6c0f4.png" /> | ||
<img src="https://user-images.githubusercontent.com/4367791/180306391-a217f309-e875-4e74-8270-8155c60dbcdc.png" /> | ||
</details> | ||
</li> | ||
<li> <details><summary>Edit <code>.github/workflows/publish.yml</code> ~line 43 to add a description for your mod </summary> | ||
<img src="https://user-images.githubusercontent.com/4367791/180337843-5213db45-850b-4759-98c5-9ad47cbab7ba.png" /> | ||
</details> | ||
</li> | ||
|
||
<li> Update this README and <code>icon.png</code> as they will be used by Thunderstore as well </li> | ||
<li> Write your mod! (HINT: Find the docs <a href="https://r2northstar.readthedocs.io/en/latest/guides/gettingstarted.html">here</a>) </li> | ||
<li> Before pushing large files (100mb or larger), run <code>concat_assets.sh</code> and commit the archives instead. Your archives will be automatically concatted and extracted when creating a github release so the mod is downloadable from thunderstore without any extra steps</li> | ||
</ol> | ||
|
||
|
||
#idk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,25 @@ | ||
{ | ||
"Name": "Example.Mod", | ||
"Description": "A cool mod that does cool stuff", | ||
"Version": "1.0.0", | ||
"DownloadLink": "https://northstar.thunderstore.io/package/download/Example/Mod/", | ||
|
||
"LoadPriority": 0, | ||
"ConVars": [], | ||
"Scripts": [{ | ||
"Path": "example.nut", | ||
"RunOn": "(CLIENT || SERVER) && MP" | ||
"ClientCallback": { | ||
"Before":"example_callback" | ||
} | ||
}], | ||
"Localisation": [] | ||
} | ||
"Name": "Odd.Holosprays", | ||
"Description": "A cool mod that does cool stuff", | ||
"Version": "1.0.0", | ||
"DownloadLink": "https://northstar.thunderstore.io/package/download/Example/Mod/", | ||
"LoadPriority": 0, | ||
"ConVars": [], | ||
"Scripts": [ | ||
{ | ||
"Path": "holosprays.nut", | ||
"RunOn": "SERVER", | ||
"ServerCallback": { | ||
"Before": "InitHoloSpray" | ||
} | ||
}, | ||
{ | ||
"Path": "playerPings.nut", | ||
"RunOn": "SERVER", | ||
"ServerCallback": { | ||
"Before": "InitPlayerPings" | ||
} | ||
} | ||
], | ||
"Localisation": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
global function InitHoloSpray | ||
global function CreateSprite | ||
global function TraceFromEnt | ||
struct SprayInfo { | ||
asset material | ||
float scale | ||
string color | ||
vector offset // extra position offset from the base | ||
} | ||
|
||
SprayInfo function _SprayInfo( asset material, float scale = 0.75, string color = "200 200 200", vector offset = <0,0,30> ) | ||
{ | ||
PrecacheSprite( material ) | ||
SprayInfo s | ||
s.material = material | ||
s.scale = scale | ||
s.color = color | ||
s.offset = offset | ||
return s | ||
} | ||
|
||
|
||
table< entity, array<entity> > holoSpraysOfPlayer | ||
array<SprayInfo> sprayInfos | ||
|
||
void function InitHoloSpray() | ||
{ | ||
sprayInfos = [ | ||
_SprayInfo( $"materials/ui/scoreboard_mcorp_logo.vmt", 0.5, "200 200 200", <0,0,60> ) | ||
_SprayInfo( $"materials/ui/scoreboard_imc_logo.vmt", 0.5, "200 200 200", <0,0,60> ) | ||
] | ||
AddCallback_OnClientConnected(OnPlayerConnected) | ||
AddCallback_OnClientDisconnected(OnPlayerDisconnected) | ||
} | ||
|
||
void function OnPlayerConnected( entity player ) | ||
{ | ||
AddButtonPressedPlayerInputCallback( player, IN_USE, OnUseHoloSpray ) | ||
holoSpraysOfPlayer[player] <- [] | ||
thread void function() : ( player ) { | ||
wait RandomFloatRange( 0.0, 0.5 ) | ||
NSSendInfoMessageToPlayer( player, "You can use HOLOSPRAYS on this server, simply press %%use%% to throw yours" ) | ||
}() | ||
|
||
} | ||
|
||
void function OnPlayerDisconnected( entity player ) | ||
{ | ||
foreach( entity spray in holoSpraysOfPlayer[ player ] ) | ||
{ | ||
if( !IsValid( spray ) ) | ||
continue | ||
spray.Destroy() | ||
} | ||
delete holoSpraysOfPlayer[ player ] | ||
} | ||
|
||
|
||
void function OnUseHoloSpray( entity player ) | ||
{ | ||
array<entity> sprays = holoSpraysOfPlayer[ player ] | ||
if(sprays.len() >= 5) //spam is not cool | ||
{ | ||
sprays[0].Destroy() // destroy the base | ||
holoSpraysOfPlayer[player] = sprays.slice(1) // remove the reference | ||
} | ||
|
||
const float force = 500.0 // initial force of the base pad | ||
entity base = CreatePropPhysics( $"models/gameplay/health_pickup_small.mdl", player.EyePosition() - <0,0,20>, <0,0,0> ) | ||
base.kv.solid = 0 | ||
|
||
base.SetOwner( player ) | ||
|
||
thread SpawnHoloSprite( base ) | ||
|
||
holoSpraysOfPlayer[player].append( base ) | ||
base.SetVelocity( ( player.GetViewVector() ) * force ) | ||
} | ||
|
||
void function SpawnHoloSprite( entity base ) | ||
{ | ||
base.EndSignal( "OnDestroy" ) | ||
entity sprite | ||
entity light | ||
WaitFrame() | ||
|
||
while( IsValid( base ) ) | ||
{ | ||
|
||
TraceResults hit = OriginToFirst( base ) | ||
if( Length( base.GetOrigin() - hit.endPos ) <= 10 ) //is object close to the floor | ||
{ | ||
//make sure the object doesnt roll | ||
base.SetVelocity( <0,0,0> ) | ||
//adjust angles to surface, <-90,0,0> is needed because we went the medkit to lie down flat | ||
base.SetAngles( < -90,0,0 > + AnglesOnSurface( hit.surfaceNormal, AnglesToForward( base.GetAngles() ) ) ) | ||
|
||
entity mover = CreateExpensiveScriptMover( base.GetOrigin(), base.GetAngles() ) | ||
base.SetParent( mover ) | ||
mover.NonPhysicsMoveTo( hit.endPos + <0,0,5.5>, 0.3, 0.0, 0.0 ) | ||
|
||
base.SetParent( hit.hitEnt ) | ||
|
||
//make sure the object doesnt rotate too much | ||
base.StopPhysics() | ||
|
||
SprayInfo info = sprayInfos.getrandom() | ||
sprite = CreateSprite( base.GetCenter() + info.offset, <0,0,0>, info.material, "200 200 200", info.scale ) | ||
sprite.SetParent( base ) | ||
light = CreateSprite( base.GetCenter() + <0,0,6.5>, <0,0,0>, $"sprites/glow_05.vmt", "200 200 200", 0.75 ) | ||
light.SetParent( base ) | ||
|
||
break | ||
} | ||
WaitFrame() | ||
} | ||
} | ||
|
||
// Trace straight down from the provided origin until the trace hits the world, a mover or a Titan (Titans break idk) | ||
TraceResults function OriginToFirst( entity base ) | ||
{ | ||
entity lastHit | ||
TraceResults traceResult | ||
array<entity> ignore = [] | ||
vector origin1 = base.GetOrigin() | ||
vector origin = origin1 + <1,1,1> | ||
printt( "TEST " , origin) | ||
printt( "TEST2 " , <origin.x, origin.y, origin.z - 2000> ) | ||
printt( "BASE ", base) | ||
printt( "OWNER ", base.GetOwner() ) | ||
printt( "NAME ", base.GetOwner().GetPlayerName() ) | ||
|
||
do | ||
{ | ||
vector endOrigin = <origin.x, origin.y, origin.z - 2000> | ||
traceResult = TraceLine( origin, endOrigin, ignore, TRACE_MASK_NPCWORLDSTATIC, TRACE_COLLISION_GROUP_NONE ) | ||
lastHit = traceResult.hitEnt | ||
if(!IsValid(lastHit)) | ||
continue | ||
ignore.append( traceResult.hitEnt ) | ||
} while( IsValid( lastHit ) && !lastHit.IsWorld() && !lastHit.IsTitan() && !(lastHit.GetClassName() == "worldspawn") && !(lastHit instanceof CNPC_Titan) && !(lastHit.GetClassName() == "script_mover") ) | ||
return traceResult | ||
} | ||
|
||
// Create a 2D sprite at position | ||
entity function CreateSprite( vector origin, vector angles, asset sprite, string lightcolor = "255 0 0", float scale = 0.5, int rendermode = 9 ) | ||
{ | ||
// attach a light so we can see it | ||
entity env_sprite = CreateEntity( "env_sprite" ) | ||
env_sprite.SetScriptName( UniqueString( "molotov_sprite" ) ) | ||
env_sprite.kv.rendermode = rendermode //these do NOT follow any pattern, trial an error is your friend, as you dont have any others anyway (they go from like 1 to 10 or sth, I hontely dont know) | ||
env_sprite.kv.origin = origin | ||
env_sprite.kv.angles = angles | ||
env_sprite.kv.rendercolor = lightcolor | ||
env_sprite.kv.renderamt = 255 | ||
env_sprite.kv.framerate = "10.0" | ||
env_sprite.SetValueForModelKey( sprite ) | ||
env_sprite.kv.scale = string( scale ) | ||
env_sprite.kv.spawnflags = 1 | ||
env_sprite.kv.GlowProxySize = 16.0 | ||
env_sprite.kv.HDRColorScale = 1.0 | ||
DispatchSpawn( env_sprite ) | ||
EntFireByHandle( env_sprite, "ShowSprite", "", 0, null, null ) | ||
|
||
return env_sprite | ||
} | ||
|
||
// Trace to the point an entity looks at | ||
TraceResults function TraceFromEnt( entity p ) | ||
{ | ||
TraceResults traceResults = TraceLineHighDetail( p.EyePosition(), | ||
p.EyePosition() + p.GetViewVector() * 10000, | ||
p, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_NONE ) | ||
return traceResults | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
global function InitPlayerPings | ||
|
||
struct Ping { | ||
entity sprite | ||
float time | ||
} | ||
|
||
table< entity, array<Ping> > playerPings | ||
const PING_DURATION = 15.0 | ||
|
||
void function InitPlayerPings() | ||
{ | ||
PrecacheSprite($"materials/ui/hud/attacker_offscreen.vmt") | ||
PrecacheSprite($"materials/vgui/hud/weapons/target_ring_arc_tool_inner.vmt") | ||
RegisterSignal( "PingDestroyed" ) | ||
|
||
AddCallback_OnClientConnected(OnPlayerConnected) | ||
AddCallback_OnClientDisconnected(OnPlayerDisconnected) | ||
} | ||
|
||
void function OnPlayerConnected( entity player ) | ||
{ | ||
AddButtonPressedPlayerInputCallback( player, IN_PING, OnUsePing ) | ||
playerPings[ player ] <- [] | ||
} | ||
|
||
void function OnPlayerDisconnected( entity player ) | ||
{ | ||
//avoid a crash by removing their pings | ||
delete playerPings[player] | ||
} | ||
|
||
string function GenerateRGBValueByIndex( int index ) | ||
{ | ||
//if somehow the player was not found in the array of his team it doesnt crash | ||
if( index == -1 ) | ||
return "100 100 100" | ||
|
||
int[3] RGBValue | ||
|
||
//rotates the columns around for each index | ||
int changeRGBindex = index % 3 | ||
|
||
int potentialNewColour = RGBValue[ changeRGBindex ] + index * 50 | ||
RGBValue[ changeRGBindex ] = potentialNewColour > 255 ? potentialNewColour % 255 : potentialNewColour | ||
|
||
//following column of the first one LOL | ||
changeRGBindex = (index+1) % 3 | ||
RGBValue[ changeRGBindex ] = 200 // fix value for RGB that constantly roates infront of the changine value | ||
|
||
return format( "%i %i %i", RGBValue[0], RGBValue[1], RGBValue[2] ) | ||
} | ||
|
||
|
||
void function OnUsePing( entity player ) | ||
{ | ||
thread SpawnPing( player ) | ||
} | ||
|
||
bool function isEnemyPing( entity player, vector newPing ) | ||
{ | ||
foreach( Ping p in playerPings[ player ] ) | ||
{ | ||
if( Time() - p.time <= 1.0 && LengthSqr( p.sprite.GetOrigin() - newPing ) <= 2000.0 ) | ||
{ | ||
Signal( p, "PingDestroyed" ) | ||
p.sprite.Destroy() | ||
playerPings[ player ].fastremovebyvalue( p ) | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
void function SpawnPing( entity player ) | ||
{ | ||
//all pings for a player so far | ||
array<Ping> pings = playerPings[player] | ||
if(pings.len() >= 5) //spam is not cool | ||
{ | ||
Signal( pings[0], "PingDestroyed" ) | ||
if( IsValid( pings[0].sprite ) ) | ||
pings[0].sprite.Destroy() | ||
playerPings[player] = pings.slice(1) | ||
} | ||
|
||
TraceResults trace = TraceFromEnt( player ) | ||
bool enemyPing = isEnemyPing( player, trace.endPos ) | ||
entity sprite = CreateSprite( trace.endPos, <0,0,0>, enemyPing ? $"materials/ui/hud/attacker_offscreen.vmt" : $"materials/vgui/hud/weapons/target_ring_arc_tool_inner.vmt", enemyPing ? "255 0 0" : GenerateRGBValueByIndex( GetPlayerArrayOfTeam( player.GetTeam( ) ).find(player) ), enemyPing ? 0.6 :0.3 , 5 ) // 5 | ||
SetTeam( sprite, player.GetTeam() ) | ||
//set it so only you can your team can see them | ||
sprite.kv.VisibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_OWNER | ||
// | ||
sprite.SetOwner( player ) | ||
//makes the ping attach to the object that moves | ||
sprite.SetParent( trace.hitEnt ) | ||
//track the time a ping was send | ||
Ping p | ||
p.sprite = sprite | ||
p.time = Time() | ||
playerPings[player].append( p ) | ||
|
||
//ping gets detroyed after a set time | ||
thread DestroyPingDelayed( p, PING_DURATION ) | ||
} | ||
|
||
void function DestroyPingDelayed( Ping p, float duration ) | ||
{ | ||
EndSignal( p, "PingDestroyed" ) | ||
wait duration | ||
if( IsValid( p.sprite ) ) | ||
p.sprite.Destroy() | ||
playerPings[ p.sprite.GetOwner() ].slice( 1 ) | ||
} |