MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | // MorgenGrauen MUDlib |
| 2 | // |
| 3 | // ranged_weapon.c -- Standardobjekt fuer Fernkampfwaffen |
| 4 | // |
| 5 | // $Id: ranged_weapon.c 8820 2014-05-14 19:31:46Z Zesstra $ |
| 6 | |
| 7 | // Gebraucht wird Munition mit einer MUN_* id. Dieses Objekt sollte ausserdem |
| 8 | // P_DAM_TYPE setzen sowie P_SHOOTING_WC und ggf. P_HIT_FUNC. In der Regel |
| 9 | // sollten diese Objekte units sein, da groessere Anzahlen davon gebraucht |
| 10 | // werden. |
| 11 | #pragma strong_types |
| 12 | #pragma save_types |
| 13 | #pragma no_clone |
| 14 | #pragma pedantic |
| 15 | #pragma range_check |
| 16 | |
| 17 | inherit "/std/living/skill_utils"; |
| 18 | inherit "/std/weapon"; |
| 19 | |
| 20 | #include <ranged_weapon.h> |
| 21 | |
| 22 | #include <properties.h> |
| 23 | #include <defines.h> |
| 24 | #include <combat.h> |
| 25 | #include <new_skills.h> |
| 26 | #include <unit.h> |
| 27 | |
| 28 | static int last_shoot; |
| 29 | |
| 30 | protected void create() |
| 31 | { |
| 32 | ::create(); |
| 33 | |
| 34 | SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON); |
| 35 | SetProp(P_STRETCH_TIME, 1); |
| 36 | SetProp(P_AMMUNITION, MUN_ARROW); |
| 37 | SetProp(P_SHOOTING_WC, 70); |
| 38 | SetProp(P_RANGE, 50); |
| 39 | SetProp(P_INFO, "Syntax: schiesse WAS auf WEN?\n"); |
| 40 | // fuer den Fall, das man den Bogen als Knueppel nutzt... |
| 41 | SetProp(P_WC, 30); |
| 42 | SetProp(P_QUALITY, 100); |
| 43 | |
| 44 | AddCmd( ({"schiess", "schiesse"}), "cmd_shoot"); |
| 45 | } |
| 46 | |
| 47 | static int shoot_dam(mapping shoot) |
| 48 | // ausgegliedert, damit man diese Funktion ggf. ueberschreiben kann. |
| 49 | { mixed res; |
| 50 | object hitfunc; |
| 51 | closure usk; |
| 52 | |
| 53 | // diesselbe Formel wie bei Nahkampfwaffen, nur das hier DEX anstatt STR |
| 54 | // benutzt wird, und die WC der Munition noch addiert wird |
| 55 | shoot[SI_SKILLDAMAGE] = random(1+( 2*(shoot[P_WC]+shoot[P_SHOOTING_WC]) |
| 56 | + 10*PL->QueryAttribute(A_DEX) )/3); |
| 57 | // HitFunc der Munition addieren |
| 58 | if ( objectp(hitfunc=(shoot[P_AMMUNITION]->QueryProp(P_HIT_FUNC))) ) |
| 59 | shoot[SI_SKILLDAMAGE] += (hitfunc->HitFunc(shoot[SI_ENEMY])); |
| 60 | |
| 61 | usk=symbol_function("UseSkill",PL); |
| 62 | |
| 63 | // Schuss-Skill der Gilde fuer diesen Munitionstyp anwenden |
| 64 | if ( mappingp(res=funcall(usk,SHOOT(shoot[P_WEAPON_TYPE]), |
| 65 | deep_copy(shoot))) ) |
| 66 | SkillResTransfer(res, &shoot); |
| 67 | |
| 68 | // Allgemeinen Schuss-Skill der Gilde anwenden |
| 69 | if ( mappingp(res=funcall(usk,SK_SHOOT,deep_copy(shoot))) ) |
| 70 | SkillResTransfer(res, &shoot); |
| 71 | |
| 72 | return shoot[SI_SKILLDAMAGE]; |
| 73 | } |
| 74 | |
| 75 | static string FindRangedTarget(string str, mapping shoot) |
| 76 | { string wen; |
| 77 | mixed area; |
| 78 | int psr; |
| 79 | |
| 80 | if ( sscanf(str, "%s auf %s", str, wen)!=2 ) |
| 81 | { |
| 82 | shoot[SI_ENEMY]=PL->SelectFarEnemy(PL->PresentEnemies(),1,4); |
| 83 | str = shoot[P_WEAPON_TYPE]; |
| 84 | } |
| 85 | else if ( objectp(shoot[SI_ENEMY]=present(wen, environment(PL))) ) |
| 86 | { |
| 87 | return str; |
| 88 | } |
| 89 | else if ( ( (area=(environment(PL)->QueryProp(P_TARGET_AREA))) |
| 90 | || (area=environment(environment(PL))) ) |
| 91 | && ( objectp(area) |
| 92 | || ( stringp(area) |
| 93 | && objectp(area=find_object(area)) ) ) |
| 94 | && (psr=environment(PL)->QueryProp(P_SHOOTING_AREA)) ) |
| 95 | { |
| 96 | shoot[SI_ENEMY]=present(wen, area); |
| 97 | if ( shoot[SI_ENEMY] && (psr>QueryProp(P_RANGE)) ) |
| 98 | { |
| 99 | write(break_string((shoot[SI_ENEMY]->Name(WER))+ |
| 100 | " ist leider ausserhalb der Reichweite "+name(WESSEN, 1)+".", |
| 101 | 78)); |
| 102 | return 0; |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | if ( !shoot[SI_ENEMY] ) |
| 107 | { |
| 108 | write("Es ist kein Feind vorhanden auf den Du schiessen koenntest.\n"); |
| 109 | return 0; |
| 110 | } |
| 111 | |
| 112 | return str; |
| 113 | } |
| 114 | |
| 115 | static int cmd_shoot(string str) |
| 116 | { int dam; |
| 117 | string no_at; |
| 118 | object quiver; |
| 119 | mapping shoot; |
| 120 | |
| 121 | if ( PL->QueryProp(P_GHOST) ) |
| 122 | { |
| 123 | write("Deine immateriellen Finger gleiten durch die Waffe hindurch.\n"); |
| 124 | return 1; |
| 125 | } |
| 126 | |
| 127 | if ( !QueryProp(P_NOGET) ) |
| 128 | { |
| 129 | // Katapulte oder aehnliches die von Magiern aufgestellt werden |
| 130 | // muessen natuerlich nicht gezueckt werden :). |
| 131 | if ( !QueryProp(P_WIELDED) ) |
| 132 | { |
| 133 | notify_fail(break_string("Du solltest "+name(WEN, 1)+ |
| 134 | " dazu schon zuecken.", 78)); |
| 135 | return 0; |
| 136 | } |
| 137 | |
| 138 | if ( (PL->InFight()) && ((PL->PresentPosition())<2) ) |
| 139 | { |
| 140 | write(break_string( |
| 141 | "Du solltest Dich erst etwas weiter zurueckziehen, um mit "+ |
| 142 | name(WEM, 1)+" schiessen zu koennen!", 78)); |
| 143 | return 1; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | if ( ( (QueryProp(P_EQUIP_TIME)+QueryProp(P_STRETCH_TIME)*2) > time() ) |
| 148 | || ( (last_shoot+QueryProp(P_STRETCH_TIME)) > absolute_hb_count() ) ) |
| 149 | { |
| 150 | write(break_string("Du kannst mit "+name(WEM, 1)+ |
| 151 | " noch nicht wieder schiessen.", 78)); |
| 152 | return 1; |
| 153 | } |
| 154 | |
| 155 | if ( (PL->QueryProp(P_DISABLE_ATTACK))>0 ) |
| 156 | { |
| 157 | write(break_string("Solange Du noch gelaehmt bist, kannst Du "+ |
| 158 | name(WEN, 1)+" nicht benutzen!", 78)); |
| 159 | return 1; |
| 160 | } |
| 161 | |
| 162 | if ( PL->QueryProp(P_ATTACK_BUSY) ) |
| 163 | { |
| 164 | write("Nicht so hektisch! So schnell bist Du nicht.\n"); |
| 165 | return 1; |
| 166 | } |
| 167 | |
| 168 | // P_ATTACK_BUSY natuerlich auch setzen... |
| 169 | PL->SetProp(P_ATTACK_BUSY,1); |
| 170 | |
| 171 | // Info-Mapping erzeugen |
| 172 | shoot = ([ P_WEAPON : ME, |
| 173 | P_WEAPON_TYPE : QueryProp(P_AMMUNITION), |
| 174 | P_STRETCH_TIME : QueryProp(P_STRETCH_TIME), |
| 175 | P_WC : QueryProp(P_SHOOTING_WC), |
| 176 | SI_SPELL : 0 |
| 177 | ]); |
| 178 | |
| 179 | if ( !stringp(str) || str=="" ) |
| 180 | str=shoot[P_WEAPON_TYPE]; |
| 181 | else if (str[0..3]=="mit ") |
| 182 | str=str[4..]; |
| 183 | else if (str[0..3]=="auf ") |
| 184 | str=shoot[P_WEAPON_TYPE]+" "+str; |
| 185 | |
| 186 | if ( !(str=FindRangedTarget(str,shoot)) ) |
| 187 | return 1; |
| 188 | |
| 189 | if ( shoot[SI_ENEMY]->QueryProp(P_GHOST) ) |
| 190 | { |
| 191 | write(break_string("Aber "+(shoot[SI_ENEMY]->name(WER, 1))+ |
| 192 | " ist doch ein Geist!", 78)); |
| 193 | return 1; |
| 194 | } |
| 195 | else if ( no_at=(shoot[SI_ENEMY]->QueryProp(P_NO_ATTACK)) ) |
| 196 | { |
| 197 | if ( stringp(no_at) ) |
| 198 | write(no_at); |
| 199 | else |
| 200 | write(break_string("Du kannst "+(shoot[SI_ENEMY]->name(WEN, 1))+ |
| 201 | " nicht angreifen.", 78)); |
| 202 | return 1; |
| 203 | } |
| 204 | else if ( shoot[SI_ENEMY]==PL ) |
| 205 | { |
| 206 | write("Du kannst doch nicht auf Dich selbst schiessen!\n"); |
| 207 | return 1; |
| 208 | } |
| 209 | |
| 210 | if ( !(shoot[P_AMMUNITION]=present(str, PL)) |
| 211 | && ( !objectp(quiver=PL->QueryArmourByType(AT_QUIVER)) |
| 212 | || !(shoot[P_AMMUNITION]=present(str, quiver)) ) ) |
| 213 | { |
| 214 | if ( str==shoot[P_WEAPON_TYPE] ) |
| 215 | write(break_string("Du hast keine passende Munition bei Dir, um "+ |
| 216 | "mit "+name(WEM, 1)+" schiessen zu koennen.", |
| 217 | 78)); |
| 218 | else |
| 219 | write(break_string("Du hast kein '"+str+"' bei Dir.", 78)); |
| 220 | return 1; |
| 221 | } |
| 222 | |
| 223 | if ( !shoot[P_AMMUNITION]->id(shoot[P_WEAPON_TYPE]) ) |
| 224 | { |
| 225 | write(break_string((shoot[P_AMMUNITION]->Name(WER))+" kannst Du mit "+ |
| 226 | name(WEM, 1)+" nicht verschiessen.", 78)); |
| 227 | return 1; |
| 228 | } |
| 229 | |
| 230 | shoot[P_AMMUNITION]->SetProp(U_REQ, 1); |
| 231 | |
| 232 | shoot[SI_SKILLDAMAGE_MSG] = shoot[SI_SKILLDAMAGE_MSG2] |
| 233 | = shoot[P_AMMUNITION]->name(WEN); |
| 234 | shoot[SI_SKILLDAMAGE_TYPE] = ( shoot[P_AMMUNITION]->QueryProp(P_DAM_TYPE) |
| 235 | || ({DT_PIERCE}) ); |
| 236 | |
| 237 | shoot[P_SHOOTING_WC] = shoot[P_AMMUNITION]->QueryProp(P_SHOOTING_WC); |
| 238 | |
| 239 | dam=shoot_dam(shoot); |
| 240 | |
| 241 | // Textausgabe |
| 242 | if (shoot[SI_ENEMY]) |
| 243 | { |
| 244 | tell_object(PL,break_string( |
| 245 | "Du schiesst "+shoot[SI_SKILLDAMAGE_MSG]+" auf "+ |
| 246 | shoot[SI_ENEMY]->name(WEN, 1)+".", 78) ); |
| 247 | tell_room(environment(PL), break_string( |
| 248 | PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+ |
| 249 | shoot[SI_ENEMY]->name(WEN, 1)+".", 78), |
| 250 | ({ shoot[SI_ENEMY], PL }) ); |
| 251 | if ( environment(PL)!=environment(shoot[SI_ENEMY]) ) |
| 252 | tell_room(environment(shoot[SI_ENEMY]),break_string( |
| 253 | PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+ |
| 254 | shoot[SI_ENEMY]->name(WEN, 1)+".", 78), |
| 255 | ({ shoot[SI_ENEMY] }) ); |
| 256 | tell_object(shoot[SI_ENEMY], break_string( |
| 257 | PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+ |
| 258 | " auf Dich ab.",78) ); |
| 259 | |
| 260 | shoot[SI_ENEMY]->Defend(dam,shoot[SI_SKILLDAMAGE_TYPE],shoot[SI_SPELL],PL); |
| 261 | } |
| 262 | // Munition verbrauchen |
| 263 | if ( shoot[P_AMMUNITION]->IsUnit() ) |
| 264 | shoot[P_AMMUNITION]->AddAmount(-1); |
| 265 | else |
| 266 | shoot[P_AMMUNITION]->remove(); |
| 267 | |
| 268 | // es wird an dieser stelle absolute_hb_count anstelle von time() benutzt |
| 269 | // um dem Spieler wirklich die vollen 2 sek Zeit zu geben um zu reagieren |
| 270 | // ohne das er dadurch einen Schaden hat (wie er es z.b. bei time() haette) |
| 271 | last_shoot=absolute_hb_count(); |
| 272 | |
| 273 | return 1; |
| 274 | } |
| 275 | |