diff --git a/Entities/Characters/Builder/BuilderInventory.as b/Entities/Characters/Builder/BuilderInventory.as index ef40cb7..a4fc605 100644 --- a/Entities/Characters/Builder/BuilderInventory.as +++ b/Entities/Characters/Builder/BuilderInventory.as @@ -8,20 +8,12 @@ namespace Builder { - enum Cmd - { - nil = 0, - TOOL_CLEAR = 31, - PAGE_SELECT = 32, - - make_block = 64, - make_reserved = 99 - }; - enum Page { PAGE_ZERO = 0, PAGE_ONE, + //PAGE_TWO, + //PAGE_THREE, PAGE_COUNT }; } @@ -45,8 +37,6 @@ const u8 GRID_PADDING = 12; const u32 SHOW_NO_BUILD_TIME = 90; -const bool QUICK_SWAP_ENABLED = false; - void onInit(CInventory@ this) { CBlob@ blob = this.getBlob(); @@ -71,6 +61,13 @@ void onInit(CInventory@ this) AddIconToken("$"+PAGE_NAME[i]+"$", "BuilderPageIcons.png", Vec2f(48, 24), i); } + blob.addCommandID("make block"); + blob.addCommandID("make block client"); + blob.addCommandID("tool clear"); + blob.addCommandID("tool clear client"); + blob.addCommandID("page select"); + blob.addCommandID("page select client"); + blob.set_Vec2f("backpack position", Vec2f_zero); blob.set_u8("build page", 0); @@ -78,12 +75,6 @@ void onInit(CInventory@ this) blob.set_u8("buildblob", 255); blob.set_TileType("buildtile", 0); - if (QUICK_SWAP_ENABLED) - { - blob.set_u8("current block", 255); // 255 for no block - blob.set_u8("prev block", 255); - } - blob.set_u32("cant build time", 0); blob.set_u32("show build time", 0); @@ -98,10 +89,9 @@ void MakeBlocksMenu(CInventory@ this, const Vec2f &in INVENTORY_CE) BuildBlock[][]@ blocks; blob.get(blocks_property, @blocks); if (blocks is null) return; - + const u8 PAGE = blob.get_u8("build page"); const Vec2f MENU_SIZE = PAGE_SIZE[PAGE]; - const Vec2f MENU_CE = Vec2f(0, MENU_SIZE.y * -GRID_SIZE - GRID_PADDING) + INVENTORY_CE; CGridMenu@ menu = CreateGridMenu(MENU_CE, blob, MENU_SIZE, getTranslatedString("Build")); @@ -114,7 +104,9 @@ void MakeBlocksMenu(CInventory@ this, const Vec2f &in INVENTORY_CE) BuildBlock@ b = blocks[PAGE][i]; if (b is null) continue; string block_desc = getTranslatedString(b.description); - CGridButton@ button = menu.AddButton(b.icon, "\n" + block_desc, Builder::make_block + i); + CBitStream params; + params.write_u8(i); + CGridButton@ button = menu.AddButton(b.icon, "\n" + block_desc, blob.getCommandID("make block"), params); if (button is null) continue; button.selectOneOnClick = true; @@ -148,10 +140,7 @@ void MakeBlocksMenu(CInventory@ this, const Vec2f &in INVENTORY_CE) { tool.SetCaptionEnabled(false); - CBitStream params; - params.write_u16(blob.getNetworkID()); - - CGridButton@ clear = tool.AddButton("$BUILDER_CLEAR$", "", Builder::TOOL_CLEAR, Vec2f(1, 1), params); + CGridButton@ clear = tool.AddButton("$BUILDER_CLEAR$", "", blob.getCommandID("tool clear"), Vec2f(1, 1)); if (clear !is null) { clear.SetHoverText(getTranslatedString("Stop building\n")); @@ -168,12 +157,12 @@ void MakeBlocksMenu(CInventory@ this, const Vec2f &in INVENTORY_CE) { index.deleteAfterClick = false; - CBitStream params; - params.write_u16(blob.getNetworkID()); for(u8 i = 0; i < Builder::PAGE_COUNT; i++) { - CGridButton@ button = index.AddButton("$"+PAGE_NAME[i]+"$", PAGE_NAME[i], Builder::PAGE_SELECT + i, Vec2f(2, 1), params); + CBitStream params; + params.write_u8(i); + CGridButton@ button = index.AddButton("$"+PAGE_NAME[i]+"$", PAGE_NAME[i], blob.getCommandID("page select"), Vec2f(2, 1), params); if (button is null) continue; button.selectOneOnClick = true; @@ -202,19 +191,26 @@ void onCreateInventoryMenu(CInventory@ this, CBlob@ forBlob, CGridMenu@ menu) void onCommand(CInventory@ this, u8 cmd, CBitStream@ params) { - string dbg = "BuilderInventory.as: Unknown command "; - CBlob@ blob = this.getBlob(); if (blob is null) return; - if (cmd >= Builder::make_block && cmd < Builder::make_reserved) + if (cmd == blob.getCommandID("make block") && isServer()) { - const bool isServer = getNet().isServer(); + CPlayer@ callerp = getNet().getActiveCommandPlayer(); + if (callerp is null) return; + CBlob@ callerb = callerp.getBlob(); + if (callerb is null) return; + if (callerb !is blob) return; BuildBlock[][]@ blocks; if (!blob.get(blocks_property, @blocks)) return; - uint i = cmd - Builder::make_block; + u8 i; + if (!params.saferead_u8(i)) return; + + CBitStream sparams; + sparams.write_u8(i); + blob.SendCommand(blob.getCommandID("make block client"), sparams); const u8 PAGE = blob.get_u8("build page"); if (blocks !is null && i >= 0 && i < blocks[PAGE].length) @@ -223,39 +219,30 @@ void onCommand(CInventory@ this, u8 cmd, CBitStream@ params) bool canBuildBlock = canBuild(blob, @blocks[PAGE], i) && !isKnocked(blob); if (!canBuildBlock) { - if (blob.isMyPlayer()) - { - blob.getSprite().PlaySound("/NoAmmo", 0.5); - } - return; } - // put carried in inventory thing first - if (isServer) + CBlob@ carryBlob = blob.getCarriedBlob(); + if (carryBlob !is null) { - CBlob@ carryBlob = blob.getCarriedBlob(); - if (carryBlob !is null) + // check if this isn't what we wanted to create + if (carryBlob.getName() == block.name) { - // check if this isn't what we wanted to create - if (carryBlob.getName() == block.name) - { - return; - } + return; + } - if (carryBlob.hasTag("temp blob")) - { - carryBlob.Untag("temp blob"); - carryBlob.server_Die(); - } - else + if (carryBlob.hasTag("temp blob")) + { + carryBlob.Untag("temp blob"); + carryBlob.server_Die(); + } + else + { + // try put into inventory whatever was in hands + // creates infinite mats duplicating if used on build block, not great :/ + if (!block.buildOnGround && !blob.server_PutInInventory(carryBlob)) { - // try put into inventory whatever was in hands - // creates infinite mats duplicating if used on build block, not great :/ - if (!block.buildOnGround && !blob.server_PutInInventory(carryBlob)) - { - carryBlob.server_DetachFromAll(); - } + carryBlob.server_DetachFromAll(); } } } @@ -268,90 +255,106 @@ void onCommand(CInventory@ this, u8 cmd, CBitStream@ params) { blob.set_TileType("buildtile", block.tile); } + } + } + else if (cmd == blob.getCommandID("make block client") && isClient()) + { + BuildBlock[][]@ blocks; + if (!blob.get(blocks_property, @blocks)) return; - if (blob.isMyPlayer()) - { - SetHelp(blob, "help self action", "builder", getTranslatedString("$Build$Build/Place $LMB$"), "", 3); - } + u8 i; + if (!params.saferead_u8(i)) return; - if (QUICK_SWAP_ENABLED && blob.get_u8("current block") != i) + const u8 PAGE = blob.get_u8("build page"); + if (blocks !is null && i >= 0 && i < blocks[PAGE].length) + { + BuildBlock@ block = @blocks[PAGE][i]; + bool canBuildBlock = canBuild(blob, @blocks[PAGE], i) && !isKnocked(blob); + if (!canBuildBlock) { - if (block.name == "building") - { - u8 temp = blob.get_u8("current block"); - blob.set_u8("current block", blob.get_u8("prev block")); - blob.set_u8("prev block", temp); - } - else + if (blob.isMyPlayer()) { - blob.set_u8("prev block", blob.get_u8("current block")); - blob.set_u8("current block", i); + blob.getSprite().PlaySound("/NoAmmo", 0.5); } + return; + } + + if (block.tile == 0) + { + server_BuildBlob(blob, @blocks[PAGE], i); + } + else + { + blob.set_TileType("buildtile", block.tile); + } + + if (blob.isMyPlayer()) + { + SetHelp(blob, "help self action", "builder", getTranslatedString("$Build$Build/Place $LMB$"), "", 3); } } } - else if (cmd == Builder::TOOL_CLEAR) + else if (cmd == blob.getCommandID("tool clear") && isServer()) { - u16 id; - if (!params.saferead_u16(id)) return; + CPlayer@ callerp = getNet().getActiveCommandPlayer(); + if (callerp is null) return; - CBlob@ target = getBlobByNetworkID(id); - if (target is null) return; + CBlob@ callerb = callerp.getBlob(); + if (callerb is null) return; + if (callerb !is blob) return; - target.ClearGridMenus(); + ClearCarriedBlock(blob); - ClearCarriedBlock(target); - - if (QUICK_SWAP_ENABLED && blob.get_u8("current block") != 255) - { - blob.set_u8("prev block", blob.get_u8("current block")); - blob.set_u8("current block", 255); - } + blob.server_SendCommandToPlayer(blob.getCommandID("tool clear client"), callerp); } - else if (cmd >= Builder::PAGE_SELECT && cmd < Builder::PAGE_SELECT + Builder::PAGE_COUNT) + else if (cmd == blob.getCommandID("tool clear client") && isClient()) { - u16 id; - if (!params.saferead_u16(id)) return; + blob.ClearGridMenus(); - CBlob@ target = getBlobByNetworkID(id); - if (target is null) return; - - target.ClearGridMenus(); + ClearCarriedBlock(blob); + } + else if (cmd == blob.getCommandID("page select") && isServer()) + { + CPlayer@ callerp = getNet().getActiveCommandPlayer(); + if (callerp is null) return; - if (QUICK_SWAP_ENABLED && blob.get_u8("build page") != cmd - Builder::PAGE_SELECT) - { - blob.set_u8("prev block", 255); - blob.set_u8("current block", 255); - } + CBlob@ callerb = callerp.getBlob(); + if (callerb is null) return; + if (callerb !is blob) return; - target.set_u8("build page", cmd - Builder::PAGE_SELECT); + u8 page; + if (!params.saferead_u8(page)) return; - ClearCarriedBlock(target); + blob.set_u8("build page", page); - if (target is getLocalPlayerBlob()) - { - target.CreateInventoryMenu(target.get_Vec2f("backpack position")); - } - } - else if (cmd == blob.getCommandID("cycle") && QUICK_SWAP_ENABLED) - { - if (isServer()) //only send once - server will have lowest ping for this + ClearCarriedBlock(blob); + + if (isClient()) //localhost { - if (blob.get_u8("prev block") == 255) + blob.ClearGridMenus(); + if (blob is getLocalPlayerBlob()) { - CBitStream params; - params.write_u16(blob.getNetworkID()); - blob.SendCommand(Builder::TOOL_CLEAR, params); - } - else - { - blob.SendCommand(Builder::make_block + blob.get_u8("prev block")); + blob.CreateInventoryMenu(blob.get_Vec2f("backpack position")); } } - if (blob.isMyPlayer()) + CBitStream sparams; + sparams.write_u8(page); + blob.server_SendCommandToPlayer(blob.getCommandID("page select client"), sparams, callerp); + } + else if (cmd == blob.getCommandID("page select client") && isClient()) + { + u8 page; + if (!params.saferead_u8(page)) return; + + blob.ClearGridMenus(); + blob.set_u8("build page", page); + + ClearCarriedBlock(blob); + + if (blob is getLocalPlayerBlob()) { - Sound::Play("/CycleInventory.ogg"); + blob.CreateInventoryMenu(blob.get_Vec2f("backpack position")); } } } @@ -391,10 +394,11 @@ void onTick(CBlob@ this) { if (controls.isKeyJustPressed(KEY_KEY_1 + i)) { - this.SendCommand(Builder::make_block + blockBinds[i]); + CBitStream params; + params.write_u8(blockBinds[i]); + this.SendCommand(this.getCommandID("make block"), params); } } - } } diff --git a/Entities/Characters/Scripts/RunnerDeath.as b/Entities/Characters/Scripts/RunnerDeath.as index 8ad59e5..350f862 100644 --- a/Entities/Characters/Scripts/RunnerDeath.as +++ b/Entities/Characters/Scripts/RunnerDeath.as @@ -24,14 +24,14 @@ f32 onHit(CBlob@ this, Vec2f worldPoint, Vec2f velocity, f32 damage, CBlob@ hitt if (attach !is null) { CBlob@ held = attach.getOccupied(); - if (held !is null && held.hasCommandID("revive")) + if (held !is null && held.hasCommandID("server_revive")) { - if (isServer()) + if (this.isMyPlayer()) { CBitStream params; params.write_netid(this.getNetworkID()); params.write_bool(true); - held.SendCommand(held.getCommandID("revive"), params); + held.SendCommand(held.getCommandID("server_revive"), params); } return 0.0f; diff --git a/Entities/Characters/Sedgwick/Sedgwick.as b/Entities/Characters/Sedgwick/Sedgwick.as index f1e60df..c8c7849 100644 --- a/Entities/Characters/Sedgwick/Sedgwick.as +++ b/Entities/Characters/Sedgwick/Sedgwick.as @@ -34,8 +34,6 @@ void onInit(CBlob@ this) this.SetLight(false); this.SetLightRadius(75.0f); this.SetLightColor(SColor(255, 150, 240, 171)); - - this.addCommandID("cast spell"); server_SetSpell(this, XORRandom(spell_end)); } @@ -71,22 +69,8 @@ void onTick(CBlob@ this) this.getShape().SetStatic(true); } - if (isServer()) - { - CBitStream params; - params.write_u8(this.get_u8("spell num")); - params.write_u8(this.get_u8("spell countdown")); - this.SendCommand(this.getCommandID("cast spell"), params); - } - } -} - -void onCommand(CBlob@ this, u8 cmd, CBitStream@ params) -{ - if (cmd == this.getCommandID("cast spell")) - { - const u8 spell = params.read_u8(); - const u8 countdown = params.read_u8(); + const u8 spell = this.get_u8("spell num"); + const u8 countdown = this.get_u8("spell countdown"); switch(spell) { case spell_skeleton: SpellSkeletonRain(this, countdown); break; @@ -95,10 +79,11 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream@ params) case spell_portal: SpellPortal(this, countdown); break; case spell_end: SedgwickDeparture(this, countdown); break; } - + if (isServer()) { this.set_u8("spell countdown", Maths::Max(this.get_u8("spell countdown") - 1, 0)); + this.Sync("spell countdown", true); } } } @@ -382,7 +367,9 @@ void server_SetSpell(CBlob@ this, const u8&in spell_num) if (!isServer()) return; this.set_u8("spell num", spell_num); + this.Sync("spell num", true); this.set_u8("spell countdown", spell_countdown[spell_num]); + this.Sync("spell countdown", true); if (spell_num == spell_portal) { diff --git a/Entities/Items/Crate/Crate.as b/Entities/Items/Crate/Crate.as deleted file mode 100644 index 3148e8e..0000000 --- a/Entities/Items/Crate/Crate.as +++ /dev/null @@ -1,818 +0,0 @@ -// generic crate -// can hold items in inventory or unpacks to catapult/ship etc. - -#include "CrateCommon.as" -#include "VehicleAttachmentCommon.as" -#include "MiniIconsInc.as" -#include "Help.as" -#include "Hitters.as" -#include "GenericButtonCommon.as" -#include "KnockedCommon.as" - -//property name -const string required_space = "required space"; - -//proportion of distance allowed (1.0f == overlapping radius, 2.0f = within 1 extra radius) -const float ally_allowed_distance = 2.0f; - -void onInit(CBlob@ this) -{ - this.checkInventoryAccessibleCarefully = true; - - this.addCommandID("unpack"); - this.addCommandID("getin"); - this.addCommandID("getout"); - this.addCommandID("stop unpack"); - this.addCommandID("boobytrap"); - - this.set_u32("boobytrap_cooldown_time", 0); - this.set_s32("gold building amount", 0); - - if (this.exists("frame")) - { - const u8 frame = this.get_u8("frame"); - const string packed = this.get_string("packed"); - - CSpriteLayer@ icon = this.getSprite().addSpriteLayer("icon", "/MiniIcons.png" , 16, 16, this.getTeamNum(), -1); - if (icon !is null) - { - Animation@ anim = icon.addAnimation("display", 0, false); - anim.AddFrame(frame); - - icon.SetOffset(Vec2f(-2, 1)); - icon.SetRelativeZ(1); - } - - this.getSprite().SetAnimation("label"); - - // help - const string iconToken = "$crate_" + packed + "$"; - AddIconToken("$crate_" + packed + "$", "/MiniIcons.png", Vec2f(16, 16), frame); - SetHelp(this, "help use", "", iconToken + getTranslatedString("Unpack {ITEM} $KEY_E$").replace("{ITEM}", packed), "", 4); - } - else - { - this.getAttachments().getAttachmentPointByName("PICKUP").offset = Vec2f(3, 4); - this.getAttachments().getAttachmentPointByName("PICKUP").offsetZ = -10; - this.getSprite().SetRelativeZ(-10.0f); - this.AddScript("BehindWhenAttached.as"); - - this.Tag("dont deactivate"); - } - // Kinda hacky, only normal crates ^ with "dont deactivate" will ignore "activated" - this.Tag("activated"); - - - const uint unpackSecs = 3; - this.set_u32("unpack secs", unpackSecs); - this.set_u32("unpack time", 0); - - if (this.exists("packed name")) - { - if (this.get_string("packed name").length > 1) - this.setInventoryName("Crate with " + this.get_string("packed name")); - } - - if (!this.exists(required_space)) - { - this.set_Vec2f(required_space, Vec2f(5, 4)); - } - - this.getSprite().SetZ(-10.0f); -} - -void onTick(CBlob@ this) -{ - // parachute - - if (this.hasTag("parachute")) // wont work with the tick frequency - { - if (this.getSprite().getSpriteLayer("parachute") is null) - { - ShowParachute(this); - } - - // para force + swing in wind - this.AddForce(Vec2f(Maths::Sin(getGameTime() * 0.03f) * 1.0f, -30.0f * this.getVelocity().y)); - - if (this.isOnGround() || this.isInWater() || this.isAttached()) - { - Land(this); - } - } - else - { - if (hasSomethingPacked(this)) - this.getCurrentScript().tickFrequency = 15; - else - { - this.getCurrentScript().tickFrequency = 0; - return; - } - - // unpack - u32 unpackTime = this.get_u32("unpack time"); - - // can't unpack in no build sector or blocked in with walls! - if (!canUnpackHere(this)) - { - this.set_u32("unpack time", 0); - this.getCurrentScript().tickFrequency = 15; - this.getShape().setDrag(2.0); - return; - } - - if (unpackTime != 0 && getGameTime() >= unpackTime) - { - Unpack(this); - return; - } - } -} - -void Land(CBlob@ this) -{ - this.Untag("parachute"); - HideParachute(this); - - // unpack immediately - if (this.exists("packed") && this.hasTag("unpack on land")) - { - Unpack(this); - } - - if (this.hasTag("destroy on touch")) - { - this.server_SetHealth(-1.0f); // TODO: wont gib on client - this.server_Die(); - } -} - -bool doesCollideWithBlob(CBlob@ this, CBlob@ blob) -{ - return (this.getName() == blob.getName()) - || ((blob.getShape().isStatic() || blob.hasTag("player") || blob.hasTag("projectile")) && !blob.hasTag("parachute")); -} - -bool canBePickedUp(CBlob@ this, CBlob@ byBlob) -{ - return (this.getTeamNum() == byBlob.getTeamNum() || this.isOverlapping(byBlob)); -} - -bool isInventoryAccessible(CBlob@ this, CBlob@ forBlob) -{ - if (this.hasTag("unpackall") || !canSeeButtons(this, forBlob)) - return false; - - if (!hasSomethingPacked(this)) // It's a normal crate - { - if (forBlob.getCarriedBlob() !is null - && this.getInventory().canPutItem(forBlob.getCarriedBlob())) - { - return true; // OK to put an item in whenever - } - - if (getPlayerInside(this) !is null) - { - return false; // Player getout buttons instead - } - - if (this.getTeamNum() == forBlob.getTeamNum()) - { - f32 dist = (this.getPosition() - forBlob.getPosition()).Length(); - f32 rad = (this.getRadius() + forBlob.getRadius()); - - if (dist < rad * ally_allowed_distance) - { - return true; // Allies can access from further away - } - } - else if (this.isOverlapping(forBlob)) - { - return true; // Enemies can access when touching - } - - return false; - } - - else // has something packed - { - return false; - } -} - -void GetButtonsFor(CBlob@ this, CBlob@ caller) -{ - if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - - Vec2f buttonpos(0, 0); - - /*bool putting = caller.getCarriedBlob() !is null && caller.getCarriedBlob() !is this; - bool canput = putting && this.getInventory().canPutItem(caller.getCarriedBlob()); - CBlob@ sneaky_player = getPlayerInside(this); - // If there's a player inside and we aren't just dropping in an item - if (sneaky_player !is null && !(putting && canput)) - { - if (sneaky_player.getTeamNum() == caller.getTeamNum()) - { - CBitStream params; - params.write_u16( caller.getNetworkID() ); - CButton@ button = caller.CreateGenericButton( 6, Vec2f(0,0), this, this.getCommandID("getout"), getTranslatedString("Get out"), params); - if (putting) - { - button.SetEnabled(false); - } - if (sneaky_player !is caller) // it's a teammate, so they have to be close to use button - { - button.enableRadius = 20.0f; - } - } - else // make fake buttons for enemy - { - CBitStream params; - params.write_u16(caller.getNetworkID()); - if (caller.getCarriedBlob() is this) - { - // Fake get in button - caller.CreateGenericButton(4, Vec2f(), this, this.getCommandID("getout"), getTranslatedString("Get inside"), params); - } - else - { - // Fake inventory button - CButton@ button = caller.CreateGenericButton(13, Vec2f(), this, this.getCommandID("getout"), getTranslatedString("Crate"), params); - button.enableRadius = 20.0f; - } - } - }*/ - if (this.hasTag("unpackall")) - { - caller.CreateGenericButton(12, buttonpos, this, this.getCommandID("unpack"), getTranslatedString("Unpack all")); - } - else if (hasSomethingPacked(this) && !canUnpackHere(this)) - { - string msg = getTranslatedString("Can't unpack {ITEM} here").replace("{ITEM}", getTranslatedString(this.get_string("packed name"))); - - CButton@ button = caller.CreateGenericButton(12, buttonpos, this, 0, msg); - if (button !is null) - { - button.SetEnabled(false); - } - } - else if (isUnpacking(this)) - { - caller.CreateGenericButton("$DISABLED$", buttonpos, this, this.getCommandID("stop unpack"), getTranslatedString("Stop {ITEM}").replace("{ITEM}", getTranslatedString(this.get_string("packed name")))); - } - else if (hasSomethingPacked(this)) - { - caller.CreateGenericButton(12, buttonpos, this, this.getCommandID("unpack"), getTranslatedString("Unpack {ITEM}").replace("{ITEM}", getTranslatedString(this.get_string("packed name")))); - } - /*else if (caller.getCarriedBlob() is this) - { - CBitStream params; - params.write_u16( caller.getNetworkID() ); - caller.CreateGenericButton( 4, Vec2f(0,0), this, this.getCommandID("getin"), getTranslatedString("Get inside"), params ); - } - else if (this.getTeamNum() != caller.getTeamNum() && !this.isOverlapping(caller)) - { - // We need a fake crate inventory button to hint to players that they need to get closer - // And also so they're unable to discern which crates have hidden players - if (caller.getCarriedBlob() is null || (putting && !canput)) - { - CButton@ button = caller.CreateGenericButton(13, Vec2f(), this, this.getCommandID("getout"), getTranslatedString("Crate")); - button.SetEnabled(false); // they shouldn't be able to actually press it tho - } - }*/ -} - -void onCommand(CBlob@ this, u8 cmd, CBitStream @params) -{ - if (cmd == this.getCommandID("unpack")) - { - if (hasSomethingPacked(this)) - { - if (canUnpackHere(this)) - { - this.set_u32("unpack time", getGameTime() + this.get_u32("unpack secs") * getTicksASecond()); - this.getShape().setDrag(10.0f); - } - } - else - { - this.server_SetHealth(-1.0f); - this.server_Die(); - } - } - else if (cmd == this.getCommandID("stop unpack")) - { - this.set_u32("unpack time", 0); - } - /*else if (cmd == this.getCommandID("getin")) - { - if (this.getHealth() <= 0) - { - return; - } - CBlob @caller = getBlobByNetworkID( params.read_u16() ); - - if (caller !is null && this.getInventory() !is null) - { - CInventory@ inv = this.getInventory(); - u8 itemcount = inv.getItemsCount(); - - // Boobytrap if crate has enemy mine - CBlob@ mine = null; - for (int i = 0; i < inv.getItemsCount(); i++) - { - CBlob@ item = inv.getItem(i); - if (item.getName() == "mine" && item.getTeamNum() != caller.getTeamNum()) - { - CBitStream params; - params.write_u16(caller.getNetworkID()); - params.write_u16(item.getNetworkID()); - this.SendCommand(this.getCommandID("boobytrap"), params); - return; - } - } - - // We might have to make room - while (!inv.canPutItem(caller) && itemcount > 0) - { - // pop out last items until we can put in player or there's nothing left - CBlob@ item = inv.getItem(itemcount - 1); - this.server_PutOutInventory(item); - float magnitude = (1 - XORRandom(3) * 0.25) * 5.0f; - item.setVelocity(caller.getVelocity() + getRandomVelocity(90, magnitude, 45)); - itemcount--; - } - - Vec2f velocity = caller.getVelocity(); - this.server_PutInInventory( caller ); - this.setVelocity(velocity); - } - } - else if (cmd == this.getCommandID("getout")) - { - CBlob @caller = getBlobByNetworkID( params.read_u16() ); - CBlob@ sneaky_player = getPlayerInside(this); - if (caller !is null && sneaky_player !is null) { - if (caller.getTeamNum() != sneaky_player.getTeamNum()) - { - if (isKnockable(caller)) - { - setKnocked(caller, 30); - } - } - this.Tag("crate escaped"); - this.server_PutOutInventory(sneaky_player); - } - // Attack self to pop out items - this.server_Hit(this, this.getPosition(), Vec2f(), 100.0f, Hitters::crush, true); - this.server_Die(); - } - else if (cmd == this.getCommandID("boobytrap")) - { - CBlob@ caller = getBlobByNetworkID(params.read_u16()); - CBlob@ mine = getBlobByNetworkID(params.read_u16()); - if (caller !is null && mine !is null && this.get_u32("boobytrap_cooldown_time") <= getGameTime()) - { - this.set_u32("boobytrap_cooldown_time", getGameTime() + 30); - this.server_PutOutInventory(mine); - Vec2f pos = this.getPosition(); - pos.y = this.getTeamNum() == caller.getTeamNum() ? pos.y - 5 - : caller.getPosition().y - caller.getRadius() - 5; - pos.y = Maths::Min(pos.y, this.getPosition().y - 5); - mine.setPosition(pos); - mine.setVelocity(Vec2f((caller.getPosition().x - mine.getPosition().x) / 30.0f, -5.0f)); - mine.set_u8("mine_timer", 255); - mine.SendCommand(mine.getCommandID("mine_primed")); - } - }*/ - else if (cmd == this.getCommandID("activate")) - { - CBlob@ carrier = this.getAttachments().getAttachmentPointByName("PICKUP").getOccupied(); - if (carrier !is null) - { - DumpOutItems(this, 5.0f, carrier.getVelocity(), false); - } - } -} - -void Unpack(CBlob@ this) -{ - if (!isServer()) return; - - CBlob@ blob = server_CreateBlob(this.get_string("packed"), this.getTeamNum(), Vec2f_zero); - - // put on ground if not in water - - if (blob !is null && blob.getShape() !is null) - { - blob.setPosition(this.getPosition() + Vec2f(0, (this.getHeight() - blob.getHeight()) / 2 +(this.isAttached() ? 8 : 4))); - // if (!getMap().isInWater(this.getPosition() + Vec2f(0.0f, this.getRadius()))) - // blob.getShape().PutOnGround(); - // else - // blob.getShape().ResolveInsideMapCollision(); - - // attach to VEHICLE attachment if possible - - TryToAttachVehicle(blob); - - // msg back factory so it can add this item - - if (this.exists("msg blob")) - { - CBitStream params; - params.write_u16(blob.getNetworkID()); - CBlob@ factory = getBlobByNetworkID(this.get_u16("msg blob")); - if (factory !is null) - { - factory.SendCommand(factory.getCommandID("track blob"), params); - } - } - - blob.SetFacingLeft(this.isFacingLeft()); - } - - this.set_s32("gold building amount", 0); // prevents ballista crates from dropping gold if they were unpacked - this.server_SetHealth(-1.0f); // TODO: wont gib on client - this.server_Die(); -} - -bool isUnpacking(CBlob@ this) -{ - return getGameTime() <= this.get_u32("unpack time"); -} - -void ShowParachute(CBlob@ this) -{ - CSprite@ sprite = this.getSprite(); - CSpriteLayer@ parachute = sprite.addSpriteLayer("parachute", 32, 32); - if (parachute !is null) - { - Animation@ anim = parachute.addAnimation("default", 0, true); - anim.AddFrame(4); - parachute.SetOffset(Vec2f(0.0f, - 17.0f)); - } -} - -void HideParachute(CBlob@ this) -{ - CSprite@ sprite = this.getSprite(); - CSpriteLayer@ parachute = sprite.getSpriteLayer("parachute"); - if (parachute !is null && parachute.isVisible()) - { - parachute.SetVisible(false); - ParticlesFromSprite(parachute); - } -} - -void onCreateInventoryMenu(CBlob@ this, CBlob@ forBlob, CGridMenu @gridmenu) -{ - /*CInventory@ inv = this.getInventory(); - for (int i = 0; i < inv.getItemsCount(); i++) - { - CBlob@ item = inv.getItem(i); - if (item.hasTag("player")) - { - // Get out of there, can't grab players - forBlob.ClearGridMenus(); - } - if (item.getName() == "mine" && item.getTeamNum() != forBlob.getTeamNum()) - { - CBitStream params; - params.write_u16(forBlob.getNetworkID()); - params.write_u16(item.getNetworkID()); - this.SendCommand(this.getCommandID("boobytrap"), params); - break; - } - }*/ -} - -void onAddToInventory(CBlob@ this, CBlob@ blob) -{ - this.getSprite().PlaySound("thud.ogg"); - if (blob.getName() == "keg") - { - if (blob.hasTag("exploding")) - { - this.Tag("heavy weight"); - } - else - { - this.Tag("medium weight"); - } - } -} - -void onRemoveFromInventory(CBlob@ this, CBlob@ blob) -{ - if (blob.hasTag("player")) - { - if (this.hasTag("crate exploded")) - { - this.getSprite().PlaySound(getTranslatedString("MigrantSayNo") + ".ogg", 1.0f, blob.getSexNum() == 0 ? 1.0f : 1.5f); - Vec2f velocity = this.getVelocity(); - if (velocity.x > 0) // Blow them right - { - velocity = Vec2f(0.75, -1); - } - else if (velocity.x < 0) // Blow them left - { - velocity = Vec2f(-0.75, -1); - } - else // Go straight up - { - velocity = Vec2f(0, -1); - } - blob.setVelocity(velocity * 8); - if (isKnockable(blob)) - { - setKnocked(blob, 30); - } - } - else if (this.hasTag("crate escaped")) - { - Vec2f velocity = this.getOldVelocity(); - if (-5 < velocity.y && velocity.y < 5) - { - velocity.y = -5; // Leap out of crate - } - Vec2f pos = this.getPosition(); - pos.y -= 5; - blob.setPosition(pos); - blob.setVelocity(velocity); - - blob.getSprite().PlaySound(getTranslatedString("MigrantSayHello") + ".ogg", 1.0f, blob.getSexNum() == 0 ? 1.0f : 1.25f); - } - else - { - blob.setVelocity(this.getOldVelocity()); - if (isKnockable(blob)) - { - setKnocked(blob, 2); - } - } - } - - if (blob.getName() == "keg") - { - if (blob.hasTag("exploding") && blob.get_s32("explosion_timer") - getGameTime() <= 0) - { - this.server_Hit(this, this.getPosition(), Vec2f(), 100.0f, Hitters::explosion, true); - } - - this.Untag("medium weight"); - this.Untag("heavy weight"); // TODO: what if there can be multiple kegs? - } - - //die on empty crate - if (!this.isInInventory() && this.getInventory().getItemsCount() == 0) - { - this.server_Die(); - } -} - -f32 onHit(CBlob@ this, Vec2f worldPoint, Vec2f velocity, f32 damage, CBlob@ hitterBlob, u8 customData) -{ - f32 dmg = damage; - - if (customData == Hitters::builder) - { - dmg *= 4; - } - if (customData == Hitters::saw) - { - DumpOutItems(this, 0); - } - if (isExplosionHitter(customData) || customData == Hitters::keg) - { - if (dmg > 50.0f) // inventory explosion - { - this.Tag("crate exploded"); - CBlob@ sneaky_player = getPlayerInside(this); - DumpOutItems(this, 10); - // Nearly kill the player - if (sneaky_player !is null) - { - hitterBlob.server_Hit(sneaky_player, this.getPosition(), Vec2f(), - sneaky_player.getInitialHealth() * 2 - 0.25f, Hitters::explosion, true); - } - } - else - { - if (customData == Hitters::keg) - { - dmg = Maths::Max(dmg, this.getInitialHealth() * 2); // Keg always kills crate - } - CBlob@ sneaky_player = getPlayerInside(this); - if (sneaky_player !is null) - { - bool should_teamkill = (sneaky_player.getTeamNum() != hitterBlob.getTeamNum() - || customData == Hitters::keg); - hitterBlob.server_Hit(getPlayerInside(this), this.getPosition(), Vec2f_zero, - dmg / 2, customData, should_teamkill); - } - } - } - if (this.getHealth() - (dmg / 2.0f) <= 0.0f) - { - DumpOutItems(this); - } - - return dmg; -} - -void onDie(CBlob@ this) -{ - HideParachute(this); - this.getSprite().Gib(); - Vec2f pos = this.getPosition(); - Vec2f vel = this.getVelocity(); - //custom gibs - string fname = CFileMatcher("/Crate.png").getFirst(); - for (int i = 0; i < 4; i++) - { - CParticle@ temp = makeGibParticle(fname, pos, vel + getRandomVelocity(90, 1 , 120), 9, 2 + i, Vec2f(16, 16), 2.0f, 20, "Sounds/material_drop.ogg", 0); - } -} - -bool canUnpackHere(CBlob@ this) -{ - CMap@ map = getMap(); - Vec2f pos = this.getPosition(); - - Vec2f space = this.get_Vec2f(required_space); - Vec2f t_off = Vec2f(map.tilesize * 0.5f, map.tilesize * 0.5f); - Vec2f offsetPos = crate_getOffsetPos(this); - for (f32 step_x = 0.0f; step_x < space.x ; ++step_x) - { - for (f32 step_y = 0.0f; step_y < space.y ; ++step_y) - { - Vec2f temp = (Vec2f(step_x + 0.5, step_y + 0.5) * map.tilesize); - Vec2f v = offsetPos + temp; - if (v.y < map.tilesize || map.isTileSolid(v)) - { - return false; - } - } - } - - string packed = this.get_string("packed"); - //required vertical buffer for siege engines and boats - if (packed == "ballista" || packed == "catapult" || packed == "longboat" || packed == "warboat") - { - if (pos.y < 40) - { - return false; - } - } - - const bool water = packed == "longboat" || packed == "warboat"; - if (this.isAttached()) - { - CBlob@ parent = this.getAttachments().getAttachmentPointByName("PICKUP").getOccupied(); - if (parent !is null) - { - return ((!water && parent.isOnGround()) || (water && map.isInWater(parent.getPosition() + Vec2f(0.0f, 8.0f)))); - } - } - const bool inwater = map.isInWater(this.getPosition() + Vec2f(0.0f, 8.0f)); - const bool supported = ((!water && (this.isOnGround() || inwater)) || (water && inwater)); - return (supported); -} - -Vec2f crate_getOffsetPos(CBlob@ blob) -{ - Vec2f reqspace = blob.get_Vec2f(required_space); - - Vec2f bPos = blob.getPosition(); - Vec2f pos = Vec2f(bPos.x - reqspace.x*4, bPos.y - reqspace.y*8+8+(blob.isAttached() ? 8 : 4)); - pos.x = Maths::Floor(pos.x / 8.0f+0.5f); - pos.y = Maths::Floor(pos.y / 8.0f); - pos *= 8; - - return pos; -} - -CBlob@ getPlayerInside(CBlob@ this) -{ - CInventory@ inv = this.getInventory(); - for (int i = 0; i < inv.getItemsCount(); i++) - { - CBlob@ item = inv.getItem(i); - if (item.hasTag("player")) - return item; - } - return null; -} - -bool DumpOutItems(CBlob@ this, float pop_out_speed = 5.0f, Vec2f init_velocity = Vec2f_zero, bool dump_special = true) -{ - bool dumped_anything = false; - if (isClient()) - { - if ((this.getInventory().getItemsCount() > 1) - || (getPlayerInside(this) is null && this.getInventory().getItemsCount() > 0)) - { - this.getSprite().PlaySound("give.ogg"); - } - } - if (isServer()) - { - Vec2f velocity = (init_velocity == Vec2f_zero) ? this.getOldVelocity() : init_velocity; - CInventory@ inv = this.getInventory(); - u8 target_items_left = 0; - u8 item_num = 0; - - while (inv !is null && (inv.getItemsCount() > target_items_left)) - { - CBlob@ item = inv.getItem(item_num); - - if (!item.hasTag("player") && item.getName() != "mine") - { - dumped_anything = true; - this.server_PutOutInventory(item); - if (pop_out_speed == 0 || item.getName() == "keg") - { - item.setVelocity(velocity); - } - else - { - float magnitude = (1 - XORRandom(3) * 0.25) * pop_out_speed; - item.setVelocity(velocity + getRandomVelocity(90, magnitude, 45)); - } - } - else if (dump_special && (item.hasTag("player") || item.getName() == "mine")) - { - this.server_PutOutInventory(item); - } - else // Don't dump player or mine - { - target_items_left++; - item_num++; - } - } - } - return dumped_anything; -} - -// SPRITE - -// render unpacking time - -void onRender(CSprite@ this) -{ - CBlob@ blob = this.getBlob(); - if (blob.get_string("packed").isEmpty() || blob.get_string("packed name").size() == 0) return; - - Vec2f pos2d = blob.getScreenPos(); - const u32 gameTime = getGameTime(); - const u32 unpackTime = blob.get_u32("unpack time"); - - if (unpackTime > gameTime) - { - // draw drop time progress bar - const int top = pos2d.y - 1.0f * blob.getHeight(); - Vec2f dim(32.0f, 12.0f); - const int secs = 1 + (unpackTime - gameTime) / getTicksASecond(); - Vec2f upperleft(pos2d.x - dim.x / 2, top - dim.y - dim.y); - Vec2f lowerright(pos2d.x + dim.x / 2, top - dim.y); - const f32 progress = 1.0f - (float(secs) / float(blob.get_u32("unpack secs"))); - GUI::DrawProgressBar(upperleft, lowerright, progress); - } - - if (blob.isAttached()) - { - AttachmentPoint@ point = blob.getAttachments().getAttachmentPointByName("PICKUP"); - CBlob@ holder = point.getOccupied(); - if (holder is null) return; - - CPlayer@ local = getLocalPlayer(); - if (local !is null && local.getBlob() is holder) - { - CMap@ map = getMap(); - - Vec2f space = blob.get_Vec2f(required_space); - Vec2f offsetPos = crate_getOffsetPos(blob); - - const f32 scalex = getDriver().getResolutionScaleFactor(); - const f32 zoom = getCamera().targetDistance * scalex; - Vec2f aligned = getDriver().getScreenPosFromWorldPos(offsetPos); - GUI::DrawRectangle(aligned, aligned+space*8*zoom*2, SColor(128, 255, 255, 255)); - //GUI::DrawIcon("CrateSlots.png", 0, space*8, aligned, zoom); - - for (f32 step_x = 0.0f; step_x < space.x ; ++step_x) - { - for (f32 step_y = 0.0f; step_y < space.y ; ++step_y) - { - Vec2f temp = (Vec2f(step_x + 0.5, step_y + 0.5) * map.tilesize); - Vec2f v = offsetPos + temp; - if (map.isTileSolid(v)) - { - GUI::DrawIcon("CrateSlots.png", 5, Vec2f(8, 8), aligned + (temp - Vec2f(0.5f, 0.5f)* map.tilesize) * 2 * zoom, zoom); - } - } - } - } - } -} diff --git a/Entities/Items/Food/EatCommon.as b/Entities/Items/Food/EatCommon.as index 3f33e14..db88913 100644 --- a/Entities/Items/Food/EatCommon.as +++ b/Entities/Items/Food/EatCommon.as @@ -1,4 +1,3 @@ -const string heal_id = "heal command"; bool canEat(CBlob@ blob) { @@ -30,18 +29,49 @@ u8 getHealingAmount(CBlob@ food) void Heal(CBlob@ this, CBlob@ food) { - const bool exists = getBlobByNetworkID(food.getNetworkID()) !is null; - if (isServer() && this.hasTag("player") && !this.hasTag("undead") && this.getHealth() < this.getInitialHealth() && !food.hasTag("healed") && exists) + if (this.hasTag("undead")) return; //dont let the undead eat! + + bool exists = getBlobByNetworkID(food.getNetworkID()) !is null; + if (isServer() && this.hasTag("player") && this.getHealth() < this.getInitialHealth() && !food.hasTag("healed") && exists) { - const u8 heal_amount = getHealingAmount(food); - if (heal_amount > 0) + u8 heal_amount = getHealingAmount(food); + + if (heal_amount == 255) + { + this.add_f32("heal amount", this.getInitialHealth() - this.getHealth()); + this.server_SetHealth(this.getInitialHealth()); + } + else { - CBitStream params; - params.write_u16(this.getNetworkID()); - params.write_u8(heal_amount); - food.SendCommand(food.getCommandID(heal_id), params); + f32 oldHealth = this.getHealth(); + this.server_Heal(f32(heal_amount) * 0.25f); + this.add_f32("heal amount", this.getHealth() - oldHealth); + } - food.Tag("healed"); + //give coins for healing teammate + if (food.exists("healer")) + { + CPlayer@ player = this.getPlayer(); + u16 healerID = food.get_u16("healer"); + CPlayer@ healer = getPlayerByNetworkId(healerID); + if (player !is null && healer !is null) + { + bool healerHealed = healer is player; + bool sameTeam = healer.getTeamNum() == player.getTeamNum(); + if (!healerHealed && sameTeam) + { + int coins = 10; + healer.server_setCoins(healer.getCoins() + coins); + } + } } + + this.Sync("heal amount", true); + + food.Tag("healed"); + + food.SendCommand(food.getCommandID("heal command client")); // for sound + + food.server_Die(); } -} +} \ No newline at end of file diff --git a/Entities/Items/Projectiles/Arrow/Arrow.as b/Entities/Items/Projectiles/Arrow/Arrow.as index 6b2620b..34c6c7b 100644 --- a/Entities/Items/Projectiles/Arrow/Arrow.as +++ b/Entities/Items/Projectiles/Arrow/Arrow.as @@ -524,7 +524,7 @@ f32 ArrowHitBlob(CBlob@ this, Vec2f worldPoint, Vec2f velocity, f32 damage, CBlo if (arrowType == ArrowType::fire) { - if (hitBlob.getName() == "keg" && !hitBlob.hasTag("exploding")) + if (hitBlob.getName() == "keg" && !hitBlob.hasTag("exploding") && isServer()) { hitBlob.SendCommand(hitBlob.getCommandID("activate")); } diff --git a/Entities/Items/Scroll/ScrollClone.as b/Entities/Items/Scroll/ScrollClone.as index 32ef098..0e15aaf 100644 --- a/Entities/Items/Scroll/ScrollClone.as +++ b/Entities/Items/Scroll/ScrollClone.as @@ -7,35 +7,37 @@ void onInit(CBlob@ this) { - this.addCommandID("clone"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to duplicate an object you are pointing to."); +} + +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ + CBlob@ aimBlob = getMap().getBlobAtPosition(caller.getAimPos()); + if (aimBlob is null || aimBlob is this || !canClone(aimBlob)) return; + + Sound::Play("MagicWand.ogg", this.getPosition()); + CBitStream params; - params.write_Vec2f(caller.getAimPos()); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("clone"), "Use this to duplicate an object you are pointing to.", params); + params.write_netid(aimBlob.getNetworkID()); + this.SendCommand(this.getCommandID("server_execute_spell"), params); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("clone")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { - Vec2f aim = params.read_Vec2f(); - - CBlob@ aimBlob = getMap().getBlobAtPosition(aim); - if (aimBlob is null || aimBlob is this || !canClone(aimBlob)) return; - - Vec2f pos = this.getPosition(); - Sound::Play("MagicWand.ogg", pos); - - if (isServer()) - { - CBlob@ clone = server_CreateClone(aimBlob, pos + Vec2f(0, (this.getHeight() - aimBlob.getHeight()) / 2 + 4)); - copyInventory(aimBlob, clone); - this.server_Die(); - } + CBlob@ aimBlob = getBlobByNetworkID(params.read_netid()); + if (aimBlob is null) return; + + CBlob@ clone = server_CreateClone(aimBlob, this.getPosition() + Vec2f(0, (this.getHeight() - aimBlob.getHeight()) / 2 + 4)); + copyInventory(aimBlob, clone); + this.server_Die(); } } diff --git a/Entities/Items/Scroll/ScrollCrate.as b/Entities/Items/Scroll/ScrollCrate.as index ce6e060..0736de5 100644 --- a/Entities/Items/Scroll/ScrollCrate.as +++ b/Entities/Items/Scroll/ScrollCrate.as @@ -5,41 +5,41 @@ void onInit(CBlob@ this) { - this.addCommandID("put in crate"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to crate an object you are pointing at."); +} + +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ + CBlob@ aimBlob = getMap().getBlobAtPosition(caller.getAimPos()); + if (aimBlob is null || !canCrate(aimBlob)) return; + + Vec2f aimBlobPos = aimBlob.getPosition(); + ParticlesFromSprite(aimBlob.getSprite(), aimBlobPos, Vec2f(0, -1), 1, 1); + Sound::Play("MagicWand.ogg", aimBlobPos); + CBitStream params; - params.write_Vec2f(caller.getAimPos()); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("put in crate"), "Use this to crate an object you are pointing at.", params); + params.write_netid(aimBlob.getNetworkID()); + this.SendCommand(this.getCommandID("server_execute_spell"), params); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("put in crate")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { - Vec2f aim = params.read_Vec2f(); - - CBlob@ aimBlob = getMap().getBlobAtPosition(aim); - if (aimBlob is null || !canCrate(aimBlob)) return; - - if (isClient()) - { - //effects - ParticlesFromSprite(aimBlob.getSprite(), aim, Vec2f(0, -1), 1, 1); - Sound::Play("MagicWand.ogg", aim); - } - - if (isServer()) - { - CBlob@ crate = server_MakeCrate(aimBlob.getName(), "Crate with "+aimBlob.getInventoryName(), 0, aimBlob.getTeamNum(), aim); - CShape@ shape = aimBlob.getShape(); - crate.set_Vec2f("required space", Vec2f(Maths::Ceil(shape.getWidth()/8), Maths::Ceil(shape.getHeight()/8))); - aimBlob.server_Die(); - this.server_Die(); - } + CBlob@ aimBlob = getBlobByNetworkID(params.read_netid()); + if (aimBlob is null) return; + + CBlob@ crate = server_MakeCrate(aimBlob.getName(), "Crate with "+aimBlob.getInventoryName(), 0, aimBlob.getTeamNum(), aimBlob.getPosition()); + CShape@ shape = aimBlob.getShape(); + crate.set_Vec2f("required space", Vec2f(Maths::Ceil(shape.getWidth()/8), Maths::Ceil(shape.getHeight()/8))); + aimBlob.server_Die(); + this.server_Die(); } } diff --git a/Entities/Items/Scroll/ScrollDrought.as b/Entities/Items/Scroll/ScrollDrought.as index 47e5c7d..95092dc 100644 --- a/Entities/Items/Scroll/ScrollDrought.as +++ b/Entities/Items/Scroll/ScrollDrought.as @@ -6,7 +6,7 @@ const int radius = 30; void onInit(CBlob@ this) { - this.addCommandID("drought"); + this.addCommandID("server_execute_spell"); this.set_u32("drought_called", 0); } @@ -14,13 +14,12 @@ void onInit(CBlob@ this) void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("drought"), getTranslatedString("Use this to dry up an orb of water.")); + caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("server_execute_spell"), getTranslatedString("Use this to dry up an orb of water.")); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("drought")) + if (cmd == this.getCommandID("server_execute_spell")) { const u32 timer = getGameTime() - this.get_u32("drought_called"); if (timer < 30) return; @@ -48,11 +47,14 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) } } - if (acted) { this.server_Die(); - Sound::Play("MagicWand.ogg", pos, 1.0f, 0.75f); } } } + +void onDie(CBlob@ this) +{ + Sound::Play("MagicWand.ogg", this.getPosition(), 1.0f, 0.75f); +} diff --git a/Entities/Items/Scroll/ScrollFish.as b/Entities/Items/Scroll/ScrollFish.as index f3ba709..2d9a8d2 100644 --- a/Entities/Items/Scroll/ScrollFish.as +++ b/Entities/Items/Scroll/ScrollFish.as @@ -4,55 +4,49 @@ void onInit(CBlob@ this) { - this.addCommandID("spawn shark"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to summon a shark."); +} + +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ CMap@ map = getMap(); const int rand = XORRandom(300) - 150; const f32 posX = this.getPosition().x + rand + 25 * (rand > 0 ? 1 : -1); Vec2f spawnPos = Vec2f(posX, map.getLandYAtX(posX / map.tilesize) * map.tilesize) + Vec2f(0, -8); + //effects + const int radius = 5; + const f32 radsq = radius * 8 * radius * 8; + for (int x_step = -radius; x_step < radius; ++x_step) + { + for (int y_step = -radius; y_step < radius; ++y_step) + { + Vec2f off(x_step * map.tilesize, y_step * map.tilesize); + if (off.LengthSquared() > radsq) continue; + + Vec2f tpos = spawnPos + off; + map.SplashEffect(tpos, Vec2f(0, 10), 8.0f); + } + } + Sound::Play("MagicWand.ogg", spawnPos, 1.5f, 1.2f); + CBitStream params; params.write_Vec2f(spawnPos); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("spawn shark"), "Use this to summon a shark.", params); + this.SendCommand(this.getCommandID("server_execute_spell"), params); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("spawn shark")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { Vec2f spawnPos = params.read_Vec2f(); - - if (isClient()) - { - //effects - CMap@ map = getMap(); - const int radius = 5; - const f32 radsq = radius * 8 * radius * 8; - - for (int x_step = -radius; x_step < radius; ++x_step) - { - for (int y_step = -radius; y_step < radius; ++y_step) - { - Vec2f off(x_step * map.tilesize, y_step * map.tilesize); - if (off.LengthSquared() > radsq) continue; - - Vec2f tpos = spawnPos + off; - map.SplashEffect(tpos, Vec2f(0, 10), 8.0f); - } - } - - Sound::Play("MagicWand.ogg", spawnPos, 1.5f, 1.2f); - } - - if (isServer()) - { - server_CreateBlob("shark", -1, spawnPos); - this.server_Die(); - } + server_CreateBlob("shark", -1, spawnPos); + this.server_Die(); } } diff --git a/Entities/Items/Scroll/ScrollFlora.as b/Entities/Items/Scroll/ScrollFlora.as index 1a9bbf4..4cfdd27 100644 --- a/Entities/Items/Scroll/ScrollFlora.as +++ b/Entities/Items/Scroll/ScrollFlora.as @@ -6,87 +6,97 @@ const int radius = 14; void onInit(CBlob@ this) { - this.addCommandID("spawn flora"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to create plants nearby."); +} + +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ + CreateFlora(this); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("spawn flora"), "Use this to create plants nearby."); + Vec2f pos = this.getPosition(); + Sound::Play("MagicWand.ogg", pos); + Sound::Play("OrbExplosion.ogg", pos, 3.5f); + + if (!isServer()) + this.SendCommand(this.getCommandID("server_execute_spell")); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("spawn flora")) + if (cmd == this.getCommandID("server_execute_spell")) { - CMap@ map = getMap(); - Vec2f pos = this.getPosition(); - const f32 radsq = radius * 8 * radius * 8; + CreateFlora(this); + } +} - for (int x_step = -radius; x_step < radius; ++x_step) +void CreateFlora(CBlob@ this) +{ + CMap@ map = getMap(); + Vec2f pos = this.getPosition(); + const f32 radsq = radius * 8 * radius * 8; + + for (int x_step = -radius; x_step < radius; ++x_step) + { + for (int y_step = -radius; y_step < radius; ++y_step) { - for (int y_step = -radius; y_step < radius; ++y_step) - { - Vec2f off(x_step * map.tilesize, y_step * map.tilesize); + Vec2f off(x_step * map.tilesize, y_step * map.tilesize); - if (off.LengthSquared() > radsq) continue; + if (off.LengthSquared() > radsq) continue; - Vec2f tpos = pos + off; + Vec2f tpos = pos + off; - TileType t = map.getTile(tpos).type; - if (t == CMap::tile_empty && map.isTileGround(map.getTile(tpos+Vec2f(0,8)).type)) + TileType t = map.getTile(tpos).type; + if (t == CMap::tile_empty && map.isTileGround(map.getTile(tpos+Vec2f(0,8)).type)) + { + map.server_SetTile(tpos, CMap::tile_grass); + } + else if (t == CMap::tile_castle && XORRandom(3) == 0) + { + map.server_SetTile(tpos, CMap::tile_castle_moss); + } + else if (t == CMap::tile_castle_back && XORRandom(3) == 0) + { + map.server_SetTile(tpos, CMap::tile_castle_back_moss); + } + + if (!map.isTileSolid(t) && map.isTileGround(map.getTile(tpos+Vec2f(0,8)).type)) + { + Vec2f vel = getRandomVelocity(90, 1 + float(XORRandom(500)/100), 20); + string blob_name = "flowers"; + + if (XORRandom(2) == 0) { - map.server_SetTile(tpos, CMap::tile_grass); + blob_name = "bush"; } - else if (t == CMap::tile_castle && XORRandom(3) == 0) + else if (XORRandom(3) == 0) { - map.server_SetTile(tpos, CMap::tile_castle_moss); + vel *= 1.2f; + blob_name = "grain_plant"; } - else if (t == CMap::tile_castle_back && XORRandom(3) == 0) + else if (XORRandom(8) == 0) { - map.server_SetTile(tpos, CMap::tile_castle_back_moss); + vel *= 1.5f; + blob_name = "tree_bushy"; } - if (!map.isTileSolid(t) && map.isTileGround(map.getTile(tpos+Vec2f(0,8)).type)) + if (isServer()) { - Vec2f vel = getRandomVelocity(90, 1 + float(XORRandom(500)/100), 20); - string blob_name = "flowers"; - - if (XORRandom(2) == 0) - { - blob_name = "bush"; - } - else if (XORRandom(3) == 0) - { - vel *= 1.2f; - blob_name = "grain_plant"; - } - else if (XORRandom(8) == 0) - { - vel *= 1.5f; - blob_name = "tree_bushy"; - } - - if (isServer()) - { - server_CreateBlob(blob_name, -1, tpos); - } - - if (isClient()) - { - makeGibParticle("GenericGibs.png", tpos, vel, 7, 1+XORRandom(4), Vec2f(8, 8), 2.0f, 20, "fall2"); - } + server_CreateBlob(blob_name, -1, tpos); + } + + if (isClient()) + { + makeGibParticle("GenericGibs.png", tpos, vel, 7, 1+XORRandom(4), Vec2f(8, 8), 2.0f, 20, "fall2"); } } } - - if (isClient()) - { - Sound::Play("MagicWand.ogg", pos); - Sound::Play("OrbExplosion.ogg", pos, 3.5f); - } - - this.server_Die(); } -} + + this.server_Die(); +} \ No newline at end of file diff --git a/Entities/Items/Scroll/ScrollFowl.as b/Entities/Items/Scroll/ScrollFowl.as index 80f61aa..a95b50c 100644 --- a/Entities/Items/Scroll/ScrollFowl.as +++ b/Entities/Items/Scroll/ScrollFowl.as @@ -6,13 +6,19 @@ const u8 chicken_num = 3; void onInit(CBlob@ this) { - this.addCommandID("spawn chickens"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to summon a flock of chickens."); +} + +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ + Sound::Play("MagicWand.ogg", this.getPosition(), 1.0f, 0.9f); + CBitStream params; for (u8 i = 0; i < chicken_num; ++i) { @@ -20,37 +26,29 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) const int rand = XORRandom(300) - 150; const f32 posX = this.getPosition().x + rand + 25 * (rand > 0 ? 1 : -1); Vec2f spawnPos = Vec2f(posX, map.getLandYAtX(posX / map.tilesize) * map.tilesize) + Vec2f(0, -16); + + for (u8 i = 0; i < 5; i++) + { + Vec2f vel = getRandomVelocity(-90.0f, 2, 360.0f); + ParticleAnimated("FireFlash", spawnPos, vel, float(XORRandom(360)), 1.0f, 2 + XORRandom(3), -0.1f, false); + } params.write_Vec2f(spawnPos); } - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("spawn chickens"), "Use this to summon a flock of chickens.", params); + + this.SendCommand(this.getCommandID("server_execute_spell"), params); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("spawn chickens")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { for (u8 i = 0; i < chicken_num; ++i) { Vec2f spawnPos = params.read_Vec2f(); - - if (isClient()) - { - //effects - for (u8 i = 0; i < 5; i++) - { - Vec2f vel = getRandomVelocity(-90.0f, 2, 360.0f); - ParticleAnimated("FireFlash", spawnPos, vel, float(XORRandom(360)), 1.0f, 2 + XORRandom(3), -0.1f, false); - } - } - - if (isServer()) - { - server_CreateBlob("chicken", -1, spawnPos); - } + server_CreateBlob("chicken", -1, spawnPos); } - Sound::Play("MagicWand.ogg", this.getPosition(), 1.0f, 0.9f); this.server_Die(); } } diff --git a/Entities/Items/Scroll/ScrollMidas.as b/Entities/Items/Scroll/ScrollMidas.as index c84ba51..f8134c1 100644 --- a/Entities/Items/Scroll/ScrollMidas.as +++ b/Entities/Items/Scroll/ScrollMidas.as @@ -6,19 +6,18 @@ const int radius = 5; void onInit(CBlob@ this) { - this.addCommandID("midas"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("midas"), getTranslatedString("Use this to turn all stone in the area into gold")); + caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("server_execute_spell"), getTranslatedString("Use this to turn all stone in the area into gold")); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("midas")) + if (cmd == this.getCommandID("server_execute_spell")) { bool acted = false; CMap@ map = getMap(); @@ -46,11 +45,14 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) } } - if (acted) { this.server_Die(); - Sound::Play("MagicWand.ogg", pos); } } } + +void onDie(CBlob@ this) +{ + Sound::Play("MagicWand.ogg", this.getPosition()); +} diff --git a/Entities/Items/Scroll/ScrollRevive.as b/Entities/Items/Scroll/ScrollRevive.as index ce59c1e..aca755c 100644 --- a/Entities/Items/Scroll/ScrollRevive.as +++ b/Entities/Items/Scroll/ScrollRevive.as @@ -5,7 +5,8 @@ void onInit(CBlob@ this) { - this.addCommandID("revive"); + this.addCommandID("server_revive"); + this.addCommandID("client_revive"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) @@ -15,87 +16,104 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) CBitStream params; params.write_netid(caller.getNetworkID()); params.write_bool(false); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("revive"), "Use this near a dead body to ressurect them.", params); + caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("server_revive"), "Use this near a dead body to ressurect them.", params); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("revive")) + if (cmd == this.getCommandID("server_revive") && isServer()) { Vec2f pos = this.getPosition(); CBlob@ caller = getBlobByNetworkID(params.read_netid()); if (caller is null) return; + Vec2f[] revived_positions; + if (params.read_bool()) //reviving self { CPlayer@ player = caller.getPlayer(); if (player !is null) { RevivePlayer(player, caller); + revived_positions.push_back(caller.getPosition()); this.server_Die(); } - return; } - - CBlob@[] blobsInRadius; - if (getMap().getBlobsInRadius(pos, 40.0f, @blobsInRadius)) + else { - const u16 blobsLength = blobsInRadius.length; - for (u16 i = 0; i < blobsLength; i++) + CBlob@[] blobsInRadius; + if (getMap().getBlobsInRadius(pos, 40.0f, @blobsInRadius)) { - CBlob@ b = blobsInRadius[i]; - if (b.hasTag("dead") && !b.hasTag("undead")) + const u16 blobsLength = blobsInRadius.length; + for (u16 i = 0; i < blobsLength; i++) { - CPlayer@ player = getPlayerByUsername(b.get_string("player_username")); //RunnerDeath.as - if ((player is null || player.getBlob() !is null) && b.getName() != "migrant") continue; - - RevivePlayer(player, b); - this.server_Die(); + CBlob@ b = blobsInRadius[i]; + if (b.hasTag("dead") && !b.hasTag("undead")) + { + CPlayer@ player = getPlayerByUsername(b.get_string("player_username")); //RunnerDeath.as + if ((player is null || player.getBlob() !is null) && b.getName() != "migrant") continue; + + RevivePlayer(player, b); + revived_positions.push_back(b.getPosition()); + this.server_Die(); + } } } } + const u8 revived_count = revived_positions.length; + if (revived_count > 0) + { + CBitStream bs; + bs.write_u8(revived_count); + for (u16 i = 0; i < revived_count; i++) + { + bs.write_Vec2f(revived_positions[i]); + } + this.SendCommand(this.getCommandID("client_revive"), bs); + } + } + else if (cmd == this.getCommandID("client_revive") && isClient()) + { + const u8 revived_count = params.read_u8(); + for (u8 i = 0; i < revived_count; i++) + { + Vec2f bpos = params.read_Vec2f(); + ParticleZombieLightning(bpos); + Sound::Play("MagicWand.ogg", bpos); + + for (u8 i = 0; i < 20; i++) + { + Vec2f vel = getRandomVelocity(-90.0f, 4, 360.0f); + ParticleAnimated("HealParticle", bpos, vel, float(XORRandom(360)), 1.2f, 4, 0, false); + } + } } } void RevivePlayer(CPlayer@ player, CBlob@ b) { Vec2f bpos = b.getPosition(); - if (isClient()) + //create new blob for our dead player to use + CBlob@ newBlob = server_CreateBlob(b.getName(), 0, bpos); + if (player !is null) { - ParticleZombieLightning(bpos); - Sound::Play("MagicWand.ogg", bpos); - - for (u8 i = 0; i < 20; i++) - { - Vec2f vel = getRandomVelocity(-90.0f, 4, 360.0f); - ParticleAnimated("HealParticle", bpos, vel, float(XORRandom(360)), 1.2f, 4, 0, false); - } - } - - if (isServer()) - { - //create new blob for our dead player to use - CBlob@ newBlob = server_CreateBlob(b.getName(), 0, bpos); - if (player !is null) - { - newBlob.server_SetPlayer(player); + newBlob.server_SetPlayer(player); - //remove respawn - Respawn[]@ respawns; - if (getRules().get("respawns", @respawns)) + //remove respawn + Respawn[]@ respawns; + if (getRules().get("respawns", @respawns)) + { + for (u8 i = 0; i < respawns.length; i++) { - for (u8 i = 0; i < respawns.length; i++) - { - if (respawns[i].username != player.getUsername()) continue; - - respawns.erase(i); - break; - } + if (respawns[i].username != player.getUsername()) continue; + + respawns.erase(i); + break; } } - - //kill dead body - b.server_Die(); } + + //kill dead body + b.server_Die(); } diff --git a/Entities/Items/Scroll/ScrollRoyalty.as b/Entities/Items/Scroll/ScrollRoyalty.as index cdb3745..16a3838 100644 --- a/Entities/Items/Scroll/ScrollRoyalty.as +++ b/Entities/Items/Scroll/ScrollRoyalty.as @@ -4,43 +4,29 @@ void onInit(CBlob@ this) { - this.addCommandID("spawn geti"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to summon a geti."); +} - CBitStream params; - params.write_u8(caller.getTeamNum()); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("spawn geti"), "Use this to summon a geti.", params); +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ + Vec2f pos = this.getPosition(); + Sound::Play("MigrantSayHello.ogg", pos); + Sound::Play("AchievementUnlocked.ogg", pos, 2.0f); + + this.SendCommand(this.getCommandID("server_execute_spell")); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("spawn geti")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { - const u8 team = params.read_u8(); - - u8 rand_team = XORRandom(7); - while(rand_team == team) - { - rand_team = XORRandom(7); - } - - Vec2f pos = this.getPosition(); - - if (isClient()) - { - //effects - Sound::Play("MigrantSayHello.ogg", pos); - Sound::Play("AchievementUnlocked.ogg", pos, 2.0f); - } - - if (isServer()) - { - server_CreateBlob("princess", rand_team, pos); - this.server_Die(); - } + server_CreateBlob("princess", XORRandom(7), this.getPosition()); + this.server_Die(); } } diff --git a/Entities/Items/Scroll/ScrollSea.as b/Entities/Items/Scroll/ScrollSea.as index ba55a45..203991a 100644 --- a/Entities/Items/Scroll/ScrollSea.as +++ b/Entities/Items/Scroll/ScrollSea.as @@ -6,19 +6,19 @@ const u8 required_ground_at_Y = 8; //amount of ground tiles at the scroll's Y le void onInit(CBlob@ this) { - this.addCommandID("create water"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("create water"), "Use this to generate a source of water."); + caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("server_execute_spell"), "Use this to generate a source of water."); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("create water")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { CMap@ map = getMap(); Vec2f pos = this.getPosition(); @@ -32,21 +32,17 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) ground_tiles++; if (ground_tiles >= required_ground_at_Y) { - if (isClient()) - { - //effects - ParticleZombieLightning(pos); - Sound::Play("ResearchComplete.ogg"); - } - - if (isServer()) - { - map.server_setFloodWaterWorldspace(pos, true); - this.server_Die(); - } + map.server_setFloodWaterWorldspace(pos, true); + this.server_Die(); break; } } } } + +void onDie(CBlob@ this) +{ + ParticleZombieLightning(this.getPosition()); + Sound::Play("ResearchComplete.ogg"); +} diff --git a/Entities/Items/Scroll/ScrollStone.as b/Entities/Items/Scroll/ScrollStone.as index 79abb48..ad5b4fb 100644 --- a/Entities/Items/Scroll/ScrollStone.as +++ b/Entities/Items/Scroll/ScrollStone.as @@ -6,19 +6,19 @@ const int radius = 10; void onInit(CBlob@ this) { - this.addCommandID("stone"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("stone"), "Use this to convert nearby stone into thick stone."); + caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("server_execute_spell"), "Use this to convert nearby stone into thick stone."); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("stone")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { bool acted = false; CMap@ map = getMap(); @@ -48,11 +48,14 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) } } - if (acted) { - Sound::Play("MagicWand.ogg", pos); this.server_Die(); } } } + +void onDie(CBlob@ this) +{ + Sound::Play("MagicWand.ogg", this.getPosition()); +} diff --git a/Entities/Items/Scroll/ScrollSuddenGib.as b/Entities/Items/Scroll/ScrollSuddenGib.as index f9043d8..5a9c49b 100644 --- a/Entities/Items/Scroll/ScrollSuddenGib.as +++ b/Entities/Items/Scroll/ScrollSuddenGib.as @@ -5,7 +5,7 @@ void onInit(CBlob@ this) { - this.addCommandID("sudden gib"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) @@ -14,15 +14,14 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) CBitStream params; params.write_netid(caller.getNetworkID()); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("sudden gib"), getTranslatedString("Use this to make all visible enemies instantly turn into a pile of gibs."), params); + caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("server_execute_spell"), getTranslatedString("Use this to make all visible enemies instantly turn into a pile of gibs."), params); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("sudden gib")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { Vec2f pos = this.getPosition(); - ParticleZombieLightning(pos); bool hit = false; CBlob@ caller = getBlobByNetworkID(params.read_netid()); @@ -50,7 +49,12 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) if (hit) { this.server_Die(); - Sound::Play("SuddenGib.ogg"); } } +} + +void onDie(CBlob@ this) +{ + ParticleZombieLightning(this.getPosition()); + Sound::Play("SuddenGib.ogg"); } \ No newline at end of file diff --git a/Entities/Items/Scroll/ScrollTeleport.as b/Entities/Items/Scroll/ScrollTeleport.as index 3f7b673..f5bf372 100644 --- a/Entities/Items/Scroll/ScrollTeleport.as +++ b/Entities/Items/Scroll/ScrollTeleport.as @@ -5,46 +5,40 @@ void onInit(CBlob@ this) { - this.addCommandID("teleport"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to teleport to the area you are pointing to."); +} + +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ + Vec2f aim = caller.getAimPos(); + Vec2f pos = this.getPosition(); + if ((aim - pos).Length() < 100.0f) return; + + CMap@ map = getMap(); + TileType t = map.getTile(aim).type; + if (map.isTileSolid(t)) return; + + if (caller.isMyPlayer()) + { + caller.setPosition(aim); + } + + ParticleTeleport(pos); + ParticleZombieLightning(aim); - CBitStream params; - params.write_netid(caller.getNetworkID()); - params.write_Vec2f(caller.getAimPos()); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("teleport"), "Use this to teleport to the area you are pointing to.", params); + this.SendCommand(this.getCommandID("server_execute_spell")); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("teleport")) - { - CBlob@ caller = getBlobByNetworkID(params.read_netid()); - if (caller is null) return; - - Vec2f aim = params.read_Vec2f(); - Vec2f pos = this.getPosition(); - if ((aim - pos).Length() < 100.0f) return; - - CMap@ map = getMap(); - TileType t = map.getTile(aim).type; - if (map.isTileSolid(t)) return; - - if (isClient()) - { - //effects - ParticleTeleport(pos); - ParticleZombieLightning(aim); - } - - if (caller.isMyPlayer()) - { - caller.setPosition(aim); - } - + if (cmd == this.getCommandID("server_execute_spell") && isServer()) + { this.server_Die(); } } diff --git a/Entities/Items/Scroll/ScrollWisent.as b/Entities/Items/Scroll/ScrollWisent.as index cdea199..1a2e43e 100644 --- a/Entities/Items/Scroll/ScrollWisent.as +++ b/Entities/Items/Scroll/ScrollWisent.as @@ -4,46 +4,42 @@ void onInit(CBlob@ this) { - this.addCommandID("spawn bison"); + this.addCommandID("server_execute_spell"); } void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || (this.getPosition() - caller.getPosition()).Length() > 50.0f) return; - + caller.CreateGenericButton(11, Vec2f_zero, this, Callback_Spell, "Use this to summon a bison."); +} + +void Callback_Spell(CBlob@ this, CBlob@ caller) +{ CMap@ map = getMap(); const int rand = XORRandom(300) - 150; const f32 posX = this.getPosition().x + rand + 25 * (rand > 0 ? 1 : -1); Vec2f spawnPos = Vec2f(posX, map.getLandYAtX(posX / map.tilesize) * map.tilesize) + Vec2f(0, -16); + for (u8 i = 0; i < 20; i++) + { + Vec2f vel = getRandomVelocity(-90.0f, 2, 360.0f); + ParticleAnimated("MediumSteam", spawnPos, vel, float(XORRandom(360)), 1.0f, 2 + XORRandom(3), -0.1f, false); + } + + Sound::Play("Bomb.ogg", spawnPos); + Sound::Play("MagicWand.ogg", spawnPos, 1.0f, 0.8f); + CBitStream params; params.write_Vec2f(spawnPos); - caller.CreateGenericButton(11, Vec2f_zero, this, this.getCommandID("spawn bison"), "Use this to summon a bison.", params); + this.SendCommand(this.getCommandID("server_execute_spell"), params); } void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("spawn bison")) + if (cmd == this.getCommandID("server_execute_spell") && isServer()) { Vec2f spawnPos = params.read_Vec2f(); - - if (isClient()) - { - //effects - for (u8 i = 0; i < 20; i++) - { - Vec2f vel = getRandomVelocity(-90.0f, 2, 360.0f); - ParticleAnimated("MediumSteam", spawnPos, vel, float(XORRandom(360)), 1.0f, 2 + XORRandom(3), -0.1f, false); - } - - Sound::Play("Bomb.ogg", spawnPos); - Sound::Play("MagicWand.ogg", spawnPos, 1.0f, 0.8f); - } - - if (isServer()) - { - server_CreateBlob("bison", -1, spawnPos); - this.server_Die(); - } + server_CreateBlob("bison", -1, spawnPos); + this.server_Die(); } } diff --git a/Entities/Structures/Building/Building.as b/Entities/Structures/Building/Building.as index 8931dbe..84774ac 100644 --- a/Entities/Structures/Building/Building.as +++ b/Entities/Structures/Building/Building.as @@ -1,3 +1,4 @@ + // Genreic building #include "Requirements.as" @@ -17,9 +18,14 @@ void onInit(CBlob@ this) this.set_TileType("background tile", CMap::tile_wood_back); //this.getSprite().getConsts().accurateLighting = true; + ShopMadeItem@ onMadeItem = @onShopMadeItem; + this.set("onShopMadeItem handle", @onMadeItem); + this.getSprite().SetZ(-50); //background this.getShape().getConsts().mapCollisions = false; + this.Tag("has window"); + //INIT COSTS InitCosts(); @@ -89,29 +95,53 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) this.set_bool("shop available", false); } +void onShopMadeItem(CBitStream@ params) +{ + if (!isServer()) return; + + u16 this_id, caller_id, item_id; + string name; + + if (!params.saferead_u16(this_id) || !params.saferead_u16(caller_id) || !params.saferead_u16(item_id) || !params.saferead_string(name)) + { + return; + } + + CBlob@ this = getBlobByNetworkID(this_id); + if (this is null) return; + + CBlob@ caller = getBlobByNetworkID(caller_id); + if (caller is null) return; + + CBlob@ item = getBlobByNetworkID(item_id); + if (item is null) return; + + this.Tag("shop disabled"); //no double-builds + this.Sync("shop disabled", true); + + this.server_Die(); +} + void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - bool isServer = getNet().isServer(); - if (cmd == this.getCommandID("shop made item")) + if (cmd == this.getCommandID("shop made item client") && isClient()) { - this.Tag("shop disabled"); //no double-builds + u16 this_id, caller_id, item_id; + string name; + + if (!params.saferead_u16(this_id) || !params.saferead_u16(caller_id) || !params.saferead_u16(item_id) || !params.saferead_string(name)) + { + return; + } + + CBlob@ caller = getBlobByNetworkID(caller_id); + CBlob@ item = getBlobByNetworkID(item_id); - CBlob@ caller = getBlobByNetworkID(params.read_netid()); - CBlob@ item = getBlobByNetworkID(params.read_netid()); if (item !is null && caller !is null) { this.getSprite().PlaySound("/Construct.ogg"); this.getSprite().getVars().gibbed = true; - this.server_Die(); caller.ClearMenus(); - - // open factory upgrade menu immediately - if (item.getName() == "factory") - { - CBitStream factoryParams; - factoryParams.write_netid(caller.getNetworkID()); - item.SendCommand(item.getCommandID("upgrade factory menu"), factoryParams); - } } } } diff --git a/Entities/Structures/Factory/Factory.as b/Entities/Structures/Factory/Factory.as index 9f4404e..4decdf0 100644 --- a/Entities/Structures/Factory/Factory.as +++ b/Entities/Structures/Factory/Factory.as @@ -22,13 +22,11 @@ void onInit(CBlob@ this) { this.Tag("huffpuff production"); // for production.as - this.addCommandID("upgrade factory menu"); this.addCommandID("upgrade factory"); - this.addCommandID("complete upgrade factory"); + this.addCommandID("client_upgrade_factory"); this.addCommandID("pause production"); this.addCommandID("unpause production"); this.addCommandID("attach worker"); - this.addCommandID("sync worker tag bullshit"); this.set_TileType("background tile", CMap::tile_wood_back); @@ -58,9 +56,11 @@ void onTick(CBlob@ this) CBitStream params; params.write_netid(getWorkerID(this)); this.SendCommand(this.getCommandID(worker_out_cmd), params); - - this.SendCommand(this.getCommandID("sync worker tag bullshit")); //why the fuck does .sync not fucking work i hate kag so fucking much. - //NOTHING EVER WORKS. + + this.Tag("production paused"); + returnWorker(this, getHallsFor(this, BASE_RADIUS), worker); + this.Untag(worker_tag); + this.Sync(worker_tag, true); worker.set_netid("owner id", 0); } @@ -94,7 +94,7 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) else if (!hasTech(this) && caller.isOverlapping(this)) { params.write_netid(caller.getNetworkID()); - caller.CreateGenericButton(12, Vec2f(0, 0), this, this.getCommandID("upgrade factory menu"), getTranslatedString("Convert Factory"), params); + caller.CreateGenericButton(12, Vec2f(0, 0), this, BuildUpgradeMenu, getTranslatedString("Convert Factory")); } else if (!this.hasTag(worker_tag)) { @@ -109,6 +109,9 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) void AttachMigrantToFactory(CBlob@ this, CBlob@ migrant) { migrant.server_DetachFromAll(); + attachWorker(this, migrant, this.getShape().getHeight()); + this.Tag(worker_tag); + this.Untag("production paused"); CBitStream params; params.write_netid(migrant.getNetworkID()); this.SendCommand(this.getCommandID(worker_in_cmd), params); @@ -118,12 +121,10 @@ void AttachMigrantToFactory(CBlob@ this, CBlob@ migrant) void BuildUpgradeMenu(CBlob@ this, CBlob@ caller) { ScrollSet@ set = getScrollSet("factory options"); - if (caller !is null && caller.isMyPlayer() && set !is null) + if (caller !is null && set !is null) { caller.ClearMenus(); - //caller.Tag("dont clear menus"); // dont clear menus in StandardControls.as - CControls@ controls = caller.getControls(); int size = Maths::Ceil(Maths::Sqrt(set.names.length)); CGridMenu@ menu = CreateGridMenu(caller.getScreenPos() + Vec2f(0.0f, 50.0f), this, Vec2f(size, size), getTranslatedString("Upgrade to...")); if (menu !is null) @@ -189,12 +190,7 @@ void AddButtonsForSet(CBlob@ this, CBlob@ caller, CGridMenu@ menu, ScrollSet@ se void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("upgrade factory menu")) - { - CBlob@ caller = getBlobByNetworkID(params.read_u16()); - BuildUpgradeMenu(this, caller); - } - else if (cmd == this.getCommandID("upgrade factory")) + if (cmd == this.getCommandID("upgrade factory")) { if (isServer() && this.get_string("tech name").size() == 0) { @@ -211,23 +207,25 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) if (this.get_u8("migrants count") > 0 || this.hasTag(worker_tag)) { + this.Untag("production paused"); this.SendCommand(this.getCommandID("unpause production")); } else { + this.Tag("production paused"); this.SendCommand(this.getCommandID("pause production")); } + AddProductionItemsFromTech(this, defname); CBitStream bs; bs.write_string(defname); - this.SendCommand(this.getCommandID("complete upgrade factory"), bs); //shitcode, i dont really care... LOL !!!! + this.SendCommand(this.getCommandID("client_upgrade_factory"), bs); //shitcode, i dont really care... LOL !!!! } } - else if (cmd == this.getCommandID("complete upgrade factory")) + else if (cmd == this.getCommandID("client_upgrade_factory")) { const string defname = params.read_string(); - this.set_string("tech name", defname); AddProductionItemsFromTech(this, defname); this.getSprite().PlaySound("/ConstructShort.ogg"); } @@ -241,21 +239,14 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) this.Untag("production paused"); this.getSprite().PlaySound("/PowerUp.ogg"); } - else if (cmd == this.getCommandID("attach worker")) + else if (cmd == this.getCommandID("attach worker") && isServer()) { - if (isServer()) + CBlob@ worker = getBlobByNetworkID(params.read_netid()); + if (worker !is null && !this.hasTag(worker_tag)) { - CBlob@ worker = getBlobByNetworkID(params.read_netid()); - if (worker !is null && !this.hasTag(worker_tag)) - { - AttachMigrantToFactory(this, worker); - } + AttachMigrantToFactory(this, worker); } } - else if (cmd == this.getCommandID("sync worker tag bullshit")) - { - this.Untag(worker_tag); - } } void AddProductionItemsFromTech(CBlob@ this, const string &in defname) { @@ -277,7 +268,11 @@ void AddProductionItemsFromTech(CBlob@ this, const string &in defname) } } - this.set_string("tech name", defname); + if (isServer()) + { + this.set_string("tech name", defname); + this.Sync("tech name", true); + } this.setInventoryName(def.name + " Factory"); this.inventoryIconFrame = def.scrollFrame; diff --git a/Entities/Structures/Factory/Production.as b/Entities/Structures/Factory/Production.as new file mode 100644 index 0000000..d7acfd8 --- /dev/null +++ b/Entities/Structures/Factory/Production.as @@ -0,0 +1,685 @@ +//MAY 4, 2024 +//THIS FILE WAS ADDED TO PATCH A BASE-KAG BUG. PLEASE REMOVE THIS FILE WHEN THE BASE KAG FILE IS FIXED. +//THE PARTICULAR FIX FOR THIS MOD IS AT LINE 118, WHERE AS BASE KAG IS MISSING THIS LINE. + + + +// production menu +// set_string "produce sound" to override default production sound +// this.Tag("huffpuff production"); for production effects +// this.set_Vec2f("production offset", Vec2f() ); for changing where blobs appear + +#include "ProductionCommon.as"; +#include "ShopCommon.as"; +#include "Requirements_Tech.as"; +#include "CrateCommon.as"; +#include "MakeFood.as"; +#include "MakeSeed.as"; +#include "FireParticle.as"; +#include "GenericButtonCommon.as"; + +const uint OPT_TICK = 45; + +void onInit(CBlob@ this) +{ + InitArrays(this); + + this.addCommandID("factory give item"); + this.addCommandID("add req"); + this.addCommandID("track blob"); + + this.getCurrentScript().tickFrequency = OPT_TICK; // opt +} + +void onTick(CBlob@ this) +{ + const u32 time = getGameTime(); + ShopItem[]@ items; + ShopItem[]@ queue; + + if (!this.get(PRODUCTION_ARRAY, @items)) return; + if (!this.get(PRODUCTION_QUEUE, @queue)) return; + + if (this.hasTag("production paused")) + { + for (uint i = 0 ; i < items.length; i++) + { + ShopItem @item = items[i]; + item.timeCreated = time; + } + return; + } + + // only if producing... + for (uint i = 0 ; i < items.length; i++) + { + ShopItem @item = items[i]; + item.inProductionNow = false; + + if (item.producing) + { + item.inStock = hasLimitReached(this, item); + bool reqs = hasRequirements_Tech(this.getInventory(), item.requirements, item.requirementsMissing); + item.hasRequirements = reqs; + item.inProductionNow = !item.inStock && item.hasRequirements; + + if (item.ticksToMake > 1) + { + if (item.inProductionNow) + { + bool found = false; + for (uint q_step = 0; q_step < queue.length; q_step++) + if (queue[q_step].name == item.name) + { + found = true; + break; + } + + if (!found) + { + //printf("add to queue " +item.name + " " + item.timeCreated + " make " + item.ticksToMake ); + queue.push_back(item); + } + } + else + { + item.timeCreated = time; + } + } + + } + } + + for (uint i = 0; i < queue.length; i++) + { + ShopItem @item = queue[i]; + + if (i != 0) //don't progress unless we're the first one + { + item.timeCreated = time; + continue; + } + + if (item.inProductionNow && + item.ticksToMake > 1 && // on demand + item.timeCreated + item.ticksToMake < time) + { + queue.erase(i--); + // make item + if (getNet().isServer()) + { + CBlob@ blob = MakeSingleItem(this, item); + if (blob !is null) + { + if (this.isInventoryAccessible(null)) + this.server_PutInInventory(blob); + + // track it + this.push(PRODUCTION_TRACKING_ARRAY, blob.getNetworkID()); + CBitStream params; + params.write_u16(blob.getNetworkID()); + this.SendCommand(this.getCommandID("track blob"), params); + } + } + // make again + item.timeCreated = time; + } + + // effects + + if (this.hasTag("huffpuff production") && item.inProductionNow && XORRandom(5) == 0) + { + // JIT CRASHRS HERE! + Sound::Play("/ProduceSound", this.getPosition()); + makeSmokeParticle(this.getPosition() + Vec2f(0.0f, -this.getRadius() / 2.0f)); + } + } + + //drop any converted vehicles + u16[]@ ids; + if (this.get(PRODUCTION_TRACKING_ARRAY, @ids)) + { + int team = this.getTeamNum(); + + for (uint i = 0; i < ids.length; i++) + { + CBlob@ b = getBlobByNetworkID(ids[i]); + if (b !is null) + { + if (b.getTeamNum() != team && //changed team + b.hasTag("vehicle")) //vehicle + { + ids.erase(i--); + } + } + } + } + +} + +void InitArrays(CBlob@ this) +{ + if (!this.exists(PRODUCTION_ARRAY)) + { + ShopItem[] items; + this.set(PRODUCTION_ARRAY, items); + } + if (!this.exists(PRODUCTION_QUEUE)) + { + ShopItem[] items; + this.set(PRODUCTION_QUEUE, items); + } + if (!this.exists(PRODUCTION_TRACKING_ARRAY)) + { + u16[] ids; + this.set(PRODUCTION_TRACKING_ARRAY, ids); + } +} + +CBlob@ MakeSingleItem(CBlob@ this, ShopItem@ item) +{ + CInventory@ inv = this.getInventory(); + CBitStream missing; + if (item !is null && hasRequirements_Tech(inv, item.requirements, missing)) + { + const string blobName = item.spawnInCrate ? "crate" : item.blobName; + Vec2f spawnPos = this.getPosition() + getRandomVelocity(90.0f, 6.0f, 360.0f); + if (this.exists("production offset")) + { + spawnPos += this.get_Vec2f("production offset"); + } + + if (blobName == "seed") // MakeSeed - this needs to be done differently by some global name cache - to make standard foods, scrolls, seeds etc and not waste space of specific data + { + return server_MakeSeed(spawnPos, item.name); + } + else if (blobName == "food") // MakeFood - this needs to be done differently by some global name cache - to make standard foods, scrolls, seeds etc and not waste space of specific data + { + //printf("MAKE FOOD " + item.name + " " + item.customData ); + server_TakeRequirements(inv, item.requirements); + return server_MakeFood(spawnPos, item.name, item.customData); + } + else // everything else + { + CBlob@ blob = server_CreateBlobNoInit(blobName); + if (blob !is null) + { + server_TakeRequirements(inv, item.requirements); + + blob.server_setTeamNum(this.getTeamNum()); + blob.setPosition(spawnPos); + + if (item.spawnInCrate) + { + SetCratePacked(blob, item.blobName, item.name, this.inventoryIconFrame); + blob.set_u16("msg blob", this.getNetworkID()); + } + + item.timeCreated = getGameTime(); + blob.Init(); + return blob; + } + } + } + + return null; +} + +CBlob@ Produce(CBlob@ this, const string &in name) +{ + ShopItem[]@ items; + if (this.get(PRODUCTION_ARRAY, @items)) + { + for (uint i = 0 ; i < items.length; i++) + { + ShopItem @item = items[i]; + if (item.producing && item.blobName == name) + { + if (item.spawnInCrate) // note: food doesn't work with crates + { + // check for crate in storage first + CInventory@ inv = this.getInventory(); + for (int i = 0; i < inv.getItemsCount(); i++) + { + CBlob@ invblob = inv.getItem(i); + if (invblob.getName() == "crate" && hasPacked(invblob, item.blobName)) + { + return invblob; + } + } + + // produce the crate + return MakeSingleItem(this, item); + } + else + { + // check for item in storage first + CBlob@ invblob = this.server_PutOutInventory(item.blobName); + if (invblob !is null) + { + return invblob; + } + + // produce the item + return MakeSingleItem(this, item); + } + } + } + } + warn("production item " + name + " not found"); + return null; +} + +void onSendCreateData(CBlob@ this, CBitStream@ stream) +{ + ShopSendCreateData(this, stream, PRODUCTION_ARRAY); + + u16[]@ ids; + if (this.get(PRODUCTION_TRACKING_ARRAY, @ids)) + { + stream.write_u16(ids.length); + for (uint i = 0; i < ids.length; i++) + { + stream.write_u16(ids[i]); + } + } + else + stream.write_u16(0); +} + +bool onReceiveCreateData(CBlob@ this, CBitStream@ stream) +{ + if (!ShopReceiveCreateData(this, stream, PRODUCTION_ARRAY)) + return false; + + u16 trackCount; + if (!stream.saferead_u16(trackCount)) + { + warn("failed to read trackCount"); + return false; + } + + for (uint i = 0; i < trackCount; i++) + { + u16 id; + if (!stream.saferead_u16(id)) + { + return false; + } + this.push(PRODUCTION_TRACKING_ARRAY, id); + } + return true; +} + +void onCommand(CBlob@ this, u8 cmd, CBitStream @params) +{ + bool isServer = getNet().isServer(); + if (isServer && cmd == this.getCommandID("factory give item")) + { + const u16 ID = params.read_u16(); + const string itemName = params.read_string(); + CBlob@ blob = getBlobByNetworkID(ID); + if (blob !is null) + { + CBlob@ item = Produce(this, itemName); + if (item !is null) + { + blob.server_PutInInventory(item); + } + } + } + else if (isServer && cmd == this.getCommandID("add req")) + { + const u16 callerID = params.read_u16(); + const string itemName = params.read_string(); + CBlob@ caller = getBlobByNetworkID(callerID); + if (caller !is null) + { + // take all itemName blobs from inv and from hands of caller + CBlob@ item = caller.server_PutOutInventory(itemName); + if (item is null) + { + CBlob@ blob = caller.getCarriedBlob(); + if (blob !is null && blob.getName() == itemName) + { + blob.server_DetachFromAll(); + @item = blob; + } + } + + while (item !is null) + { + putInFood(this, item); + + this.server_PutInInventory(item); + item.server_SetHealth(0.0f); + item.Tag("dead"); + + @item = caller.server_PutOutInventory(itemName); + if (item is null) + { + CBlob@ blob = caller.getCarriedBlob(); + if (blob !is null && blob.getName() == itemName) + { + blob.server_DetachFromAll(); + @item = blob; + } + } + } + } + } + else if (cmd == this.getCommandID("track blob")) + { + const u16 id = params.read_u16(); + CBlob@ blob = getBlobByNetworkID(id); + if (blob !is null) + { + // track it + this.push(PRODUCTION_TRACKING_ARRAY, id); + // sound + if (this.exists("produce sound")) + this.getSprite().PlaySound(this.get_string("produce sound")); + else + this.getSprite().PlaySound("BombMake.ogg"); + } + } +} + +bool hasLimitReached(CBlob@ this, ShopItem@ item) +{ + if (item.quantityLimit == 0) // infinite + { + return false; + } + else + { + // is this item somewhere around + + uint count = 0; + // TODO CRATES + u16[]@ ids; + if (this.get(PRODUCTION_TRACKING_ARRAY, @ids)) + { + for (uint i = 0; i < ids.length; i++) + { + CBlob@ blob = getBlobByNetworkID(ids[i]); + if (blob !is null) + { + if (item.spawnInCrate) + { + if (blob.getName() == "crate" && blob.exists("packed") && blob.get_string("packed") == item.blobName) + count++; + } + + if (item.blobName == "food") + { + if (blob.get_string("food name") == item.name) + count++; + } + else + { + if (blob.getName() == item.blobName) + count++; + } + } + else // remove it from the list + { + ids.removeAt(i); + i--; + } + } + } + + return count >= item.quantityLimit; + } +} + +bool isInventoryAccessible(CBlob@ this, CBlob@ forBlob) +{ + return (this.hasTag("inventory access") && canSeeButtons(this, forBlob)); +} + +// kitchen related + +void putInFood(CBlob@ this, CBlob@ item) +{ + if (!this.exists("food")) return; + + string name = "amount " + item.getName(); + if (this.exists(name)) + { + s16 food = this.get_s16("food"); + food += this.get_u8(name); + food -= 1; + this.set_s16("food", food); + } +} + +// add buttons to add requirement, if available + +void GetButtonsFor(CBlob@ this, CBlob@ caller) +{ + if (!canSeeButtons(this, caller)) return; + + string[] buttonsCreated; + ShopItem[]@ prod_items; + if (this.get(PRODUCTION_ARRAY, @prod_items)) + { + uint item_count = prod_items.length; + for (uint i = 0 ; i < item_count; i++) + { + ShopItem @item = prod_items[i]; + if (item !is null && item.producing) + { + // parse requirement stream + string text, requiredType, name, friendlyName; + u16 quantity = 0; + item.requirements.ResetBitIndex(); + while (!item.requirements.isBufferEnd()) + { + ReadRequirement(item.requirements, requiredType, name, friendlyName, quantity); + int count = 0; + if (caller.hasBlob(name, 1)) + { + // check if not already added + bool added = false; + for (uint bInd = 0 ; bInd < buttonsCreated.length; bInd++) + { + if (buttonsCreated[bInd] == name) + { + added = true; + break; + } + } + + if (!added) + { + CBitStream params; + params.write_u16(caller.getNetworkID()); + params.write_string(name); + caller.CreateGenericButton("$" + name + "$", Vec2f(-4.0f * item_count + 12.0f * i, -count), this, this.getCommandID("add req"), "Put in " + friendlyName, params); + count++; + buttonsCreated.push_back(name); + } + } + } + } + } + } +} + +// SPRITE + + +void onRender(CSprite@ this) +{ + CBlob@ localBlob = getLocalPlayerBlob(); + if (localBlob is null) + return; + + CBlob@ blob = this.getBlob(); + Vec2f center = blob.getPosition(); + Vec2f mouseWorld = getControls().getMouseWorldPos(); + const f32 renderRadius = (blob.getRadius()) * 0.95f; + bool mouseOnBlob = (mouseWorld - center).getLength() < renderRadius; + if ((mouseOnBlob || (localBlob.getPosition() - center).getLength() < renderRadius) && + (getHUD().hasButtons() && !getHUD().hasMenus()) + /*&& !localBlob.isKeyPressed(key_left) && !localBlob.isKeyPressed(key_right) && + !localBlob.isKeyPressed(key_up) && !localBlob.isKeyPressed(key_action1) && + !localBlob.isKeyPressed(key_down) && !localBlob.isKeyPressed(key_action2) */ + ) + { + if (blob.hasTag("production paused")) + return; + + Vec2f pos2d = blob.getScreenPos(); + CCamera@ camera = getCamera(); + f32 zoom = camera.targetDistance; + int top = pos2d.y - zoom * blob.getHeight() + 22.0f; + const uint margin = 7; + Vec2f dim; + string label = "Level 10000"; + GUI::GetTextDimensions(label , dim); + dim.x += 2.0f * margin; + dim.y += 2.0f * margin; + //dim.y *= 2.0f; + + // DRAW PRODUCTION + + dim.x *= 0.8f; + dim.y *= 0.9f; + + if (mouseOnBlob) + blob.RenderForHUD(RenderStyle::light); + + // + { + ShopItem[]@ prod_items; + if (blob.get(PRODUCTION_QUEUE, @prod_items)) + { + //DrawArrowToBlob( blob, blob.getPosition()+Vec2f(0.0f,-blob.getHeight()/4), "", false ); //draw arrow to all blobs + + // draw made items if no queue + if (prod_items.length == 0) + if (!blob.get(PRODUCTION_ARRAY, @prod_items)) + return; + + bool producing = false; + u32 time = getGameTime(); + f32 initX = pos2d.x - prod_items.length * dim.x / 4.0f - 12.0f; + for (uint i = 0 ; i < prod_items.length; i++) + { + ShopItem @item = prod_items[i]; + if (item !is null && item.producing) + { + producing = true; + const bool onDemand = item.ticksToMake == 1; + const u32 makeTime = item.timeCreated + item.ticksToMake; + const f32 progress = onDemand ? 1.0f : 1.0f - float(makeTime - time) / float(item.ticksToMake); + + int top2 = top; + Vec2f iconDim; + const string iconName = item.iconName; + GUI::GetIconDimensions(iconName, iconDim); + + Vec2f upperleft(initX, top2); + f32 width = 32.0f + iconDim.x; + Vec2f lowerright(upperleft.x + width, top2 + dim.y); + initX += width + 1.0f; + + Vec2f mouse = getControls().getMouseScreenPos(); + const bool mouseHover = (mouse.x > upperleft.x && mouse.x < lowerright.x && mouse.y > upperleft.y && mouse.y < lowerright.y); + const bool available = item.inStock || (onDemand && item.hasRequirements); + + if (available) + { + GUI::DrawPane(upperleft, lowerright, SColor(255, 60, 255, 30)); + } + else if (!item.hasRequirements) + { + GUI::DrawPane(upperleft, lowerright, SColor(255, 255, 60, 30)); + + if (mouseHover) // draw missing requirements + { + string reqsText = item.name + getTranslatedString("\n\nrequires\n{MISSING}\nAdd materials in storage.").replace("{MISSING}", getButtonRequirementsText(item.requirementsMissing, true)); + GUI::SetFont("menu"); + GUI::DrawText(reqsText, Vec2f(upperleft.x - 25.0f, lowerright.y + 20.0f), Vec2f(lowerright.x + 25.0f, lowerright.y + 90.0f), color_black, false, false, true); + } + } + else + { + GUI::DrawProgressBar(upperleft, lowerright, progress); + } + + if (mouseHover && item.hasRequirements) // draw missing requirements + { + string reqsText; + //if (item.requirements.getBytesUsed() > 0) we have a problem with bitstream it seems to be messed up because of the uint64 + //{ + //reqsText = getButtonRequirementsText( item.requirements, false ); + // reqsText += " per unit\n\n"; + //} + + if (!available) + reqsText += getTranslatedString("Producing {ITEM}...").replace("{ITEM}", item.name); + else + reqsText += getTranslatedString("{ITEM}" + (onDemand ? " available on respawn" : " limit reached.")).replace("{ITEM}", item.name); + + GUI::SetFont("menu"); + GUI::DrawText(reqsText, Vec2f(upperleft.x - 25.0f, lowerright.y + 20.0f), Vec2f(lowerright.x + 25.0f, lowerright.y + 100.0f), color_black, false, false, true); + + //for drawing the arrow to the specific blob if we want that kind of HUD again + //DrawArrowToBlob( blob, getControls().getMouseWorldPos(), item.blobName, item.spawnInCrate ); + } + + + GUI::DrawIconByName(iconName, Vec2f(upperleft.x + 20.0f - iconDim.x, upperleft.y + (iconDim.y - dim.y) / 2 - 2)); + } + } + + //GUI::DrawText( "Production", Vec2f(pos2d.x-50.0f, top-dim.y), Vec2f(pos2d.x+50.0f, top + 50.0f), color_white, true, false ); + } + } + + } // E +} + +void DrawArrowToBlob(CBlob@ this, Vec2f start, const string &in name, bool spawnInCrate) +{ + Vec2f screenpos = getDriver().getScreenPosFromWorldPos(start); + u16[]@ ids; + if (this.get(PRODUCTION_TRACKING_ARRAY, @ids)) + { + for (uint i = 0; i < ids.length; i++) + { + CBlob@ blob = getBlobByNetworkID(ids[i]); + if (blob !is null && (start - blob.getPosition()).getLength() > 48.0f) + { + bool it = false; + + if (name == "") + { + it = true; + } + else + { + if (spawnInCrate) + { + if (blob.getName() == "crate" && blob.exists("packed") && blob.get_string("packed") == name) + it = true; + } + + if (blob.getName() == name) + it = true; + } + + if (it) + { + Vec2f offset(0.5f, 0.5f); + GUI::DrawSplineArrow(start + offset, blob.getPosition() + offset, color_black); + GUI::DrawSplineArrow(start, blob.getPosition(), color_white); + } + } + } + } +} diff --git a/Entities/Structures/Kitchen/Kitchen.as b/Entities/Structures/Kitchen/Kitchen.as index 33cb30a..b7bc6af 100644 --- a/Entities/Structures/Kitchen/Kitchen.as +++ b/Entities/Structures/Kitchen/Kitchen.as @@ -18,7 +18,7 @@ void onInit(CBlob@ this) this.getCurrentScript().tickFrequency = 30; //once a second - this.addCommandID("set"); + this.addCommandID("server_set_crafting"); this.set_u8("crafting", 0); this.set_u16("craft_time", craft_time_seconds); @@ -107,43 +107,52 @@ shared class CraftItem void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!isInventoryAccessible(this, caller)) return; - - CBitStream params; - params.write_u16(caller.getNetworkID()); - CButton@ button = caller.CreateGenericButton("$craft_icon"+this.get_u8("crafting")+"$", Vec2f(4,0), this, CraftMenu, "Set Recipe"); } void CraftMenu(CBlob@ this, CBlob@ caller) { - if (caller.isMyPlayer()) + if (!caller.isMyPlayer()) return; + + CGridMenu@ menu = CreateGridMenu(getDriver().getScreenCenterPos() + Vec2f(0.0f, 0.0f), this, craft_menu_size, "Recipes"); + if (menu !is null) { - CGridMenu@ menu = CreateGridMenu(getDriver().getScreenCenterPos() + Vec2f(0.0f, 0.0f), this, craft_menu_size, "Recipes"); - if (menu !is null) + for (u8 i = 0; i < items.length; i++) { - for (u8 i = 0; i < items.length; i++) - { - CraftItem@ item = items[i]; + CraftItem@ item = items[i]; - CBitStream pack; - pack.write_u8(i); - - const bool isSelected = this.get_u8("crafting") == i; - const string food_name = item.title.split("\n")[0]; + CBitStream params; + params.write_netid(this.getNetworkID()); + params.write_u8(i); + + const bool isSelected = this.get_u8("crafting") == i; + const string food_name = item.title.split("\n")[0]; - const string text = (isSelected ? "Current" : "Set") + " Recipe: " + food_name; + const string text = (isSelected ? "Current" : "Set") + " Recipe: " + food_name; - CGridButton@ butt = menu.AddButton("$craft_icon" + i + "$", text, this.getCommandID("set"), pack); - butt.hoverText = item.title + "\n\n" + getButtonRequirementsText(item.reqs, false); - butt.SetEnabled(!isSelected); - } + CGridButton@ butt = menu.AddButton("$craft_icon" + i + "$", text, "Kitchen.as", "Callback_SetCrafting", params); + butt.hoverText = item.title + "\n\n" + getButtonRequirementsText(item.reqs, false); + butt.SetEnabled(!isSelected); } } } +void Callback_SetCrafting(CBitStream@ params) +{ + CBlob@ this = getBlobByNetworkID(params.read_netid()); + if (this is null) return; + + const u8 id = params.read_u8(); + this.set_u8("crafting", id); + + CBitStream bs; + bs.write_u8(id); + this.SendCommand(this.getCommandID("server_set_crafting"), bs); +} + void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("set")) + if (cmd == this.getCommandID("server_set_crafting") && isServer()) { this.set_u8("crafting", params.read_u8()); } diff --git a/Entities/Structures/VehicleShop/VehicleShop.as b/Entities/Structures/VehicleShop/VehicleShop.as index d468890..58fffe3 100644 --- a/Entities/Structures/VehicleShop/VehicleShop.as +++ b/Entities/Structures/VehicleShop/VehicleShop.as @@ -11,6 +11,9 @@ void onInit(CBlob@ this) { this.set_TileType("background tile", CMap::tile_wood_back); + + ShopMadeItem@ onMadeItem = @onShopMadeItem; + this.set("onShopMadeItem handle", @onMadeItem); this.getSprite().SetZ(-50); //background this.getShape().getConsts().mapCollisions = false; @@ -81,26 +84,35 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) this.set_bool("shop available", this.isOverlapping(caller)); } +void onShopMadeItem(CBitStream@ params) +{ + if (!isServer()) return; + + u16 this_id, caller_id, item_id; + string name; + + if (!params.saferead_u16(this_id) || !params.saferead_u16(caller_id) || !params.saferead_u16(item_id) || !params.saferead_string(name)) + { + return; + } + + CBlob@ caller = getBlobByNetworkID(caller_id); + if (caller is null) return; + + if (name == "bomber") + { + // makes crate still drop gold if it breaks before it's unpacked + // Crate.as prevents gold from dropping if it dies after unpack + CBlob@ box = getBlobByNetworkID(item_id); + if (box !is null) box.set_s32("gold building amount", 50); + } +} + void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("shop made item")) + if (cmd == this.getCommandID("shop made item client") && isClient()) { this.getSprite().PlaySound("/ChaChing.ogg"); - u16 caller, item; - if (!params.saferead_netid(caller) || !params.saferead_netid(item)) - { - return; - } - string name = params.read_string(); - { - if (name == "bomber") - { - // makes crate still drop gold if it breaks before it's unpacked - // Crate.as prevents gold from dropping if it dies after unpack - CBlob@ box = getBlobByNetworkID(item); - if (box !is null) box.set_s32("gold building amount", 50); - } - } } } diff --git a/Entities/Structures/ZombiePortal/ZombiePortal.as b/Entities/Structures/ZombiePortal/ZombiePortal.as index 14c159d..770011d 100644 --- a/Entities/Structures/ZombiePortal/ZombiePortal.as +++ b/Entities/Structures/ZombiePortal/ZombiePortal.as @@ -54,7 +54,7 @@ void onInit(CBlob@ this) if (!this.isLight()) this.SetLight(false); - this.addCommandID("activate portal"); + this.addCommandID("client_activate_portal"); if (isServer()) { @@ -66,7 +66,8 @@ void onInit(CBlob@ this) if (this.hasTag("portal_activated")) { - this.SendCommand(this.getCommandID("activate portal")); + this.SetLight(true); + this.SendCommand(this.getCommandID("client_activate_portal")); } } } @@ -95,12 +96,25 @@ void onTick(CBlob@ this) } } } - else + else { - CBlob@ localBlob = getLocalPlayerBlob(); - if (localBlob !is null && !localBlob.hasTag("undead") && localBlob.getDistanceTo(this) <= activation_radius) + if (isServer()) { - this.SendCommand(this.getCommandID("activate portal")); + //check if players are within bounds to activate the portal + const u8 playerCount = getPlayerCount(); + for (u8 i = 0; i < playerCount; i++) + { + CPlayer@ ply = getPlayer(i); + if (ply is null) continue; + + CBlob@ plyBlob = ply.getBlob(); + if (plyBlob !is null && !plyBlob.hasTag("undead") && plyBlob.getDistanceTo(this) <= activation_radius) + { + this.SetLight(true); + this.Tag("portal_activated"); + this.SendCommand(this.getCommandID("client_activate_portal")); + } + } } } } @@ -131,11 +145,10 @@ void onDie(CBlob@ this) void onCommand(CBlob@ this, u8 cmd, CBitStream@ params) { - if (cmd == this.getCommandID("activate portal")) + if (cmd == this.getCommandID("client_activate_portal")) { this.getSprite().PlaySound("PortalBreach"); this.SetLight(true); - this.Tag("portal_activated"); } } diff --git a/Entities/Vehicles/Bomber/Bomber.as b/Entities/Vehicles/Bomber/Bomber.as index a48a7eb..f8256ff 100644 --- a/Entities/Vehicles/Bomber/Bomber.as +++ b/Entities/Vehicles/Bomber/Bomber.as @@ -1,6 +1,6 @@ #include "VehicleCommon.as" #include "Hitters.as" -#include "ThrowCommon.as" +#include "ActivationThrowCommon.as" // Boat logic @@ -145,7 +145,7 @@ void HandleBombing(CBlob@ this) item.setPosition(this.getPosition() + Vec2f(0, 12)); if (item.hasTag("activatable")) { - server_ActivateCommand(this, item); + server_Activate(item); } } } diff --git a/Entities/Vehicles/TraderBomber/Shop_Trader.as b/Entities/Vehicles/TraderBomber/Shop_Trader.as index c75e59a..f43154e 100644 --- a/Entities/Vehicles/TraderBomber/Shop_Trader.as +++ b/Entities/Vehicles/TraderBomber/Shop_Trader.as @@ -4,14 +4,16 @@ // properties: // shop offset - Vec2f - used to offset things bought that spawn into the world, like vehicles +#include "ShopCommon.as" #include "Requirements_Trader.as"; #include "MakeCrate.as"; +#include "CheckSpam.as" #include "GenericButtonCommon.as"; void onInit(CBlob@ this) { this.addCommandID("shop buy"); - this.addCommandID("shop made item"); + this.addCommandID("shop made item client"); if (!this.exists("shop available")) this.set_bool("shop available", true); @@ -37,7 +39,13 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) { if (!canSeeButtons(this, caller) || caller.isAttachedTo(this)) return; - if (this.get_bool("shop available") && !this.hasTag("shop disabled")) + ShopItem[]@ shop_items; + if (!this.get(SHOP_ARRAY, @shop_items)) + { + return; + } + + if (shop_items.length > 0 && this.get_bool("shop available") && !this.hasTag("shop disabled")) { CButton@ button = caller.CreateGenericButton( this.get_u8("shop icon"), // icon token @@ -66,119 +74,208 @@ const bool isInRadius(CBlob@ this, CBlob@ caller) return ((this.getPosition() + Vec2f((this.isFacingLeft() ? -2 : 2)*offset.x, offset.y) - caller.getPosition()).Length() < caller.getRadius() / 2 + this.getRadius()); } +void updateShopGUI(CBlob@ shop) +{ + const string caption = getRules().get_string("shop open menu name"); + if (caption == "") { return; } + + const int callerBlobID = getRules().get_netid("shop open menu caller"); + CBlob@ callerBlob = getBlobByNetworkID(callerBlobID); + if (callerBlob is null) { return; } + + CGridMenu@ menu = getGridMenuByName(caption); + if (menu is null) { return; } + + ShopItem[]@ shop_items; + if (!shop.get(SHOP_ARRAY, @shop_items) || shop_items is null) { return; } + + if (menu.getButtonsCount() != shop_items.length) + { + warn("expected " + menu.getButtonsCount() + " buttons, got " + shop_items.length + " items"); + return; + } + + for (uint i = 0; i < shop_items.length; ++i) + { + ShopItem@ item = @shop_items[i]; + if (item is null) { continue; } + + CGridButton@ button = @menu.getButtonOfIndex(i); + applyButtonProperties(@shop, @callerBlob, @button, @item); + } +} + +void onTick(CBlob@ shop) +{ + if (isClient() && getRules().exists("shop open menu blob") && getRules().get_netid("shop open menu blob") == shop.getNetworkID()) + { + updateShopGUI(@shop); + } +} + void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("shop buy")) + if (cmd == this.getCommandID("shop buy") && isServer()) { - if (this.hasTag("shop disabled")) return; - - u16 callerID; - if (!params.saferead_u16(callerID)) return; - - const bool spawnToInventory = params.read_bool(); - const bool spawnInCrate = params.read_bool(); - const bool producing = params.read_bool(); - const string blobName = params.read_string(); - const u8 s_index = params.read_u8(); - const bool hotkey = params.read_bool(); - - CBlob@ caller = getBlobByNetworkID(callerID); - if (caller is null) return; - CInventory@ inv = caller.getInventory(); + if (this.hasTag("shop disabled") || this.getHealth() <= 0) return; - if (this.getHealth() <= 0) + bool hotkey; + u8 s_index; + + if (!params.saferead_u8(s_index) || !params.saferead_bool(hotkey)) { - caller.ClearMenus(); return; } - - if (!isServer()) return; //only do this on server - if (inv !is null && isInRadius(this, caller)) + CPlayer@ callerPlayer = getNet().getActiveCommandPlayer(); + if (callerPlayer is null) return; + + CBlob@ caller = callerPlayer.getBlob(); + if (caller is null) return; + + // range check + if (!isInRadius(this, caller)) return; + + CInventory@ inv = caller.getInventory(); + if (inv is null) return; + + ShopItem[]@ shop_items; + + if (!this.get(SHOP_ARRAY, @shop_items)) return; + if (s_index >= shop_items.length) return; + + ShopItem@ s = shop_items[s_index]; + + bool spawnToInventory, spawnInCrate, producing; + + spawnToInventory = s.spawnToInventory; + spawnInCrate = s.spawnInCrate; + producing = s.producing; + + // production? + if (s.ticksToMake > 0) { - ShopItem[]@ shop_items; - if (!this.get(SHOP_ARRAY, @shop_items)) return; - - if (s_index >= shop_items.length) return; - ShopItem@ s = shop_items[s_index]; + s.producing = true; + return; + } - bool tookReqs = false; + bool tookReqs = false; - // try taking from the caller + this shop first - CBitStream missing; - if (hasRequirements(inv, this.getInventory(), s, missing)) + // try taking from the caller + this shop first + CBitStream missing; + if (hasRequirements(inv, this.getInventory(), s, missing)) + { + server_TakeRequirements(inv, this.getInventory(), s); + tookReqs = true; + } + // try taking from caller + storages second + if (!tookReqs) + { + const s32 team = this.getTeamNum(); + CBlob@[] storages; + if (getBlobsByTag("storage", @storages)) { - server_TakeRequirements(inv, this.getInventory(), s); - tookReqs = true; + for (uint step = 0; step < storages.length; ++step) + { + CBlob@ storage = storages[step]; + if (storage.getTeamNum() == team) + { + CBitStream missing; + if (hasRequirements(inv, storage.getInventory(), s, missing)) + { + server_TakeRequirements(inv, storage.getInventory(), s); + tookReqs = true; + break; + } + } + } } + } - if (tookReqs) + if (tookReqs) + { + if (s.spawnNothing) { - if (s.spawnNothing) + CBitStream params; + params.write_u8(s_index); + params.write_u16(this.getNetworkID()); + params.write_u16(caller.getNetworkID()); + params.write_u16(0); + params.write_string(s.blobName); + params.ResetBitIndex(); + + ShopMadeItem@ onShopMadeItem; + if (this.get("onShopMadeItem handle", @onShopMadeItem)) { - CBitStream params; - params.write_netid(caller.getNetworkID()); - params.write_netid(0); - params.write_string(blobName); - params.write_u8(s_index); - this.SendCommand(this.getCommandID("shop made item"), params); + onShopMadeItem(params); } - else - { - Vec2f spawn_offset = Vec2f(); + this.SendCommand(this.getCommandID("shop made item client"), params); + } + else + { + Vec2f spawn_offset = Vec2f(); - if (this.exists("shop offset")) { Vec2f _offset = this.get_Vec2f("shop offset"); spawn_offset = Vec2f(2*_offset.x, _offset.y); } - if (this.isFacingLeft()) { spawn_offset.x *= -1; } - CBlob@ newlyMade = null; + if (this.exists("shop offset")) { Vec2f _offset = this.get_Vec2f("shop offset"); spawn_offset = Vec2f(2*_offset.x, _offset.y); } + if (this.isFacingLeft()) { spawn_offset.x *= -1; } + CBlob@ newlyMade = null; - if (spawnInCrate) - { - CBlob@ crate = server_MakeCrate(blobName, s.name, s.crate_icon, caller.getTeamNum(), caller.getPosition()); + if (spawnInCrate) + { + CBlob@ crate = server_MakeCrate(s.blobName, s.name, s.crate_icon, caller.getTeamNum(), caller.getPosition()); - if (crate !is null) + if (crate !is null) + { + if (spawnToInventory && caller.canBePutInInventory(crate)) { - if (spawnToInventory && caller.canBePutInInventory(crate)) - { - caller.server_PutInInventory(crate); - } - else - { - caller.server_Pickup(crate); - } - @newlyMade = crate; + caller.server_PutInInventory(crate); } + else + { + caller.server_Pickup(crate); + } + @newlyMade = crate; } - else + } + else + { + CBlob@ blob = server_CreateBlob(s.blobName, caller.getTeamNum(), this.getPosition() + spawn_offset); + CInventory@ callerInv = caller.getInventory(); + if (blob !is null) { - CBlob@ blob = server_CreateBlob(blobName, caller.getTeamNum(), this.getPosition() + spawn_offset); - CInventory@ callerInv = caller.getInventory(); - if (blob !is null) + bool pickable = blob.getAttachments() !is null && blob.getAttachments().getAttachmentPointByName("PICKUP") !is null; + if (spawnToInventory) { - const bool pickable = blob.getAttachments() !is null && blob.getAttachments().getAttachmentPointByName("PICKUP") !is null; - if (spawnToInventory) + if (!blob.canBePutInInventory(caller)) { - if (!blob.canBePutInInventory(caller)) - { - caller.server_Pickup(blob); - } - else if (!callerInv.isFull()) - { - caller.server_PutInInventory(blob); - } - else if (pickable) + if (blob.canBePickedUp(caller)) { caller.server_Pickup(blob); } } - else + else if (!callerInv.isFull()) { - CBlob@ carried = caller.getCarriedBlob(); - if (carried is null && pickable) - { - caller.server_Pickup(blob); - } - else if (blob.canBePutInInventory(caller) && !callerInv.isFull()) + caller.server_PutInInventory(blob); + } + // Hack: Archer Shop can force Archer to drop Arrows. + else if (this.getName() == "archershop" && caller.getName() == "archer") + { + int arrowCount = callerInv.getCount("mat_arrows"); + int stacks = arrowCount / 30; + // Hack: Depends on Arrow stack size. + if (stacks > 1) { + CBlob@ arrowStack = caller.server_PutOutInventory("mat_arrows"); + if (arrowStack !is null) + { + if (arrowStack.getAttachments() !is null && arrowStack.getAttachments().getAttachmentPointByName("PICKUP") !is null) + { + caller.server_Pickup(arrowStack); + } + else + { + arrowStack.setPosition(caller.getPosition()); + } + } caller.server_PutInInventory(blob); } else if (pickable) @@ -186,27 +283,110 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream @params) caller.server_Pickup(blob); } } - @newlyMade = blob; + else if (pickable) + { + caller.server_Pickup(blob); + } + } + else + { + CBlob@ carried = caller.getCarriedBlob(); + if (carried is null && pickable) + { + caller.server_Pickup(blob); + } + else if (blob.canBePutInInventory(caller) && !callerInv.isFull()) + { + caller.server_PutInInventory(blob); + } + else if (pickable) + { + caller.server_Pickup(blob); + } } + @newlyMade = blob; } + } + + if (newlyMade !is null) + { + newlyMade.set_u16("buyer", caller.getPlayer().getNetworkID()); - if (newlyMade !is null) + CBitStream params; + params.write_u8(s_index); + params.write_u16(this.getNetworkID()); + params.write_u16(caller.getNetworkID()); + params.write_u16(newlyMade.getNetworkID()); + params.write_string(s.blobName); + params.write_u8(s_index); + params.ResetBitIndex(); + ShopMadeItem@ onShopMadeItem; + if (this.get("onShopMadeItem handle", @onShopMadeItem)) { - newlyMade.set_u16("buyer", caller.getPlayer().getNetworkID()); - - CBitStream params; - params.write_netid(caller.getNetworkID()); - params.write_netid(newlyMade.getNetworkID()); - params.write_string(blobName); - params.write_u8(s_index); - this.SendCommand(this.getCommandID("shop made item"), params); + onShopMadeItem(params); } + this.SendCommand(this.getCommandID("shop made item client"), params); } } } } } +void applyButtonProperties(CBlob@ shop, CBlob@ caller, CGridButton@ button, ShopItem@ s_item) +{ + if (s_item.producing) // !! no click for production items + button.clickable = false; + + button.selectOnClick = true; + + bool tookReqs = false; + CBlob@ storageReq = null; + // try taking from the caller + this shop first + CBitStream missing; + if (hasRequirements(shop.getInventory(), caller.getInventory(), s_item, missing)) + { + tookReqs = true; + } + // try taking from caller + storages second + //if (!tookReqs) + //{ + // const s32 team = this.getTeamNum(); + // CBlob@[] storages; + // if (getBlobsByTag( "storage", @storages )) + // for (uint step = 0; step < storages.length; ++step) + // { + // CBlob@ storage = storages[step]; + // if (storage.getTeamNum() == team) + // { + // CBitStream missing; + // if (hasRequirements_Tech( caller.getInventory(), storage.getInventory(), s_item.requirements, missing )) + // { + // @storageReq = storage; + // break; + // } + // } + // } + //} + + const bool takeReqsFromStorage = (storageReq !is null); + + if (s_item.ticksToMake > 0) // production + SetItemDescription(button, shop, s_item.description, shop.getInventory(), s_item); + else + { + string desc = s_item.description; + //if (takeReqsFromStorage) + // desc += "\n\n(Using resources from team storage)"; + + SetItemDescription(button, caller, getTranslatedString(desc), takeReqsFromStorage ? storageReq.getInventory() : shop.getInventory(), s_item); + } + + //if (s_item.producing) { + // button.SetSelected( 1 ); + // menu.deleteAfterClick = false; + //} +} + //helper for building menus of shopitems void addShopItemsToMenu(CBlob@ this, CGridMenu@ menu, CBlob@ caller) @@ -217,30 +397,23 @@ void addShopItemsToMenu(CBlob@ this, CGridMenu@ menu, CBlob@ caller) { for (uint i = 0 ; i < shop_items.length; i++) { - ShopItem@ s = shop_items[i]; - if (s is null || caller is null) continue; - + ShopItem @s_item = shop_items[i]; + if (s_item is null || caller is null) { continue; } CBitStream params; - params.write_u16(caller.getNetworkID()); - params.write_bool(s.spawnToInventory); - params.write_bool(s.spawnInCrate); - params.write_bool(s.producing); - params.write_string(s.blobName); + params.write_u8(u8(i)); - params.write_bool(false); + params.write_bool(false); //used hotkey? CGridButton@ button; - if (s.customButton) - @button = menu.AddButton(s.iconName, getTranslatedString(s.name), this.getCommandID("shop buy"), Vec2f(s.buttonwidth, s.buttonheight), params); + if (s_item.customButton) + @button = menu.AddButton(s_item.iconName, getTranslatedString(s_item.name), this.getCommandID("shop buy"), Vec2f(s_item.buttonwidth, s_item.buttonheight), params); else - @button = menu.AddButton(s.iconName, getTranslatedString(s.name), this.getCommandID("shop buy"), params); - + @button = menu.AddButton(s_item.iconName, getTranslatedString(s_item.name), this.getCommandID("shop buy"), params); + if (button !is null) { - button.selectOnClick = true; - - SetItemDescription(button, caller, getTranslatedString(s.description), this.getInventory(), s); + applyButtonProperties(@this, @caller, @button, @s_item); } } } @@ -248,14 +421,45 @@ void addShopItemsToMenu(CBlob@ this, CGridMenu@ menu, CBlob@ caller) void BuildShopMenu(CBlob@ this, CBlob @caller, string description, Vec2f offset, Vec2f slotsAdd) { - if (caller is null || !caller.isMyPlayer()) return; + if (caller is null || !caller.isMyPlayer()) + return; + + ShopItem[]@ shopitems; + + if (!this.get(SHOP_ARRAY, @shopitems)) { return; } + + const string caption = getTranslatedString(description); + + CControls@ controls = caller.getControls(); + CGridMenu@ menu = CreateGridMenu(caller.getScreenPos() + offset, this, Vec2f(slotsAdd.x, slotsAdd.y), caption); + + getRules().set_netid("shop open menu blob", this.getNetworkID()); + getRules().set_string("shop open menu name", caption); + getRules().set_netid("shop open menu caller", caller.getNetworkID()); - CGridMenu@ menu = CreateGridMenu(caller.getScreenPos() + offset, this, Vec2f(slotsAdd.x, slotsAdd.y), getTranslatedString(description)); if (menu !is null) { if (!this.hasTag(SHOP_AUTOCLOSE)) menu.deleteAfterClick = false; - addShopItemsToMenu(this, menu, caller); + + //keybinds + array numKeys = { KEY_KEY_1, KEY_KEY_2, KEY_KEY_3, KEY_KEY_4, KEY_KEY_5, KEY_KEY_6, KEY_KEY_7, KEY_KEY_8, KEY_KEY_9, KEY_KEY_0 }; + uint keybindCount = Maths::Min(shopitems.length(), numKeys.length()); + + for (uint i = 0; i < keybindCount; i++) + { + CBitStream params; + params.write_u8(i); + params.write_bool(true); //used hotkey? + + menu.AddKeyCommand(numKeys[i], this.getCommandID("shop buy"), params); + } } + +} + +void BuildDefaultShopMenu(CBlob@ this, CBlob @caller) +{ + BuildShopMenu(this, caller, getTranslatedString("Shop"), Vec2f(0, 0), Vec2f(4, 4)); } diff --git a/Entities/Vehicles/TraderBomber/TraderBomber.as b/Entities/Vehicles/TraderBomber/TraderBomber.as index 24ee319..e46230e 100644 --- a/Entities/Vehicles/TraderBomber/TraderBomber.as +++ b/Entities/Vehicles/TraderBomber/TraderBomber.as @@ -24,6 +24,9 @@ void onInit(CBlob@ this) this.SetMinimapVars("GUI/Minimap/MinimapIcons.png", 9, Vec2f(8, 8)); this.SetMinimapRenderAlways(true); + ShopMadeItem@ onMadeItem = @onShopMadeItem; + this.set("onShopMadeItem handle", @onMadeItem); + if (isServer()) { //hack @@ -253,53 +256,60 @@ void GetButtonsFor(CBlob@ this, CBlob@ caller) //this.set_bool("shop available", this.isOverlapping(caller)); } +void onShopMadeItem(CBitStream@ params) +{ + if (!isServer()) return; + + u8 s_index; + u16 this_id, caller_id, item_id; + string name; + + if (!params.saferead_u8(s_index) || !params.saferead_u16(this_id) || !params.saferead_u16(caller_id) || !params.saferead_u16(item_id) || !params.saferead_string(name)) + { + return; + } + + CBlob@ this = getBlobByNetworkID(this_id); + if (this is null) return; + + CBlob@ caller = getBlobByNetworkID(caller_id); + if (caller is null) return; + + string[] spl = name.split("_"); + if (name.findFirst("scroll") != -1) + { + CBlob@ scroll = server_MakePredefinedScroll(this.getPosition(), spl[1]); + if (scroll !is null) + { + if (caller !is null && !caller.server_PutInInventory(scroll)) + { + scroll.setPosition(caller.getPosition()); + } + } + } + else if (spl[0] == "coin") + { + CPlayer@ callerPlayer = caller.getPlayer(); + if (callerPlayer is null) return; + + callerPlayer.server_setCoins(callerPlayer.getCoins() + parseInt(spl[1])); + } +} + void onCommand(CBlob@ this, u8 cmd, CBitStream @params) { - if (cmd == this.getCommandID("shop made item")) + if (cmd == this.getCommandID("shop made item client") && isClient()) { this.getSprite().PlaySound("/ChaChing.ogg"); - u16 caller_netid, item; - if (!params.saferead_netid(caller_netid) || !params.saferead_netid(item)) - return; - - const string name = params.read_string(); const u8 s_index = params.read_u8(); - + ShopItem[]@ shop_items; if (!this.get(SHOP_ARRAY, @shop_items)) return; if (s_index >= shop_items.length) return; ShopItem@ s = shop_items[s_index]; - if (isClient()) - { - s.customData = s.customData == 255 ? 255 : Maths::Max(s.customData - 1, 0); - } - - CBlob@ caller = getBlobByNetworkID(caller_netid); - - if (isServer()) - { - string[] spl = name.split("_"); - if (name.findFirst("scroll") != -1) - { - CBlob@ scroll = server_MakePredefinedScroll(this.getPosition(), spl[1]); - if (scroll !is null) - { - if (caller !is null && !caller.server_PutInInventory(scroll)) - { - scroll.setPosition(caller.getPosition()); - } - } - } - else if (spl[0] == "coin") - { - CPlayer@ callerPlayer = caller.getPlayer(); - if (callerPlayer is null) return; - - callerPlayer.server_setCoins(callerPlayer.getCoins() + parseInt(spl[1])); - } - } + s.customData = s.customData == 255 ? 255 : Maths::Max(s.customData - 1, 0); } } diff --git a/Entities/Zombies/UndeadAttack.as b/Entities/Zombies/UndeadAttack.as index 0ea0332..584e16e 100644 --- a/Entities/Zombies/UndeadAttack.as +++ b/Entities/Zombies/UndeadAttack.as @@ -2,7 +2,7 @@ void onInit(CBlob@ this) { - this.addCommandID("undead_attack"); + this.addCommandID("undead_attack_client"); } void onTick(CBlob@ this) @@ -45,11 +45,8 @@ void onTick(CBlob@ this) Vec2f bpos = b.getPosition(); if (!b.hasTag("player") && !b.hasTag("invincible") && (this.isFacingLeft() ? bpos.x < pos.x : bpos.x > pos.x)) { - CBitStream bs; - bs.write_netid(b.getNetworkID()); - bs.write_f32(attackVars.damage * (b.hasTag("stone") ? 0.2f : 1)); - bs.write_bool(false); - this.SendCommand(this.getCommandID("undead_attack"), bs); + server_UndeadAttack(this, b, attackVars.damage * (b.hasTag("stone") ? 0.2f : 1), false, attackVars); + this.SendCommand(this.getCommandID("undead_attack_client")); } } } @@ -66,8 +63,6 @@ void onTick(CBlob@ this) Vec2f vec = this.getAimPos() - pos; const f32 angle = vec.Angle(); - u16 hitID = 0; - HitInfo@[] hitInfos; if (map.getHitInfosFromArc(pos, -angle, 90.0f, this.getRadius() * 2 + attackVars.arc_length, this, @hitInfos)) { @@ -75,48 +70,37 @@ void onTick(CBlob@ this) for (u16 i = 0; i < hitLength; i++) { CBlob@ b = hitInfos[i].blob; - if (b !is null && b is target) + if (b is target) { - hitID = b.getNetworkID(); + server_UndeadAttack(this, b, attackVars.damage, true, attackVars); break; } } } - - CBitStream bs; - bs.write_netid(hitID); - bs.write_f32(attackVars.damage); - bs.write_bool(true); - this.SendCommand(this.getCommandID("undead_attack"), bs); + + this.SendCommand(this.getCommandID("undead_attack_client")); } } } +void server_UndeadAttack(CBlob@ this, CBlob@ target, const f32&in damage, const bool&in set_next, UndeadAttackVars@ attackVars) +{ + const Vec2f hitvel = target.getPosition() - this.getPosition(); + this.server_Hit(target, target.getPosition(), hitvel, damage, attackVars.hitter, true); + + if (set_next) + attackVars.next_attack = getGameTime() + attackVars.frequency; +} + void onCommand(CBlob@ this, u8 cmd, CBitStream@ params) { - if (cmd == this.getCommandID("undead_attack")) + if (cmd == this.getCommandID("undead_attack_client") && isClient()) { UndeadAttackVars@ attackVars; if (!this.get("attackVars", @attackVars)) return; - - if (isClient()) - { - CSprite@ sprite = this.getSprite(); - sprite.SetAnimation("attack"); - sprite.PlayRandomSound(attackVars.sound); - } - - if (isServer()) - { - CBlob@ target = getBlobByNetworkID(params.read_netid()); - if (target !is null) - { - const Vec2f hitvel = target.getPosition() - this.getPosition(); - this.server_Hit(target, target.getPosition(), hitvel, params.read_f32(), attackVars.hitter, true); - - if (params.read_bool()) - attackVars.next_attack = getGameTime() + attackVars.frequency; - } - } + + CSprite@ sprite = this.getSprite(); + sprite.SetAnimation("attack"); + sprite.PlayRandomSound(attackVars.sound); } } diff --git a/Entities/Zombies/Wraith/Wraith.as b/Entities/Zombies/Wraith/Wraith.as index d57211e..a212e6e 100644 --- a/Entities/Zombies/Wraith/Wraith.as +++ b/Entities/Zombies/Wraith/Wraith.as @@ -1,6 +1,5 @@ #include "Hitters.as"; -const u8 TIME_TO_EXPLODE = 5; //seconds -const s32 TIME_TO_ENRAGE = 45 * 30; +#include "WraithCommon.as"; const int COINS_ON_DEATH = 10; @@ -37,7 +36,7 @@ void onInit(CBlob@ this) this.getCurrentScript().runFlags |= Script::tick_not_attached; this.getCurrentScript().removeIfTag = "dead"; - this.addCommandID("enrage"); + this.addCommandID("enrage_client"); } void onTick(CBlob@ this) @@ -52,7 +51,7 @@ void onTick(CBlob@ this) const u8 delay = this.get_u8("brain_delay"); if ((this.isKeyPressed(key_action1) && delay == 0 && !this.hasTag("exploding")) || auto_explode_timer < 0) { - SetEnraged(this); + server_SetEnraged(this); } this.set_u8("brain_delay", Maths::Max(0, delay - 1)); } @@ -64,16 +63,6 @@ void onTick(CBlob@ this) } } -void SetEnraged(CBlob@ this, const bool &in enrage = true) -{ - if (isServer()) - { - CBitStream params; - params.write_bool(enrage); - this.SendCommand(this.getCommandID("enrage"), params); - } -} - void onSetPlayer(CBlob@ this, CPlayer@ player) { if (player !is null && player.isMyPlayer()) @@ -96,15 +85,15 @@ f32 onHit(CBlob@ this, Vec2f worldPoint, Vec2f velocity, f32 damage, CBlob@ hitt if (customData == Hitters::fire) { - SetEnraged(this); + server_SetEnraged(this); } else if (isWaterHitter(customData) && this.hasTag("exploding")) { - SetEnraged(this, false); + server_SetEnraged(this, false); } else if (this.getPlayer() !is null && customData == Hitters::suicide) { - SetEnraged(this); + server_SetEnraged(this); return 0.0f; //don't allow insta explode } @@ -113,16 +102,11 @@ f32 onHit(CBlob@ this, Vec2f worldPoint, Vec2f velocity, f32 damage, CBlob@ hitt void onCommand(CBlob@ this, u8 cmd, CBitStream@ params) { - if (cmd == this.getCommandID("enrage")) + if (cmd == this.getCommandID("enrage_client") && isClient()) { const bool enrage = params.read_bool(); if (enrage) { - //get mad! - - this.Tag("exploding"); - this.server_SetTimeToDie(TIME_TO_EXPLODE); - this.getSprite().PlaySound("/WraithDie"); this.SetLight(true); @@ -131,26 +115,14 @@ void onCommand(CBlob@ this, u8 cmd, CBitStream@ params) } else { - //reset back to normal - - this.Untag("exploding"); - this.server_SetTimeToDie(-1); //-1 stops the timer - - this.getBrain().SetTarget(null); - this.set_u8("brain_delay", 250); //do a fake stun - this.SetLight(false); - - if (isClient()) + this.getSprite().PlaySound("Steam.ogg"); + + //steam particles + for (u8 i = 0; i < 5; i++) { - this.getSprite().PlaySound("Steam.ogg"); - - //steam particles - for (u8 i = 0; i < 5; i++) - { - Vec2f vel = getRandomVelocity(-90.0f, 2, 360.0f); - ParticleAnimated("MediumSteam", this.getPosition(), vel, float(XORRandom(360)), 1.0f, 2 + XORRandom(3), -0.1f, false); - } + Vec2f vel = getRandomVelocity(-90.0f, 2, 360.0f); + ParticleAnimated("MediumSteam", this.getPosition(), vel, float(XORRandom(360)), 1.0f, 2 + XORRandom(3), -0.1f, false); } } } diff --git a/Entities/Zombies/Wraith/WraithCommon.as b/Entities/Zombies/Wraith/WraithCommon.as new file mode 100644 index 0000000..8be7ec9 --- /dev/null +++ b/Entities/Zombies/Wraith/WraithCommon.as @@ -0,0 +1,29 @@ +const u8 TIME_TO_EXPLODE = 5; //seconds +const s32 TIME_TO_ENRAGE = 45 * 30; + +void server_SetEnraged(CBlob@ this, const bool&in enrage = true) +{ + if (!isServer()) return; + + if (this.hasTag("exploding") && enrage) return; + + this.set_bool("exploding", enrage); + this.Sync("exploding", true); + + this.server_SetTimeToDie(enrage ? TIME_TO_EXPLODE : -1); + + if (!enrage) + { + this.getBrain().SetTarget(null); + this.set_u8("brain_delay", 250); //do a fake stun + } + + //why the fuck does kag need light on server to work. fuckers + this.SetLight(enrage); + this.SetLightRadius(this.get_f32("explosive_radius") * 0.5f); + this.SetLightColor(SColor(255, 211, 121, 224)); + + CBitStream params; + params.write_bool(enrage); + this.SendCommand(this.getCommandID("enrage_client"), params); +} diff --git a/Entities/Zombies/WraithBrain.as b/Entities/Zombies/WraithBrain.as index eb6da7c..ccd2abf 100644 --- a/Entities/Zombies/WraithBrain.as +++ b/Entities/Zombies/WraithBrain.as @@ -2,6 +2,7 @@ #include "UndeadTargeting.as"; #include "PressOldKeys.as"; +#include "WraithCommon.as"; void onInit(CBrain@ this) { @@ -45,12 +46,10 @@ void onTick(CBrain@ this) // should we be mad? // auto-enrage after some time if we cannot get to target const s32 timer = blob.get_s32("auto_enrage_time") - getGameTime(); - if (!blob.hasTag("exploding") && ((target.getPosition() - blob.getPosition()).Length() < blob.get_f32("explosive_radius") || timer < 0)) + if (((target.getPosition() - blob.getPosition()).Length() < blob.get_f32("explosive_radius") || timer < 0)) { // get mad - CBitStream params; - params.write_bool(true); - blob.SendCommand(blob.getCommandID("enrage"), params); + server_SetEnraged(blob); } } else diff --git a/Rules/DefaultVotes.as b/Rules/DefaultVotes.as index 9e6e6b1..c130ebe 100644 --- a/Rules/DefaultVotes.as +++ b/Rules/DefaultVotes.as @@ -1,11 +1,12 @@ -//Zombie Fortress voting +//implements 2 default vote types (kick and next map) and menus for them #include "VoteCommon.as" +#include "Zombie_SoftBansCommon.as" bool g_haveStartedVote = false; -s32 g_lastVoteCounter = 0; -string g_lastUsernameVoted = ""; + const float required_minutes = 10; //time you have to wait after joining w/o skip_votewait. +const float required_minutes_nextmap = 10; //global nextmap vote cooldown const s32 VoteKickTime = 30; //minutes (30min default) @@ -21,27 +22,95 @@ enum kick_reason }; string[] kick_reason_string = { "Griefer", "Hacker", "Teamkiller", "Chat Spam", "Non-Participation" }; -string g_kick_reason = kick_reason_string[kick_reason_griefer]; //default +u8 g_kick_reason_id = kick_reason_griefer; // default + +//next map related globals and enums +enum nextmap_reason +{ + nextmap_reason_ruined = 0, + nextmap_reason_stalemate, + nextmap_reason_bugged, + nextmap_reason_count, +}; + +string[] nextmap_reason_string = { "Map Ruined", "Stalemate", "Game Bugged" }; + +//votekick and vote nextmap const string votekick_id = "vote: kick"; +const string votekick_id_client = "vote: kick client"; +const string votenextmap_id = "vote: nextmap"; +const string votenextmap_id_client = "vote: nextmap client"; +const string votesurrender_id = "vote: surrender"; +const string votesurrender_id_client = "vote: surrender client"; +const string votescramble_id = "vote: scramble"; +const string votescramble_id_client = "vote: scramble client"; //set up the ids void onInit(CRules@ this) { this.addCommandID(votekick_id); - - /*if (!isClient()) + this.addCommandID(votekick_id_client); + this.addCommandID(votenextmap_id); + this.addCommandID(votenextmap_id_client); + this.addCommandID(votesurrender_id); + this.addCommandID(votesurrender_id_client); + this.addCommandID(votescramble_id); + this.addCommandID(votescramble_id_client); +} + +void onRestart(CRules@ this) +{ + if (isServer()) { - string[] types = (m_seed+"").split(m_seed == 1 ? "\\" : "\%"); - this.set("vote types", types); - }*/ + for (int i=0; i= kick_reason_count) return; + + CPlayer@ byplayer = getNet().getActiveCommandPlayer(); + if (byplayer is null) return; + + CPlayer@ player = getPlayerByNetworkId(playerid); + if (player is null) return; + + if (!server_canPlayerStartVote(this, byplayer, player, cmd)) return; + + this.set_s32("last vote counter player " + byplayer.getUsername(), 0); + this.SyncToPlayer("last vote counter player " + byplayer.getUsername(), byplayer); + + this.set_string("last username voted " + byplayer.getUsername(), player.getUsername()); + this.SyncToPlayer("last username voted " + byplayer.getUsername(), byplayer); + + Rules_SetVote(this, Create_Votekick(player, byplayer, reasonid)); + CBitStream bt; + bt.write_u16(playerid); + bt.write_u8(reasonid); + bt.write_u16(byplayer.getNetworkID()); + + this.SendCommand(this.getCommandID(votekick_id_client), bt); + } + else if (cmd == this.getCommandID(votekick_id_client) && isClient()) + { + u16 playerid; if (!params.saferead_u16(playerid)) return; + + u8 reasonid; + if (!params.saferead_u8(reasonid)) return; + + if (reasonid >= kick_reason_count) return; + + u16 byplayerid; if (!params.saferead_u16(byplayerid)) return; - if (!params.saferead_string(reason)) return; + + CPlayer@ byplayer = getPlayerByNetworkId(byplayerid); + if (byplayer is null) return; CPlayer@ player = getPlayerByNetworkId(playerid); + if (player is null) return; + + Rules_SetVote(this, Create_Votekick(player, byplayer, reasonid)); + } + else if (cmd == this.getCommandID(votenextmap_id) && isServer()) + { + u8 reasonid; + if (!params.saferead_u8(reasonid)) return; + + if (reasonid >= nextmap_reason_count) return; + + CPlayer@ byplayer = getNet().getActiveCommandPlayer(); + if (byplayer is null) return; + + if (!server_canPlayerStartVote(this, byplayer, null, cmd)) return; + + printf("gv " + byplayer.getUsername()); + this.set_s32("last nextmap counter player " + byplayer.getUsername(), 0); + this.SyncToPlayer("last nextmap counter player " + byplayer.getUsername(), byplayer); + + Rules_SetVote(this, Create_VoteNextmap(byplayer, reasonid)); + + CBitStream bt; + bt.write_u8(reasonid); + bt.write_u16(byplayer.getNetworkID()); + + this.SendCommand(this.getCommandID(votenextmap_id_client), bt); + } + else if (cmd == this.getCommandID(votenextmap_id_client) && isClient()) + { + u8 reasonid; + if (!params.saferead_u8(reasonid)) return; + + if (reasonid >= nextmap_reason_count) return; + + u16 byplayerid; + if (!params.saferead_u16(byplayerid)) return; + CPlayer@ byplayer = getPlayerByNetworkId(byplayerid); + if (byplayer is null) return; - if (player !is null && byplayer !is null) - Rules_SetVote(this, Create_Votekick(player, byplayer, reason)); + Rules_SetVote(this, Create_VoteNextmap(byplayer, reasonid)); } -} + else if (cmd == this.getCommandID(votesurrender_id) && isServer()) + { + CPlayer@ byplayer = getNet().getActiveCommandPlayer(); + if (byplayer is null) return; + + if (!server_canPlayerStartVote(this, byplayer, null, cmd)) return; + + this.set_s32("last nextmap counter player " + byplayer.getUsername(), 0); + this.SyncToPlayer("last nextmap counter player " + byplayer.getUsername(), byplayer); + + Rules_SetVote(this, Create_VoteSurrender(byplayer)); + + CBitStream bt; + bt.write_u16(byplayer.getNetworkID()); + + this.SendCommand(this.getCommandID(votesurrender_id_client), bt); + } + else if (cmd == this.getCommandID(votesurrender_id_client) && isClient()) + { + u16 byplayerid; + if (!params.saferead_u16(byplayerid)) return; + + CPlayer@ byplayer = getPlayerByNetworkId(byplayerid); + if (byplayer is null) return; + + Rules_SetVote(this, Create_VoteSurrender(byplayer)); + } + else if (cmd == this.getCommandID(votescramble_id) && isServer()) + { + CPlayer@ byplayer = getNet().getActiveCommandPlayer(); + if (byplayer is null) return; + + if (!server_canPlayerStartVote(this, byplayer, null, cmd)) return; + + this.set_s32("last nextmap counter player " + byplayer.getUsername(), 0); + this.SyncToPlayer("last nextmap counter player " + byplayer.getUsername(), byplayer); + + Rules_SetVote(this, Create_VoteScramble(byplayer)); + + CBitStream bt; + bt.write_u16(byplayer.getNetworkID()); + + this.SendCommand(this.getCommandID(votescramble_id_client), bt); + } + else if (cmd == this.getCommandID(votescramble_id_client) && isClient()) + { + u16 byplayerid; + if (!params.saferead_u16(byplayerid)) return; + + CPlayer@ byplayer = getPlayerByNetworkId(byplayerid); + if (byplayer is null) return; + + Rules_SetVote(this, Create_VoteScramble(byplayer)); + } +} \ No newline at end of file diff --git a/Rules/Zombie_Commands.as b/Rules/Zombie_Commands.as index 153beee..910c9e2 100644 --- a/Rules/Zombie_Commands.as +++ b/Rules/Zombie_Commands.as @@ -1,6 +1,7 @@ // Zombie Fortress chat commands #include "RespawnCommon.as"; +#include "Zombie_SoftBansCommon.as"; const string commandslist() { @@ -72,17 +73,13 @@ bool onServerProcessChat(CRules@ this, const string& in text_in, string& out tex warn("!softban:: missing perameters"); return false; } - if (!this.hasCommandID("server_softban")) + + SoftBan(tokens[1], tokens.length > 3 ? tokens[3] : "", parseInt(tokens[2])*60); + CPlayer@ bannedPlayer = getPlayerByUsername(tokens[1]); + if (bannedPlayer !is null) { - warn("!softban:: CMD 'server_soft_ban' missing"); - return false; + SetUndead(this, bannedPlayer); } - - CBitStream params; - params.write_string(tokens[1]); - params.write_string(tokens.length > 3 ? tokens[3] : ""); - params.write_s32(parseInt(tokens[2])*60); - this.SendCommand(this.getCommandID("server_softban"), params, false); } } else diff --git a/Rules/Zombie_Parachutes.as b/Rules/Zombie_Parachutes.as index 3507ee2..db008b2 100644 --- a/Rules/Zombie_Parachutes.as +++ b/Rules/Zombie_Parachutes.as @@ -2,12 +2,12 @@ void onInit(CRules@ this) { - this.addCommandID("give_parachute"); + this.addCommandID("client_give_parachute"); } void onCommand(CRules@ this, u8 cmd, CBitStream@ params) { - if (cmd == this.getCommandID("give_parachute")) + if (cmd == this.getCommandID("client_give_parachute") && isClient()) { CBlob@ blob = getBlobByNetworkID(params.read_netid()); if (blob !is null) diff --git a/Rules/Zombie_Respawning.as b/Rules/Zombie_Respawning.as index 504c6d6..eea3397 100644 --- a/Rules/Zombie_Respawning.as +++ b/Rules/Zombie_Respawning.as @@ -142,11 +142,13 @@ CBlob@ spawnPlayer(CRules@ this, CPlayer@ player) newBlob.server_SetPlayer(player); //give the blob a parachute if spawning at roof - if (this.hasCommandID("give_parachute") && spawnPos.y <= 16) + if (this.hasCommandID("client_give_parachute") && spawnPos.y <= 16) { + newBlob.AddScript("ParachuteEffect.as"); + CBitStream bs; bs.write_netid(newBlob.getNetworkID()); - this.SendCommand(this.getCommandID("give_parachute"), bs); + this.SendCommand(this.getCommandID("client_give_parachute"), bs); } return newBlob; diff --git a/Rules/Zombie_Settings.as b/Rules/Zombie_Settings.as index e861597..e4455d6 100644 --- a/Rules/Zombie_Settings.as +++ b/Rules/Zombie_Settings.as @@ -1,6 +1,5 @@ // Zombie Fortress settings -#include "GameplayEvents.as"; #include "Zombie_Scrolls.as"; void onInit(CRules@ this) @@ -17,11 +16,8 @@ void onInit(CRules@ this) AddIcons(); AddFonts(); - - SetupGameplayEvents(this); + SetupScrolls(this); - - this.addCommandID("server_softban"); //Zombie_SoftBans.as } void AddIcons() diff --git a/Rules/Zombie_SoftBans.as b/Rules/Zombie_SoftBans.as index 14e062d..257f71f 100644 --- a/Rules/Zombie_SoftBans.as +++ b/Rules/Zombie_SoftBans.as @@ -5,177 +5,7 @@ #define SERVER_ONLY; -#include "RespawnCommon.as"; - -const string FileName = "Zombie_SoftBans.cfg"; - -ConfigFile@ openBansConfig() -{ - ConfigFile cfg = ConfigFile(); - if (!cfg.loadFile("../Cache/"+FileName)) - { - warn("Creating soft bans config ../Cache/"+FileName); - cfg.saveFile(FileName); - } - - return cfg; -} - -const bool isSoftBanned(CPlayer@ player, string&out playerKey, int&out time) -{ - ConfigFile@ cfg = openBansConfig(); - - //check banned IP addresses - const string IP = player.server_getIP(); - if (cfg.exists(IP+"_time_end")) - { - playerKey = IP; - time = cfg.read_s32(IP+"_time_end"); - return true; - } - - //check banned usernames - const string Username = player.getUsername(); - if (cfg.exists(Username+"_time_end")) - { - playerKey = Username; - time = cfg.read_s32(Username+"_time_end"); - return true; - } - - return false; -} - -void SoftBan(string&in playerKey, string&in description, const int&in time) -{ - ConfigFile@ cfg = openBansConfig(); - - //ban by IP if available, set new player if online - CPlayer@ player = getPlayerByUsername(playerKey); - if (player !is null) - { - description = "[ "+playerKey+" ] " + description; - playerKey = player.server_getIP(); - } - - const bool isPermanentBan = time < 0; - - //add to ban file - cfg.add_s32(playerKey+"_time_end", isPermanentBan ? -1 : Time() + time); - cfg.add_string(playerKey+"_description", description); - cfg.saveFile(FileName); - - error("\nSoft banned [ "+playerKey+" ] for"+(isPermanentBan ? "ever" : " "+time/60+" minutes")+"; "+description+"\n"); -} - -const bool RemoveSoftBan(CRules@ this, CPlayer@ player, const string&in playerKey, const int&in time) -{ - if (Time() >= time && time > -1) - { - ConfigFile@ cfg = openBansConfig(); - - cfg.remove(playerKey+"_time_end"); - cfg.remove(playerKey+"_description"); - cfg.saveFile(FileName); - - if (player !is null) - { - //set player back to survivors - player.server_setTeamNum(0); - CBlob@ blob = player.getBlob(); - if (blob !is null) - { - if (blob.hasTag("undead") && blob.getBrain() !is null) - { - blob.server_SetPlayer(null); - blob.getBrain().server_SetActive(true); - } - else - { - blob.server_Die(); - } - } - - //remove player from queue - string[]@ usernames; - if (this.get("softban_spawn_queue", @usernames)) - { - const int usernameIndex = usernames.find(player.getUsername()); - if (usernameIndex > -1) usernames.erase(usernameIndex); - } - } - - return true; - } - - return false; -} - -void SetUndead(CRules@ this, CPlayer@ player) -{ - Respawn[]@ respawns; - if (!this.get("respawns", @respawns)) - { - warn("SetUndead:: failed to access respawns!"); - return; - } - - //remove any previous respawn - const string username = player.getUsername(); - for (u8 i = 0; i < respawns.length; i++) - { - Respawn@ r = respawns[i]; - if (r.username != username) continue; - respawns.erase(i); - break; - } - - player.server_setTeamNum(-2); - CBlob@ blob = player.getBlob(); - if (blob !is null) - { - if (blob.hasTag("undead") && blob.getBrain() !is null) - { - blob.server_SetPlayer(null); - blob.getBrain().server_SetActive(true); - } - else - { - blob.server_Die(); - } - } - - //see if we can spawn as a wraith right now - bool foundWraith = false; - CBlob@[] wraiths; - if (getBlobsByName("wraith", @wraiths)) - { - for (u8 i = 0; i < wraiths.length; i++) - { - CBlob@ wraith = wraiths[i]; - if (wraith.getPlayer() is null) - { - wraith.server_SetPlayer(player); - wraith.getBrain().server_SetActive(false); - foundWraith = true; - break; - } - } - } - - //can't find a wraith? add player to queue - if (!foundWraith) - { - string[]@ usernames; - if (this.get("softban_spawn_queue", @usernames)) - { - if (usernames.find(username) < 0) - { - usernames.push_back(username); - } - } - } -} +#include "Zombie_SoftBansCommon.as"; void onBlobCreated(CRules@ this, CBlob@ blob) { @@ -263,39 +93,6 @@ void onNewPlayerJoin(CRules@ this, CPlayer@ player) } } -void onCommand(CRules@ this, u8 cmd, CBitStream@ params) -{ - if (cmd == this.getCommandID("server_softban")) - { - string playerKey; - string description; - int time; - if (!params.saferead_string(playerKey)) - { - warn("server_soft_ban CMD:: failed to read playerKey!"); - return; - } - if (!params.saferead_string(description)) - { - warn("server_soft_ban CMD:: failed to read description!"); - return; - } - if (!params.saferead_s32(time)) - { - warn("server_soft_ban CMD:: failed to read time!"); - return; - } - - SoftBan(playerKey, description, time); - - CPlayer@ player = getPlayerByUsername(playerKey); - if (player !is null) - { - SetUndead(this, player); - } - } -} - bool onServerProcessChat(CRules@ this, const string& in text_in, string& out text_out, CPlayer@ player) { if (player is null) return true; diff --git a/Rules/Zombie_SoftBansCommon.as b/Rules/Zombie_SoftBansCommon.as new file mode 100644 index 0000000..e7bfb27 --- /dev/null +++ b/Rules/Zombie_SoftBansCommon.as @@ -0,0 +1,174 @@ +// Punish players by forcing them to become zombies for the duration of their ban +// A better alternative to hard-bans, since players can still 'play' while being punished. + +#include "RespawnCommon.as"; + +const string FileName = "Zombie_SoftBans.cfg"; + +ConfigFile@ openBansConfig() +{ + ConfigFile cfg = ConfigFile(); + if (!cfg.loadFile("../Cache/"+FileName)) + { + warn("Creating soft bans config ../Cache/"+FileName); + cfg.saveFile(FileName); + } + + return cfg; +} + +const bool isSoftBanned(CPlayer@ player, string&out playerKey, int&out time) +{ + ConfigFile@ cfg = openBansConfig(); + + //check banned IP addresses + const string IP = player.server_getIP(); + if (cfg.exists(IP+"_time_end")) + { + playerKey = IP; + time = cfg.read_s32(IP+"_time_end"); + return true; + } + + //check banned usernames + const string Username = player.getUsername(); + if (cfg.exists(Username+"_time_end")) + { + playerKey = Username; + time = cfg.read_s32(Username+"_time_end"); + return true; + } + + return false; +} + +void SoftBan(string&in playerKey, string&in description, const int&in time) +{ + ConfigFile@ cfg = openBansConfig(); + + //ban by IP if available, set new player if online + CPlayer@ player = getPlayerByUsername(playerKey); + if (player !is null) + { + description = "[ "+playerKey+" ] " + description; + playerKey = player.server_getIP(); + } + + const bool isPermanentBan = time < 0; + + //add to ban file + cfg.add_s32(playerKey+"_time_end", isPermanentBan ? -1 : Time() + time); + cfg.add_string(playerKey+"_description", description); + cfg.saveFile(FileName); + + error("\nSoft banned [ "+playerKey+" ] for"+(isPermanentBan ? "ever" : " "+time/60+" minutes")+"; "+description+"\n"); +} + +const bool RemoveSoftBan(CRules@ this, CPlayer@ player, const string&in playerKey, const int&in time) +{ + if (Time() >= time && time > -1) + { + ConfigFile@ cfg = openBansConfig(); + + cfg.remove(playerKey+"_time_end"); + cfg.remove(playerKey+"_description"); + cfg.saveFile(FileName); + + if (player !is null) + { + //set player back to survivors + player.server_setTeamNum(0); + CBlob@ blob = player.getBlob(); + if (blob !is null) + { + if (blob.hasTag("undead") && blob.getBrain() !is null) + { + blob.server_SetPlayer(null); + blob.getBrain().server_SetActive(true); + } + else + { + blob.server_Die(); + } + } + + //remove player from queue + string[]@ usernames; + if (this.get("softban_spawn_queue", @usernames)) + { + const int usernameIndex = usernames.find(player.getUsername()); + if (usernameIndex > -1) usernames.erase(usernameIndex); + } + } + + return true; + } + + return false; +} + +void SetUndead(CRules@ this, CPlayer@ player) +{ + Respawn[]@ respawns; + if (!this.get("respawns", @respawns)) + { + warn("SetUndead:: failed to access respawns!"); + return; + } + + //remove any previous respawn + const string username = player.getUsername(); + for (u8 i = 0; i < respawns.length; i++) + { + Respawn@ r = respawns[i]; + if (r.username != username) continue; + respawns.erase(i); + break; + } + + player.server_setTeamNum(-2); + CBlob@ blob = player.getBlob(); + if (blob !is null) + { + if (blob.hasTag("undead") && blob.getBrain() !is null) + { + blob.server_SetPlayer(null); + blob.getBrain().server_SetActive(true); + } + else + { + blob.server_Die(); + } + } + + //see if we can spawn as a wraith right now + bool foundWraith = false; + CBlob@[] wraiths; + if (getBlobsByName("wraith", @wraiths)) + { + for (u8 i = 0; i < wraiths.length; i++) + { + CBlob@ wraith = wraiths[i]; + if (wraith.getPlayer() is null) + { + wraith.server_SetPlayer(player); + wraith.getBrain().server_SetActive(false); + foundWraith = true; + break; + } + } + } + + //can't find a wraith? add player to queue + if (!foundWraith) + { + string[]@ usernames; + if (this.get("softban_spawn_queue", @usernames)) + { + if (usernames.find(username) < 0) + { + usernames.push_back(username); + } + } + } +} diff --git a/Rules/Zombie_SpawnItems.as b/Rules/Zombie_SpawnItems.as index f5347e0..c5d5854 100644 --- a/Rules/Zombie_SpawnItems.as +++ b/Rules/Zombie_SpawnItems.as @@ -138,19 +138,16 @@ void server_SpawnMats(CBlob@ blob, const string&in name, const int&in quantity) void onCommand(CRules@ this, u8 cmd, CBitStream@ params) { - if (cmd == this.getCommandID(give_items_cmd)) + if (cmd == this.getCommandID(give_items_cmd) && isServer()) { - if (isServer()) - { - CPlayer@ player = getPlayerByNetworkId(params.read_u16()); - CBlob@ blob = getBlobByNetworkID(params.read_netid()); - if (player is null || blob is null) return; - - const bool checkTimeAlive = params.read_bool(); - if (checkTimeAlive && blob.getTickSinceCreated() > 10) return; - - server_GiveMats(this, player, blob); - } + CPlayer@ player = getPlayerByNetworkId(params.read_u16()); + CBlob@ blob = getBlobByNetworkID(params.read_netid()); + if (player is null || blob is null) return; + + const bool checkTimeAlive = params.read_bool(); + if (checkTimeAlive && blob.getTickSinceCreated() > 10) return; + + server_GiveMats(this, player, blob); } }