/* Clonk Author: Randrian The protoganist of the game. Witty and nimble if skillfully controlled ;-) */ // selectable by HUD and some performance optimizations for the HUD updates #include Library_HUDAdapter // standard controls #include Library_ClonkControl // manager for aiming #include Library_AimManager #include Clonk_Animations // un-comment them as soon as the new controls work with context menus etc.^ // Context menu //#include Library_ContextMenu // Auto production //#include Library_AutoProduction // ladder climbing #include Library_CanClimbLadder local pInventory; /* Initialization */ protected func Construction() { _inherited(...); // Modifiable ActMap if (ActMap == this.Prototype.ActMap) ActMap = { Prototype = this.Prototype.ActMap }; SetAction("Walk"); SetDir(Random(2)); // Broadcast for rules GameCallEx("OnClonkCreation", this); AddEffect("IntTurn", this, 1, 1, this); AddEffect("IntEyes", this, 1, 35+Random(4), this); AttachBackpack(); iHandMesh = [0,0]; SetSkin(0); } /* When adding to the crew of a player */ protected func Recruitment(int iPlr) { //The clonk's appearance //Player settings can be overwritten for individual Clonks. In your clonk file: "ExtraData=1;Skin=iX" (X = chosen skin) var skin = GetCrewExtraData("Skin"); if (skin == nil) skin = GetPlrClonkSkin(iPlr); if(skin != nil) SetSkin(skin); else SetSkin(Random(GetSkinCount())); // Broadcast for crew GameCallEx("OnClonkRecruitment", this, iPlr); return _inherited(iPlr,...); } protected func DeRecruitment(int iPlr) { // Broadcast for crew GameCallEx("OnClonkDeRecruitment", this, iPlr); return _inherited(iPlr,...); } protected func ControlCommand(szCommand, pTarget, iTx, iTy, pTarget2, Data) { // RejectConstruction Callback for building via Drag'n'Drop form a building menu // TODO: Check if we still need this with the new system if(szCommand == "Construct") { if(Data->~RejectConstruction(iTx - GetX(), iTy - GetY(), this) ) { return 1; } } // No overloaded command return 0; } /* Transformation */ public func Redefine(idTo) { // save data of activity var phs=GetPhase(),act=GetAction(); // Transform ChangeDef(idTo); // restore action var chg=SetAction(act); if (!chg) SetAction("Walk"); if (chg) SetPhase(phs); // Done return 1; } /* Events */ protected func CatchBlow() { if (GetAction() == "Dead") return; if (!Random(5)) Hurt(); } protected func Hurt() { if(gender == 0) Sound("Hurt?"); else Sound("FHurt?"); } protected func Grab(object pTarget, bool fGrab) { Sound("Grab"); } protected func Get() { Sound("Grab"); } protected func Put() { Sound("Grab"); } protected func Death(int killed_by) { // this must be done first, before any goals do funny stuff with the clonk _inherited(killed_by,...); // Info-broadcasts for dying clonks. GameCallEx("OnClonkDeath", this, killed_by); // The broadcast could have revived the clonk. if (GetAlive()) return; // Some effects on dying. if(gender == 0) Sound("Die"); else Sound("FDie"); CloseEyes(1); DeathAnnounce(); return; } protected func Destruction() { _inherited(...); // If the clonk wasn't dead yet, he will be now. if (GetAlive()) GameCallEx("OnClonkDeath", this, GetKiller()); // If this is the last crewmember, do broadcast. if (GetCrew(GetOwner()) == this) if (GetCrewCount(GetOwner()) == 1) // Only if the player is still alive and not yet elimnated. if (GetPlayerName(GetOwner())) GameCallEx("RelaunchPlayer", GetOwner(), GetKiller()); return; } protected func DeepBreath() { Sound("Breath"); } protected func CheckStuck() { // Prevents getting stuck on middle vertex if(!GetXDir()) if(Abs(GetYDir()) < 5) if(GBackSolid(0, 3)) SetPosition(GetX(), GetY() + 1); } public func Eat(object food) { if(GetProcedure() == "WALK") { DoEnergy(food->NutritionalValue()); food->RemoveObject(); Sound("Munch?"); SetAction("Eat"); } } func DigOutObject(object obj) { // Collect fragile objects when dug out if (obj->GetDefFragile()) return Collect(obj,nil,nil,true); return false; } // Building material bridges (like loam bridge) func Bridge() { var proc = GetProcedure(); // Clonk must stand on ground. Allow during SCALE; but Clonk won't keep animation if he's not actually near the ground if (proc != "WALK" && proc != "SCALE") return false; if (proc == "WALK") SetAction("BridgeStand"); else SetAction("BridgeScale"); SetComDir(COMD_Stop); SetXDir(0); SetYDir(0); return true; } /* Status */ // TODO: Make this more sophisticated, readd turn animation and other // adaptions public func IsClonk() { return true; } public func IsJumping(){return WildcardMatch(GetAction(), "*Jump*");} public func IsWalking(){return GetProcedure() == "WALK";} public func IsBridging(){return WildcardMatch(GetAction(), "Bridge*");} /* Carry items on the clonk */ local iHandMesh; local fHandAction; local fBothHanded; func OnSelectionChanged(int oldslot, int newslot, bool secondaryslot) { AttachHandItem(secondaryslot); return _inherited(oldslot, newslot, secondaryslot); } func OnSlotEmpty(int slot) { AttachHandItem(slot); return _inherited(slot); } func OnSlotFull(int slot) { AttachHandItem(slot); return _inherited(slot); } public func DetachObject(object obj) { if(GetHandItem(0) == obj) DetachHandItem(0); if(GetHandItem(1) == obj) DetachHandItem(1); } func DetachHandItem(bool secondary) { if(iHandMesh[secondary]) DetachMesh(iHandMesh[secondary]); iHandMesh[secondary] = 0; } func AttachHandItem(bool secondary) { DetachHandItem(secondary); UpdateAttach(); } func UpdateAttach() { StopAnimation(GetRootAnimation(6)); DoUpdateAttach(0); DoUpdateAttach(1); } func DoUpdateAttach(bool sec) { var obj = GetHandItem(sec); var other_obj = GetHandItem(!sec); if(!obj) return; var iAttachMode = obj->~GetCarryMode(this); if(iAttachMode == CARRY_None) return; if(iHandMesh[sec]) { DetachMesh(iHandMesh[sec]); iHandMesh[sec] = 0; } var bone = "main"; var bone2; if(obj->~GetCarryBone()) bone = obj->~GetCarryBone(this); if(obj->~GetCarryBone2()) bone2 = obj->~GetCarryBone2(this); else bone2 = bone; var nohand = 0; if(!HasHandAction(sec, 1)) nohand = 1; var trans = obj->~GetCarryTransform(this, sec, nohand); var pos_hand = "pos_hand2"; if(sec) pos_hand = "pos_hand1"; var pos_back = "pos_back1"; if(sec) pos_back = "pos_back2"; var closehand = "Close2Hand"; if(sec) closehand = "Close1Hand"; if(!sec) fBothHanded = 0; var special = obj->~GetCarrySpecial(this); var special_other; if(other_obj) special_other = other_obj->~GetCarrySpecial(this); if(special) { iHandMesh[sec] = AttachMesh(obj, special, bone, trans); iAttachMode = 0; } if(iAttachMode == CARRY_Hand) { if(HasHandAction(sec, 1)) { iHandMesh[sec] = AttachMesh(obj, pos_hand, bone, trans); PlayAnimation(closehand, 6, Anim_Const(GetAnimationLength(closehand)), Anim_Const(1000)); } } else if(iAttachMode == CARRY_HandBack) { if(HasHandAction(sec, 1)) { iHandMesh[sec] = AttachMesh(obj, pos_hand, bone, trans); PlayAnimation(closehand, 6, Anim_Const(GetAnimationLength(closehand)), Anim_Const(1000)); } else iHandMesh[sec] = AttachMesh(obj, pos_back, bone2, trans); } else if(iAttachMode == CARRY_HandAlways) { iHandMesh[sec] = AttachMesh(obj, pos_hand, bone, trans); PlayAnimation(closehand, 6, Anim_Const(GetAnimationLength(closehand)), Anim_Const(1000)); } else if(iAttachMode == CARRY_Back) { iHandMesh[sec] = AttachMesh(obj, pos_back, bone2, trans); } else if(iAttachMode == CARRY_BothHands) { if(sec) return; if(HasHandAction(sec, 1) && !sec && !special_other) { iHandMesh[sec] = AttachMesh(obj, "pos_tool1", bone, trans); PlayAnimation("CarryArms", 6, Anim_Const(obj->~GetCarryPhase(this)), Anim_Const(1000)); fBothHanded = 1; } } else if(iAttachMode == CARRY_Spear) { if(HasHandAction(sec, 1) && !sec) { PlayAnimation("CarrySpear", 6, Anim_Const(0), Anim_Const(1000)); } else iHandMesh[sec] = AttachMesh(obj, pos_back, bone2, trans); } else if(iAttachMode == CARRY_Musket) { if(HasHandAction(sec, 1) && !sec) { iHandMesh[sec] = AttachMesh(obj, "pos_hand2", bone, trans); PlayAnimation("CarryMusket", 6, Anim_Const(0), Anim_Const(1000)); fBothHanded = 1; } else iHandMesh[sec] = AttachMesh(obj, pos_back, bone2, trans); } else if(iAttachMode == CARRY_Grappler) { if(HasHandAction(sec, 1) && !sec) { iHandMesh[sec] = AttachMesh(obj, "pos_hand2", bone, trans); PlayAnimation("CarryCrossbow", 6, Anim_Const(0), Anim_Const(1000)); fBothHanded = 1; } else iHandMesh[sec] = AttachMesh(obj, pos_back, bone2, trans); } }//AttachMesh(DynamiteBox, "pos_tool1", "main", Trans_Translate(0,0,0)); public func GetHandMesh(object obj) { if(GetHandItem(0) == obj) return iHandMesh[0]; if(GetHandItem(1) == obj) return iHandMesh[1]; } static const CARRY_None = 0; static const CARRY_Hand = 1; static const CARRY_HandBack = 2; static const CARRY_HandAlways = 3; static const CARRY_Back = 4; static const CARRY_BothHands = 5; static const CARRY_Spear = 6; static const CARRY_Musket = 7; static const CARRY_Grappler = 8; func HasHandAction(sec, just_wear) { if(sec && fBothHanded) return false; if(just_wear) { if( HasActionProcedure() && !fHandAction ) // For wear purpose fHandAction==-1 also blocks return true; } else { if( HasActionProcedure() && (!fHandAction || fHandAction == -1) ) return true; } return false; } func HasActionProcedure() { var action = GetAction(); if (action == "Walk" || action == "Jump" || action == "WallJump" || action == "Kneel" || action == "Ride" || action == "BridgeStand") return true; return false; } public func ReadyToAction(fNoArmCheck) { if(!fNoArmCheck) return HasActionProcedure(); return HasHandAction(0); } public func SetHandAction(bool fNewValue) { if(fNewValue > 0) fHandAction = 1; // 1 means can't use items and doesn't draw items in hand else if(fNewValue < 0) fHandAction = -1; // just don't draw items in hand can still use them else fHandAction = 0; UpdateAttach(); } public func GetHandAction() { if(fHandAction == 1) return true; return false; } /* Mesh transformations */ local mesh_transformation_list; func SetMeshTransformation(array transformation, int layer) { if(!mesh_transformation_list) mesh_transformation_list = []; if(GetLength(mesh_transformation_list) < layer) SetLength(mesh_transformation_list, layer+1); mesh_transformation_list[layer] = transformation; var all_transformations = nil; for(var trans in mesh_transformation_list) { if(!trans) continue; if(all_transformations) all_transformations = Trans_Mul(trans, all_transformations); else all_transformations = trans; } SetProperty("MeshTransformation", all_transformations); } /* Backpack */ local backpack; func AttachBackpack() { //Places a backpack onto the clonk backpack = AttachMesh(BackpackGraphic, "skeleton_body", "main", Trans_Mul(Trans_Rotate(180,0,1,0), Trans_Scale(700,700,400), Trans_Translate(0,4000,1000))); } func RemoveBackpack() { if(backpack) { DetachMesh(backpack); backpack = nil; } else return false; } // calback from engine func OnMaterialChanged(int new, int old) { if(!GetAlive()) return; var newdens = GetMaterialVal("Density","Material",new); var olddens = GetMaterialVal("Density","Material",old); var newliquid = (newdens >= C4M_Liquid) && (newdens < C4M_Solid); var oldliquid = (olddens >= C4M_Liquid) && (olddens < C4M_Solid); // into water if(newliquid && !oldliquid) AddEffect("Bubble", this, 1, 8, this); // out of water else if(!newliquid && oldliquid) RemoveEffect("Bubble", this); } func FxBubbleTimer(pTarget, effect, iTime) { if(GBackLiquid(0,-5)) { var mouth_off = GetCon()/11; var iRot = GetSwimRotation(); var mouth_off_x = Sin(iRot, mouth_off), mouth_off_y = Cos(iRot, mouth_off); // Search for bubbles to breath from var bubble = FindObject(Find_Func("CanBeBreathed", this), Find_AtRect(mouth_off_x-mouth_off/2, mouth_off_y, mouth_off, mouth_off/3)); if (bubble) { bubble->~OnClonkBreath(this); } else if (!Random(6)) { // Make your own bubbles Bubble(1, mouth_off_x, mouth_off_y); } } } func QueryCatchBlow(object obj) { var r=0; var e=0; var i=0; // Blocked by object effects? while(e=GetEffect("*", obj, i++)) if(EffectCall(obj, e, "QueryHitClonk", this)) return true; // Blocked by Clonk effects? i=0; while(e=GetEffect("*Control*", this, i++)) { if(EffectCall(this, e, "QueryCatchBlow", obj)) { r=true; break; } } if(r) return r; // No blocking return _inherited(obj, ...); } local gender; func SetSkin(int skin) { //Adventurer if(skin == 0) { SetGraphics(); gender = 0; } //Steampunk if(skin == 1) { SetGraphics("Steampunk"); gender = 1; } //Alchemist if(skin == 2) { SetGraphics("Alchemist"); gender = 0; } //Farmer if(skin == 3) { SetGraphics("Farmer"); gender = 1; } RemoveBackpack(); //add a backpack AttachBackpack(); SetAction("Jump"); //refreshes animation return skin; } func GetSkinCount() { return 4; } /* Scenario saving */ func SaveScenarioObject(props) { if (!inherited(props, ...)) return false; // Direction is randomized at creation and there's no good way to find // out if the user wanted that specific direction. So just always save // it, because that's what scenario designer usually wants. if (!props->HasProp("Dir")) props->AddCall("Dir", this, "SetDir", GetConstantNameByValueSafe(GetDir(),"DIR_")); return true; } /* AI editor helper */ func EditCursorSelection() { var ai = S2AI->GetAI(this); if (ai) Call(S2AI.EditCursorSelection, ai); return _inherited(...); } func EditCursorDeselection() { var ai = S2AI->GetAI(this); if (ai) Call(S2AI.EditCursorDeselection, ai); return _inherited(...); } /* Act Map */ local ActMap = { Walk = { Prototype = Action, Name = "Walk", Procedure = DFA_WALK, Accel = 16, Decel = 22, Speed = 200, Directions = 2, FlipDir = 0, Length = 1, Delay = 0, X = 0, Y = 0, Wdt = 8, Hgt = 20, StartCall = "StartWalk", AbortCall = "StopWalk", // InLiquidAction = "Swim", }, Stand = { Prototype = Action, Name = "Stand", Procedure = DFA_THROW, Directions = 2, FlipDir = 0, Length = 1, Delay = 0, X = 0, Y = 0, Wdt = 8, Hgt = 20, StartCall = "StartStand", InLiquidAction = "Swim", }, Kneel = { Prototype = Action, Name = "Kneel", Procedure = DFA_KNEEL, Directions = 2, FlipDir = 0, Length = 1, Delay = 0, X = 0, Y = 0, Wdt = 8, Hgt = 20, // StartCall = "StartKneel", InLiquidAction = "Swim", }, Roll = { Prototype = Action, Name = "Roll", Procedure = DFA_NONE, Directions = 2, FlipDir = 0, Length = 1, Delay = 0, X = 0, Y = 0, Wdt = 8, Hgt = 20, StartCall = "StartRoll", NextAction = "Walk", InLiquidAction = "Swim", }, Scale = { Prototype = Action, Name = "Scale", Procedure = DFA_SCALE, Speed = 60, Accel = 20, Directions = 2, Length = 1, Delay = 0, X = 0, Y = 20, Wdt = 8, Hgt = 20, OffX = 0, OffY = 0, StartCall = "StartScale", AbortCall = "StopScale", }, Tumble = { Prototype = Action, Name = "Tumble", Procedure = DFA_FLIGHT, Speed = 200, Accel = 0, Directions = 2, Length = 1, Delay = 0, X = 0, Y = 40, Wdt = 8, Hgt = 20, NextAction = "Tumble", ObjectDisabled = 1, InLiquidAction = "Swim", StartCall = "StartTumble", AbortCall = "StopTumble", EndCall = "CheckStuck", }, Dig = { Prototype = Action, Name = "Dig", Procedure = DFA_DIG, Speed = 50, Directions = 2, Length = 16, Delay = 0,//15*3*0, X = 0, Y = 60, Wdt = 8, Hgt = 20, NextAction = "Dig", StartCall = "StartDigging", AbortCall = "StopDigging", DigFree = 11, // InLiquidAction = "Swim", Attach = CNAT_Left | CNAT_Right | CNAT_Bottom, }, BridgeStand = { Prototype = Action, Name = "BridgeStand", Procedure = DFA_THROW, Directions = 2, Length = 16, Delay = 1, X = 0, Y = 60, Wdt = 8, Hgt = 20, NextAction = "BridgeStand", StartCall = "StartStand", InLiquidAction = "Swim", }, BridgeScale = { Prototype = Action, Name = "BridgeScale", Procedure = DFA_THROW, Directions = 2, Length = 16, Delay = 1, X = 0, Y = 60, Wdt = 8, Hgt = 20, NextAction = "BridgeScale", InLiquidAction = "Swim", }, Swim = { Prototype = Action, Name = "Swim", Procedure = DFA_SWIM, Speed = 96, Accel = 7, Decel = 9, Directions = 2, Length = 1, Delay = 0, X = 0, Y = 80, Wdt = 8, Hgt = 20, OffX = 0, OffY = 0, // SwimOffset = -5, StartCall = "StartSwim", AbortCall = "StopSwim", }, Hangle = { Prototype = Action, Name = "Hangle", Procedure = DFA_HANGLE, Speed = 48, Accel = 20, Directions = 2, Length = 1, Delay = 0, X = 0, Y = 100, Wdt = 8, Hgt = 20, OffX = 0, OffY = 3, StartCall = "StartHangle", AbortCall = "StopHangle", InLiquidAction = "Swim", }, Jump = { Prototype = Action, Name = "Jump", Procedure = DFA_FLIGHT, Speed = 200, Accel = 14, Directions = 2, Length = 1, Delay = 0, X = 0, Y = 120, Wdt = 8, Hgt = 20, InLiquidAction = "Swim", PhaseCall = "CheckStuck", StartCall = "StartJump", }, WallJump = { Prototype = Action, Name = "WallJump", Procedure = DFA_FLIGHT, Speed = 200, Accel = 14, Directions = 2, Length = 1, Delay = 0, X = 0, Y = 120, Wdt = 8, Hgt = 20, InLiquidAction = "Swim", PhaseCall = "CheckStuck", }, Dive = { Prototype = Action, Name = "Dive", Procedure = DFA_FLIGHT, Speed = 200, Accel = 16, Directions = 2, Length = 8, Delay = 4, X = 0, Y = 160, Wdt = 8, Hgt = 20, NextAction = "Hold", ObjectDisabled = 1, InLiquidAction = "Swim", PhaseCall = "CheckStuck", }, Dead = { Prototype = Action, Name = "Dead", Directions = 2, X = 0, Y = 240, Wdt = 8, Hgt = 20, Length = 1, Delay = 0, NextAction = "Hold", StartCall = "StartDead", NoOtherAction = 1, ObjectDisabled = 1, }, Ride = { Prototype = Action, Name = "Ride", Procedure = DFA_ATTACH, Directions = 2, Length = 1, Delay = 0, X = 128, Y = 120, Wdt = 8, Hgt = 20, StartCall = "StartRiding", AbortCall = "StopRiding", InLiquidAction = "Swim", }, Push = { Prototype = Action, Name = "Push", Procedure = DFA_PUSH, Speed = 200, Accel = 100, Directions = 2, Length = 8, Delay = 15, X = 128, Y = 140, Wdt = 8, Hgt = 20, NextAction = "Push", StartCall = "StartPushing", AbortCall = "StopPushing", InLiquidAction = "Swim", }, Build = { Prototype = Action, Name = "Build", Directions = 2, Length = 8, Delay = 15, X = 128, Y = 140, Wdt = 8, Hgt = 20, NextAction = "Build", InLiquidAction = "Swim", Attach = CNAT_Bottom, }, HangOnto = { Prototype = Action, Name = "HangOnto", Procedure = DFA_ATTACH, Directions = 2, Length = 1, Delay = 0, X = 128, Y = 120, Wdt = 8, Hgt = 20, StartCall = "StartHangOnto", AbortCall = "AbortHangOnto", InLiquidAction = "Swim", }, Eat = { Prototype = Action, Name = "Eat", Procedure = DFA_NONE, Directions = 2, Length = 1, Delay = 45, X = 0, Y = 0, Wdt = 8, Hgt = 20, StartCall = "StartEat", NextAction = "Walk", InLiquidAction = "Swim", Attach=CNAT_Bottom, }, }; local Name = "Clonk"; local MaxEnergy = 50000; local MaxBreath = 720; // Clonk can breathe for 20 seconds under water. local JumpSpeed = 400; local ThrowSpeed = 294; local NoBurnDecay = 1; local ContactIncinerate = 10; func Definition(def) { // Set perspective SetProperty("PictureTransformation", Trans_Mul(Trans_Translate(0,1000,5000), Trans_Rotate(70,0,1,0)), def); _inherited(def); }