Author Name: Near
Submission Name: PresentDay
Short Description: Make bullet become present which spawns weapon/powerup when opened. Sometimes it spawns something else...
Code: Select all
public void OnStartup()
{
Game.GetPlayers()[0].GiveWeaponItem(WeaponItem.M60);
Game.RunCommand("ia 1");
Events.ProjectileCreatedCallback.Start(OnProjectileCreated);
Events.ObjectTerminatedCallback.Start(OnObjectTerminated);
}
private static Random rnd = new Random();
private static T GetItem<T>(List<T> list)
{
var rndIndex = rnd.Next(list.Count);
return list[rndIndex];
}
private Dictionary<int, IObject> m_customBullets = new Dictionary<int, IObject>();
private readonly List<string> m_presents = new List<string>()
{
"XmasPresent00",
"WpnPistol",
"WpnPistol45",
"WpnSilencedPistol",
"WpnMachinePistol",
"WpnMagnum",
"WpnRevolver",
"WpnPumpShotgun",
"WpnDarkShotgun",
"WpnTommygun",
"WpnSMG",
"WpnM60",
"WpnPipeWrench",
"WpnChain",
"WpnWhip",
"WpnHammer",
"WpnKatana",
"WpnMachete",
"WpnChainsaw",
"WpnKnife",
"WpnSawedoff",
"WpnBat",
"WpnBaton",
"WpnShockBaton",
"WpnLeadPipe",
"WpnUzi",
"WpnSilencedUzi",
"WpnBazooka",
"WpnAxe",
"WpnAssaultRifle",
"WpnMP50",
"WpnSniperRifle",
"WpnCarbine",
"WpnFlamethrower",
"ItemPills",
"ItemMedkit",
"ItemSlomo5",
"ItemSlomo10",
"ItemStrengthBoost",
"ItemSpeedBoost",
"ItemLaserSight",
"ItemBouncingAmmo",
"ItemFireAmmo",
"WpnGrenades",
"WpnMolotovs",
"WpnMines",
"WpnShuriken",
"WpnBow",
"WpnFlareGun",
"WpnGrenadeLauncher",
};
private readonly List<string> m_oofs = new List<string>()
{
"WpnGrenadesThrown",
"WpnMolotovsThrown",
"WpnMineThrown",
};
private void OnObjectTerminated(IObject[] objs)
{
foreach (var obj in objs)
{
if (m_customBullets.ContainsKey(obj.UniqueID))
{
var customBullet = m_customBullets[obj.UniqueID];
var position = customBullet.GetWorldPosition();
// normally, the present spawn some random shits upon destroyed. make the present disappeared
// and spawn something else as a workaround
customBullet.SetWorldPosition(new Vector2(-5000, 5000));
var rndNum = rnd.Next(0, 100);
if (rndNum < 1) // big oof
{
SpawnBadSanta(position);
}
if (1 <= rndNum && rndNum < 5)
{
Game.CreateObject(GetItem(m_oofs), position);
}
if (5 <= rndNum && rndNum < 30)
{
Game.CreateObject(GetItem(m_presents), position);
}
m_customBullets.Remove(obj.UniqueID);
}
}
}
private void SpawnBadSanta(Vector2 position)
{
var player = Game.CreatePlayer(position);
player.SetModifiers(new PlayerModifiers()
{
MaxHealth = 200,
CurrentHealth = 200,
ExplosionDamageTakenModifier = 0.5f,
MeleeForceModifier = 1.5f,
SizeModifier = 1.1f,
InfiniteAmmo = 1,
});
player.SetProfile(new IProfile()
{
Name = "Bad Santa",
Accesory = new IProfileClothingItem("SantaMask", "ClothingLightGray", "ClothingLightGray", ""),
ChestOver = new IProfileClothingItem("Coat", "ClothingRed", "ClothingLightGray", ""),
ChestUnder = new IProfileClothingItem("SleevelessShirt", "ClothingLightGray", "ClothingLightGray", ""),
Feet = new IProfileClothingItem("BootsBlack", "ClothingBrown", "ClothingLightGray", ""),
Gender = Gender.Male,
Hands = new IProfileClothingItem("SafetyGlovesBlack", "ClothingGray", "ClothingLightGray", ""),
Head = new IProfileClothingItem("SantaHat", "ClothingRed", "ClothingLightGray", ""),
Legs = new IProfileClothingItem("Pants", "ClothingRed", "ClothingLightGray", ""),
Skin = new IProfileClothingItem("Tattoos", "Skin3", "ClothingPink", ""),
Waist = new IProfileClothingItem("Belt", "ClothingDarkRed", "ClothingLightYellow", ""),
});
player.SetBotName("Bad Santa");
player.SetBotBehaviorSet(BotBehaviorSet.GetBotBehaviorPredefinedSet(PredefinedAIType.ChallengeA));
player.SetBotBehaviorActive(true);
player.SetTeam(PlayerTeam.Independent);
player.GiveWeaponItem(WeaponItem.KNIFE);
player.GiveWeaponItem(WeaponItem.M60);
player.GiveWeaponItem(WeaponItem.UZI);
Game.CreateDialogue("Ho ho ho!", new Color(128, 32, 32), player);
}
private void OnProjectileCreated(IProjectile[] projectiles)
{
foreach (var projectile in projectiles)
{
switch (projectile.ProjectileItem)
{
case ProjectileItem.BAZOOKA:
case ProjectileItem.GRENADE_LAUNCHER:
break;
default:
ToPresentBullet(projectile);
break;
}
}
}
private void ToPresentBullet(IProjectile projectile)
{
var customBullet = Game.CreateObject("XmasPresent00",
worldPosition: projectile.Position,
angle: (float)Math.Atan2(projectile.Direction.X, projectile.Direction.Y),
linearVelocity: projectile.Velocity / 50 + new Vector2(0, 3),
angularVelocity: 50f * (int)(projectile.Direction.X % 1),
faceDirection: (int)(projectile.Direction.X % 1)
);
customBullet.TrackAsMissile(true);
m_customBullets.Add(customBullet.UniqueID, customBullet);
projectile.FlagForRemoval();
}
Author Name: Near
Submission Name: Infected Bullet
Short Description: Bullets have a small chance to infect player. Infected players turn into zombie after dying for a while
Code: Select all
private class InfectedCorpse
{
public static int TimeToTurnIntoZombie = 5000;
public IPlayer Body { get; set; }
public float DeathTime { get; private set; }
public bool IsTurningIntoZombie { get; private set; }
public bool CanTurnIntoZombie { get; private set; }
public bool IsZombie { get; private set; }
private bool TurnIntoZombie()
{
if (Body.IsRemoved || Body.IsBurnedCorpse) return false;
var zombieBody = Game.CreatePlayer(Body.GetWorldPosition());
if (rnd.Next(0, 100) < 10)
{
Game.CreateDialogue("Brainzz", zombieBody);
}
zombieBody.SetBotBehaviorSet(BotBehaviorSet.GetBotBehaviorPredefinedSet(GetZombieType(Body)));
zombieBody.SetTeam(ZombieTeam);
var modifiers = Body.GetModifiers();
modifiers.RunSpeedModifier -= 0.15f;
modifiers.SprintSpeedModifier -= 0.15f;
modifiers.CurrentHealth = modifiers.MaxHealth * 0.75f;
zombieBody.SetModifiers(modifiers);
var profile = Body.GetProfile();
zombieBody.SetProfile(ToZombieProfile(profile));
zombieBody.SetBotName(Body.Name);
Body.Remove();
Body = zombieBody;
Body.SetBotBehaivorActive(false);
Body.AddCommand(new PlayerCommand(PlayerCommandType.StartCrouch));
IsTurningIntoZombie = true;
return true;
}
public InfectedCorpse(IPlayer player)
{
Body = player;
IsTurningIntoZombie = false;
IsZombie = false;
CanTurnIntoZombie = true;
DeathTime = Game.TotalElapsedGameTime;
}
public void Update()
{
if (IsElapsed(DeathTime, TimeToTurnIntoZombie))
{
if (!IsTurningIntoZombie)
{
CanTurnIntoZombie = TurnIntoZombie();
}
if (!IsZombie)
{
UpdateTurningIntoZombieAnimation();
}
}
}
private bool isKneeling;
private float kneelingTime;
private void UpdateTurningIntoZombieAnimation()
{
if (!isKneeling)
{
kneelingTime = Game.TotalElapsedGameTime;
isKneeling = true;
}
else
{
if (IsElapsed(kneelingTime, 700))
{
Body.AddCommand(new PlayerCommand(PlayerCommandType.StopCrouch));
Body.SetBotBehaivorActive(true);
IsZombie = true;
}
}
}
}
private class TheInfected
{
private IPlayer m_player;
public TheInfected(IPlayer player)
{
m_player = player;
}
private float m_damageTimer = 0;
public void Update(float dt)
{
if (!m_player.IsRemoved && !m_player.IsBurnedCorpse)
{
m_damageTimer += dt;
if (m_damageTimer > 2000)
{
var health = m_player.GetHealth();
health -= 0.5f;
m_player.SetHealth(health);
Game.PlayEffect(EffectName.BloodTrail, m_player.GetWorldPosition());
m_damageTimer = 0;
}
}
}
}
/// <summary>
/// Placeholder constructor that's not to be included in the ScriptWindow!
/// </summary>
private static Random rnd = new Random();
private static readonly PlayerTeam ZombieTeam = PlayerTeam.Team4;
private static Dictionary<int, TheInfected> m_infectedPlayers = new Dictionary<int, TheInfected>();
private static List<InfectedCorpse> m_infectedCorpses = new List<InfectedCorpse>();
private Dictionary<int, IProjectile> m_customBullets = new Dictionary<int, IProjectile>();
public void OnStartup()
{
Game.GetPlayers()[0].GiveWeaponItem(WeaponItem.M60);
Game.RunCommand("ia 1");
Events.UpdateCallback.Start(OnUpdate);
Events.ProjectileCreatedCallback.Start(OnProjectileCreated);
Events.ProjectileHitCallback.Start(OnProjectileHit);
Events.PlayerDamageCallback.Start(OnPlayerDamage);
Events.PlayerDeathCallback.Start(OnPlayerDeath);
}
private void OnProjectileCreated(IProjectile[] projectiles)
{
foreach (var projectile in projectiles)
{
switch (projectile.ProjectileItem)
{
case ProjectileItem.BAZOOKA:
case ProjectileItem.GRENADE_LAUNCHER:
break;
default:
ToInfectedBullet(projectile);
break;
}
}
}
private void OnProjectileHit(IProjectile projectile, ProjectileHitArgs args)
{
if (args.RemoveFlag)
{
m_customBullets.Remove(projectile.InstanceID);
}
}
private void OnPlayerDamage(IPlayer player, PlayerDamageArgs args)
{
if (args.DamageType == PlayerDamageEventType.Projectile)
{
if (m_customBullets.ContainsKey(args.SourceID))
{
if (rnd.Next(0, 100) < 15 && !CanInfectFrom(player) && !player.IsBurnedCorpse)
{
Infect(player);
}
}
}
if (args.DamageType == PlayerDamageEventType.Melee)
{
var attacker = Game.GetPlayer(args.SourceID);
UpdateInfectedStatus(player, attacker, args);
}
}
private void UpdateInfectedStatus(IPlayer player, IPlayer attacker, PlayerDamageArgs args)
{
if (player == null) return;
if (!CanInfectFrom(player) && !player.IsBurnedCorpse && attacker != null)
{
var attackerPunching = args.DamageType == PlayerDamageEventType.Melee
&& attacker.CurrentWeaponDrawn == WeaponItemType.NONE
&& !attacker.IsKicking && !attacker.IsJumpKicking;
if (CanInfectFrom(attacker) && attackerPunching)
{
Infect(player);
}
}
}
private static void OnPlayerDeath(IPlayer player, PlayerDeathArgs args)
{
if (player == null) return;
if (m_infectedPlayers.ContainsKey(player.UniqueID))
{
m_infectedCorpses.Add(new InfectedCorpse(player));
}
}
private bool CanInfectFrom(IPlayer player)
{
return m_infectedPlayers.ContainsKey(player.UniqueID)
|| HasZombieSkin(player);
}
private void Infect(IPlayer player)
{
Game.PlayEffect(EffectName.CustomFloatText, player.GetWorldPosition(), "infected");
m_infectedPlayers.Add(player.UniqueID, new TheInfected(player));
if (player.IsDead)
{
m_infectedCorpses.Add(new InfectedCorpse(player));
}
}
private void OnUpdate(float dt)
{
UpdateTheInfected(dt);
}
private void UpdateTheInfected(float dt)
{
foreach (var player in m_infectedPlayers.Values)
{
player.Update(dt);
}
foreach (var corpse in m_infectedCorpses.ToList())
{
corpse.Update();
if (corpse.IsZombie || !corpse.CanTurnIntoZombie)
{
m_infectedCorpses.Remove(corpse);
}
}
}
private void ToInfectedBullet(IProjectile projectile)
{
if (!m_customBullets.ContainsKey(projectile.InstanceID))
{
m_customBullets.Add(projectile.InstanceID, projectile);
}
}
public static bool IsElapsed(float timeStarted, float timeToElapse)
{
return Game.TotalElapsedGameTime - timeStarted >= timeToElapse;
}
#region Zombie helper methods
public static PredefinedAIType GetZombieType(IPlayer player)
{
if (player == null)
{
throw new Exception("Player cannot be null");
}
if (player.IsUser)
{
return PredefinedAIType.ZombieB;
}
else
{
var playerAI = player.GetBotBehavior().PredefinedAI;
switch (playerAI)
{
// Expert, Hard
case PredefinedAIType.BotA:
case PredefinedAIType.BotB:
case PredefinedAIType.ChallengeA:
case PredefinedAIType.MeleeB:
return PredefinedAIType.ZombieB;
default:
return PredefinedAIType.ZombieA;
}
}
}
private static IProfile ToZombieProfile(IProfile profile)
{
switch (profile.Skin.Name)
{
case "Normal":
case "Tattoos":
profile.Skin = new IProfileClothingItem("Zombie", "Skin1", "ClothingLightGray", "");
break;
case "Normal_fem":
case "Tattoos_fem":
profile.Skin = new IProfileClothingItem("Zombie_fem", "Skin1", "ClothingLightGray", "");
break;
case "BearSkin":
profile.Skin = new IProfileClothingItem("FrankenbearSkin", "ClothingDarkGray", "ClothingLightBlue", "");
break;
}
return profile;
}
private bool HasZombieSkin(IPlayer player)
{
return player.GetProfile().Skin.Name == "Zombie"
|| player.GetProfile().Skin.Name == "Zombie_fem"
|| player.GetProfile().Skin.Name == "FrankenbearSkin";
}
#endregion
Author Name: Near
Submission Name: Electric Bullet
Short Description: EMP bullets that have a chance to stunned player for a while (like shock baton). Have a very small chance to make a blast (stun multiple players in a small area)
Code: Select all
private class StunnedPlayer
{
public static readonly float StunnedTime = 3000f;
private IPlayer m_player;
public int UniqueID
{
get { return m_player.UniqueID; }
}
private bool isStunned;
public bool IsStunned
{
get
{
return isStunned;
}
private set
{
isStunned = value;
if (isStunned)
{
m_player.SetInputEnabled(false);
m_player.AddCommand(new PlayerCommand(PlayerCommandType.DeathKneelInfinite));
}
else
{
m_player.AddCommand(new PlayerCommand(PlayerCommandType.StopDeathKneel));
m_player.SetInputEnabled(true);
}
}
}
public StunnedPlayer(IPlayer player) : this(player, player.GetWorldPosition())
{
}
public StunnedPlayer(IPlayer player, Vector2 hitPosition)
{
m_player = player;
IsStunned = true;
Game.PlayEffect(EffectName.Electric, hitPosition);
if (rnd.Next(0, 100) < 30)
{
Game.SpawnFireNode(hitPosition, Vector2.Zero);
Game.PlayEffect(EffectName.FireTrail, hitPosition);
}
}
private float m_stunnedTimer = 0;
private float m_stunnedEffectTimer = 0;
public void Update(float dt)
{
if (IsStunned)
{
m_stunnedTimer += dt;
m_stunnedEffectTimer += dt;
if (m_stunnedEffectTimer >= 400)
{
var position = m_player.GetWorldPosition();
position.X += rnd.Next(-10, 10);
position.Y += rnd.Next(-10, 10);
Game.PlayEffect(EffectName.Electric, position);
m_stunnedEffectTimer = 0;
}
if (m_stunnedTimer >= StunnedTime)
{
IsStunned = false;
}
}
}
}
/// <summary>
/// Placeholder constructor that's not to be included in the ScriptWindow!
/// </summary>
private static Random rnd = new Random();
private Dictionary<int, IProjectile> m_customBullets = new Dictionary<int, IProjectile>();
private static Dictionary<int, StunnedPlayer> m_stunnedPlayers = new Dictionary<int, StunnedPlayer>();
public void OnStartup()
{
var me = Game.GetPlayers()[0];
me.GiveWeaponItem(WeaponItem.PISTOL);
me.GiveWeaponItem(WeaponItem.M60);
var mod = me.GetModifiers();
mod.MeleeStunImmunity = 1;
me.SetModifiers(mod);
Game.RunCommand("ia 1");
Game.RunCommand("ih 1");
foreach (var player in Game.GetPlayers())
{
if (player == me) continue;
var bot = Game.CreatePlayer(player.GetWorldPosition());
bot.SetBotName("bot " + rnd.Next(0, 1000));
bot.SetBotBehaviorSet(BotBehaviorSet.GetBotBehaviorPredefinedSet(PredefinedAIType.BotD));
bot.SetBotBehaviorActive(true);
player.Remove();
}
Events.UpdateCallback.Start(OnUpdate);
Events.ProjectileCreatedCallback.Start(OnProjectileCreated);
Events.ProjectileHitCallback.Start(OnProjectileHit);
}
private void OnProjectileCreated(IProjectile[] projectiles)
{
foreach (var projectile in projectiles)
{
switch (projectile.ProjectileItem)
{
case ProjectileItem.BAZOOKA:
case ProjectileItem.GRENADE_LAUNCHER:
break;
default:
ToEMPBullet(projectile);
break;
}
}
}
private void OnProjectileHit(IProjectile projectile, ProjectileHitArgs args)
{
if (m_customBullets.ContainsKey(projectile.InstanceID))
{
var rndNum = rnd.Next(0, 100);
if (rndNum < 1)
{
ElectrocuteRange(args.HitPosition);
}
if (1 <= rndNum && rndNum < 21)
{
var obj = Game.GetObject(args.HitObjectID);
Electrocute(args);
}
m_customBullets.Remove(projectile.InstanceID);
}
}
private Vector2? m_empBlastCenter = null;
public static readonly float EMPBlastRadius = 15f;
private float m_stunnedRangeTimer = 0f;
private void ElectrocuteRange(Vector2 position)
{
foreach (var player in Game.GetPlayers())
{
if (IsTouchingCircle(player.GetAABB(), position, EMPBlastRadius) && CanBeStunned(player))
{
Game.PlayEffect(EffectName.CustomFloatText, player.GetWorldPosition(), "stunned");
m_stunnedPlayers.Add(player.UniqueID, new StunnedPlayer(player));
}
}
for (var i = 0; i < 360; i += 72) // Play electric effect 5 times in circle (360 / 5 = 72)
{
var degree = i;
var radianAngle = degree * Math.PI / 180.0f;
var direction = new Vector2()
{
X = (float)Math.Cos(radianAngle),
Y = (float)Math.Sin(radianAngle),
};
Game.PlayEffect(EffectName.Electric, position + direction * EMPBlastRadius);
}
m_empBlastCenter = position;
}
private void Electrocute(ProjectileHitArgs args)
{
var position = args.HitPosition;
if (args.IsPlayer)
{
var player = (IPlayer)Game.GetObject(args.HitObjectID);
if (CanBeStunned(player))
{
Game.PlayEffect(EffectName.CustomFloatText, position, "stunned");
m_stunnedPlayers.Add(player.UniqueID, new StunnedPlayer(player, position));
}
}
}
private bool CanBeStunned(IPlayer player)
{
return !player.IsRemoved && !player.IsDead && !m_stunnedPlayers.ContainsKey(player.UniqueID);
}
private void OnUpdate(float dt)
{
UpdatePlayers(dt);
m_stunnedRangeTimer += dt;
if (m_stunnedRangeTimer >= StunnedPlayer.StunnedTime)
{
m_stunnedRangeTimer = 0;
m_empBlastCenter = null;
}
if (m_empBlastCenter != null)
Game.DrawCircle((Vector2)m_empBlastCenter, EMPBlastRadius, Color.Red);
}
private void UpdatePlayers(float dt)
{
foreach (var player in m_stunnedPlayers.Values.ToList())
{
player.Update(dt);
if (!player.IsStunned)
{
m_stunnedPlayers.Remove(player.UniqueID);
}
}
}
private void ToEMPBullet(IProjectile projectile)
{
if (!m_customBullets.ContainsKey(projectile.InstanceID))
{
m_customBullets.Add(projectile.InstanceID, projectile);
}
}
private bool IsTouchingCircle(Area area, Vector2 center, float radius)
{
var lines = new List<Vector2[]>()
{
new Vector2[] { area.BottomRight, area.BottomLeft },
new Vector2[] { area.BottomLeft, area.TopLeft },
new Vector2[] { area.TopLeft, area.TopRight },
new Vector2[] { area.TopRight, area.BottomRight },
};
var minDistanceToCenter = float.MaxValue;
foreach (var line in lines)
{
var distanceToCenter = FindDistanceToSegment(center, line[0], line[1]);
if (distanceToCenter < minDistanceToCenter) minDistanceToCenter = distanceToCenter;
}
return minDistanceToCenter <= radius;
}
// https://stackoverflow.com/a/1501725/9449426
private float FindDistanceToSegment(Vector2 point, Vector2 p1, Vector2 p2)
{
// Return minimum distance between line segment vw and point point
var lengthSquare = (float)(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); // i.e. |p2-p1|^2 - avoid a sqrt
if (lengthSquare == 0.0) return Vector2.Distance(point, p1); // p1 == p2 case
// Consider the line extending the segment, parameterized as p1 + t (p2 - p1).
// We find projection of point point onto the line.
// It falls where t = [(point-p1) . (p2-p1)] / |p2-p1|^2
// We clamp t from [0,1] to handle points outside the segment vw.
var t = MathHelper.Clamp(Vector2.Dot(point - p1, p2 - p1) / lengthSquare, 0, 1);
var projection = p1 + t * (p2 - p1); // Projection falls on the segment
return Vector2.Distance(point, projection);
}
EDIT: Update the scripts to 1.3.0b. Add Electric bullet