From 4178c3da7f9c56750bab830652dc9e1868bbb3ad Mon Sep 17 00:00:00 2001
From: Picoseconds <rw7@proacad.ca>
Date: Sat, 1 Aug 2020 16:59:27 -0600
Subject: [PATCH] feat: added hats

---
 src/moomoo/Game.ts   | 114 +++++++++++++++++++++++++++++++++++++++----
 src/moomoo/Hats.ts   |  37 ++------------
 src/moomoo/Player.ts |  42 ++++++++++++++--
 3 files changed, 149 insertions(+), 44 deletions(-)

diff --git a/src/moomoo/Game.ts b/src/moomoo/Game.ts
index c16c549..cd7f88e 100755
--- a/src/moomoo/Game.ts
+++ b/src/moomoo/Game.ts
@@ -14,9 +14,11 @@ import GameObject from "../gameobjects/GameObject";
 import { PacketType } from "../packets/PacketType";
 import FileAsync from 'lowdb/adapters/FileAsync';
 import { PacketFactory } from "../packets/PacketFactory";
-import { getWeaponDamage, getWeaponAttackDetails, getItemCost, getPlaceable, PrimaryWeapons, getWeaponGatherAmount, getPrerequisiteItem, getGroupID, Weapons, getPrerequisiteWeapon } from "../items/items";
+import { getWeaponDamage, getWeaponAttackDetails, getItemCost, getPlaceable, PrimaryWeapons, getWeaponGatherAmount, getPrerequisiteItem, getGroupID, Weapons, getPrerequisiteWeapon, getPlaceOffset } from "../items/items";
 import { gameObjectSizes, GameObjectType } from "../gameobjects/gameobjects";
 import { getUpgrades, getWeaponUpgrades } from './Upgrades';
+import { getHat } from './Hats';
+import { WeaponVariant } from './Weapons';
 
 let currentGame: Game | null = null;
 
@@ -410,6 +412,11 @@ export default class Game {
     this.state.players.forEach((player) => {
       Physics.movePlayer(player, 33, this.state);
 
+      if (Date.now() - player.lastDot >= 1000) {
+        player.damageOverTime();
+        player.lastDot = Date.now();
+      }
+
       if (player.isAttacking && player.selectedWeapon != Weapons.Shield && player.buildItem == -1) {
         if (Date.now() - player.lastHitTime >= player.getWeaponHitTime()) {
           let nearbyPlayers = player.getNearbyPlayers(this.state);
@@ -424,17 +431,47 @@ export default class Game {
             player.getNearbyGameObjects(this.state)
           );
 
+          let weaponVariant = player.selectedWeapon == player.weapon ?
+            player.primaryWeaponVariant :
+            player.secondaryWeaponVariant;
           for (let hitPlayer of hitPlayers) {
             if (hitPlayer.clanName == player.clanName && hitPlayer.clanName != null) continue;
 
             let dmg = getWeaponDamage(
               player.selectedWeapon,
-              player.selectedWeapon == player.weapon ?
-                player.primaryWeaponVariant :
-                player.secondaryWeaponVariant
+              weaponVariant
             );
 
+            let hat = getHat(player.hatID);
+            let hitPlayerHat = getHat(hitPlayer.hatID);
+
+            if (hat && hat.dmgMultO)
+              dmg *= hat.dmgMultO;
+
+            if (hitPlayerHat) {
+              dmg *= hitPlayerHat.dmgMult || 1;
+
+              if (hitPlayerHat.dmg) {
+                player.health -= hitPlayerHat.dmg * dmg;
+              }
+
+              if (hitPlayerHat.dmgK) {
+                let knockback = hitPlayerHat.dmgK;
+                hitPlayer.velocity.add(
+                  knockback * Math.cos(player.angle),
+                  knockback * Math.sin(player.angle)
+                );
+              }
+            }
+
             hitPlayer.health -= dmg;
+
+            if (weaponVariant === WeaponVariant.Ruby) {
+              hitPlayer.bleedDmg = 5;
+              hitPlayer.bleedAmt = 0;
+              hitPlayer.maxBleedAmt = 5;
+            }
+
             if (hitPlayer.health <= 0 && hitPlayer.client) {
               this.killPlayer(hitPlayer);
               player.kills++;
@@ -456,7 +493,7 @@ export default class Game {
 
             player.client?.socket.send(
               packetFactory.serializePacket(
-                new Packet(PacketType.HEALTH_CHANGE, [hitPlayer.location.x, hitPlayer.location.y, dmg, 1])
+                new Packet(PacketType.HEALTH_CHANGE, [hitPlayer.location.x, hitPlayer.location.y, Math.round(dmg), 1])
               )
             );
           }
@@ -513,10 +550,16 @@ export default class Game {
 
     for (let player of this.state.players) {
       if (player.moveDirection !== null) {
+        let speedMult = player.location.y > 2400 ? 1 : 0.8;
+
+        if (player.hatID !== -1) {
+          speedMult *= getHat(player.hatID)?.spdMult || 1;
+        }
+
         Physics.moveTowards(
           player,
           player.moveDirection,
-          player.location.y > 2400 ? 1 : 0.8,
+          speedMult,
           deltaTime,
           this.state
         );
@@ -696,9 +739,6 @@ export default class Game {
           this.kickClient(client, "Malformed spawn packet!");
         }
         break;
-      case PacketType.BUY_AND_EQUIP:
-        if (packet.data[0] === 1) if (client.player) break;
-        break;
       case PacketType.ATTACK:
         if (client.player) {
           if (packet.data[0]) {
@@ -880,6 +920,62 @@ export default class Game {
           }
         }
         break;
+      case PacketType.BUY_AND_EQUIP:
+        let isAcc = packet.data[2];
+
+        if ((!getHat(packet.data[1]) || getHat(packet.data[1])?.dontSell) && packet.data[1] !== 0) {
+          this.kickClient(client, "Kicked for hacks");
+          return;
+        }
+
+        if (client.player) {
+          if (packet.data[0]) {
+            if (client.ownedHats.includes(packet.data[1])) {
+              this.kickClient(client, "Kicked for hacks");
+            } else {
+              if (client.player.points >= (getHat(packet.data[1])?.price || 0)) {
+                client.ownedHats.push(packet.data[1]);
+                client.socket.send(
+                  packetFactory.serializePacket(
+                    new Packet(
+                      PacketType.UPDATE_STORE,
+                      [0, packet.data[1], isAcc]
+                    )
+                  )
+                );
+              }
+            }
+          } else {
+            if (client.ownedHats.includes(packet.data[1]) || getHat(packet.data[1])?.price === 0 || packet.data[1] === 0) {
+              if (client.player.hatID === packet.data[1]) {
+                client.player.hatID = 0;
+
+                client.socket.send(
+                  packetFactory.serializePacket(
+                    new Packet(
+                      PacketType.UPDATE_STORE,
+                      [1, 0, isAcc]
+                    )
+                  )
+                );
+              } else {
+                client.player.hatID = packet.data[1];
+
+                client.socket.send(
+                  packetFactory.serializePacket(
+                    new Packet(
+                      PacketType.UPDATE_STORE,
+                      [1, packet.data[1], isAcc]
+                    )
+                  )
+                );
+              }
+            } else {
+              this.kickClient(client, "Kicked for hacks");
+            }
+          }
+        }
+        break;
       case PacketType.SELECT_UPGRADE:
         if (client.player) {
           let item = packet.data[0] as number;
diff --git a/src/moomoo/Hats.ts b/src/moomoo/Hats.ts
index 92cc9c5..a50082e 100755
--- a/src/moomoo/Hats.ts
+++ b/src/moomoo/Hats.ts
@@ -1,34 +1,7 @@
-import hats from "../hats.json";
+import hats from "../hats.json"
 
-interface Hat {
-    /**
-     * The id of this hat
-     */
-    id: number;
+function getHat(id: number) {
+    return hats.find(hat => hat.id == id);
+}
 
-    /**
-     * The multiplier of damage when this hat is worn
-     */
-    dmgMult: number;
-    dmgMultO: number;
-
-    /**
-     * The multiplier of speed when this hat is worn
-     */
-    spdMult: number;
-
-    /**
-     * Whether or not it should be impossible to eat while wearing this hat
-     */
-    noEat: boolean;
-
-    /**
-     * How much to knock an enemy back when attacked while wearing this hat
-     */
-    dmgK: number;
-
-    /**
-     * Whether or not to be ignored by turret AI while wearing this hat
-     */
-    antiTurret: boolean;
-}
\ No newline at end of file
+export { getHat };
\ No newline at end of file
diff --git a/src/moomoo/Player.ts b/src/moomoo/Player.ts
index 4123c70..c040909 100755
--- a/src/moomoo/Player.ts
+++ b/src/moomoo/Player.ts
@@ -19,9 +19,9 @@ import {
   getGameObjID
 } from "../items/items";
 import { ItemType } from "../items/UpgradeItems";
-import Item from "../items/Item";
 import GameObject from "../gameobjects/GameObject";
 import { collideGameObjects } from "./Physics";
+import { getHat } from "./Hats";
 
 export default class Player extends Entity {
   public name: string;
@@ -30,14 +30,23 @@ export default class Player extends Entity {
   public game: Game;
 
   public lastPing: number = 0;
+  public lastDot = 0;
 
-  public hatID: number;
-  public accID: number;
+  public hatID: number = 0;
+  public accID: number = 0;
 
   public ownerID: string;
 
   public upgradeAge = 2;
 
+  public foodHealOverTime = 0;
+  public foodHealOverTimeAmt = 0;
+  public maxFoodHealOverTime = -1;
+
+  public bleedDmg = 5;
+  public bleedAmt = 0;
+  public maxBleedAmt = -1;
+
   public weapon: PrimaryWeapons = 0;
   public secondaryWeapon: SecondaryWeapons = -1;
   public selectedWeapon: Weapons = 0;
@@ -65,6 +74,28 @@ export default class Player extends Entity {
     this._kills = newKills;
   }
 
+  public damageOverTime() {
+    let hat = getHat(this.hatID);
+
+    if (hat) {
+      this.health += hat.healthRegen || 0;
+
+      if (this.foodHealOverTimeAmt < this.maxFoodHealOverTime) {
+        this.health += this.foodHealOverTime;
+        this.foodHealOverTimeAmt++;
+      } else {
+        this.foodHealOverTime = -1;
+      }
+
+      if (this.bleedAmt < this.maxBleedAmt) {
+        this.health += this.bleedDmg;
+        this.bleedAmt++;
+      } else {
+        this.maxBleedAmt = -1;
+      }
+    }
+  }
+
   public maxXP = 300;
   public age = 1;
 
@@ -201,6 +232,10 @@ export default class Player extends Entity {
     }
 
     this._health = newHealth;
+
+    if (this._health <= 0 && !this.dead) {
+      this.die();
+    }
   }
 
   constructor(
@@ -333,6 +368,7 @@ export default class Player extends Entity {
     this.autoAttackOn = false;
     this.disableRotation = false;
     this.moveDirection = null;
+    this.items = [ItemType.Apple, ItemType.WoodWall, ItemType.Spikes, ItemType.Windmill];
 
     this.client?.socket.send(
       packetFactory.serializePacket(